From 13ca4544f22644ccf06ddc780c7d6b66ff956755 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 3 Oct 2024 02:13:14 -0700 Subject: [PATCH] enhance INVALID_ARG_TYPE and cleanup some node:buffer error handling (#14200) --- src/bun.js/bindings/ErrorCode.cpp | 30 ++--- src/bun.js/bindings/ErrorCode.h | 4 +- src/bun.js/bindings/JSBuffer.cpp | 181 +++++++++++--------------- src/bun.js/bindings/NodeValidator.cpp | 2 +- 4 files changed, 90 insertions(+), 127 deletions(-) diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index 9e27f4b40f..43c5d33004 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -307,45 +307,43 @@ WTF::String ERR_OUT_OF_RANGE(JSC::ThrowScope& scope, JSC::JSGlobalObject* global namespace ERR { -JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value, bool instance) +JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value) { auto arg_name = val_arg_name.span8(); ASSERT(WTF::charactersAreAllASCII(arg_name)); + auto arg_kind = String(arg_name).startsWith("options."_s) ? "property"_s : "argument"_s; + auto expected_type = val_expected_type.span8(); ASSERT(WTF::charactersAreAllASCII(expected_type)); + auto ty_first_char = expected_type[0]; + auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s; + auto actual_value = JSValueToStringSafe(globalObject, val_actual_value); RETURN_IF_EXCEPTION(throwScope, {}); - if (instance) { - auto message = makeString("The \""_s, arg_name, "\" argument must be an instance of "_s, expected_type, ". Received "_s, actual_value); - throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); - return {}; - } - - auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); + auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value); throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); return {}; } -JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value, bool instance) +JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value) { auto arg_name = val_arg_name.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); + auto arg_kind = String(arg_name).startsWith("options."_s) ? "property"_s : "argument"_s; + auto expected_type = val_expected_type.span8(); ASSERT(WTF::charactersAreAllASCII(expected_type)); + auto ty_first_char = expected_type[0]; + auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s; + auto actual_value = JSValueToStringSafe(globalObject, val_actual_value); RETURN_IF_EXCEPTION(throwScope, {}); - if (instance) { - auto message = makeString("The \""_s, arg_name, "\" argument must be an instance of "_s, expected_type, ". Received "_s, actual_value); - throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); - return {}; - } - - auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); + auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value); throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); return {}; } diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h index 6330aa4442..9477291105 100644 --- a/src/bun.js/bindings/ErrorCode.h +++ b/src/bun.js/bindings/ErrorCode.h @@ -75,8 +75,8 @@ enum Bound { namespace ERR { -JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value, bool instance = false); -JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value, bool instance = false); +JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value); +JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue val_arg_name, ASCIILiteral val_expected_type, JSC::JSValue val_actual_value); JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral arg_name, size_t lower, size_t upper, JSC::JSValue actual); JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name, size_t lower, size_t upper, JSC::JSValue actual); JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, size_t bound_num, Bound bound, JSC::JSValue actual); diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index c88626f780..7a139a92b1 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -52,6 +52,7 @@ #include #include "JSBufferEncodingType.h" +#include "ErrorCode.h" #include "wtf/Assertions.h" #include "wtf/Forward.h" #include @@ -245,29 +246,29 @@ static int normalizeCompareVal(int val, size_t a_length, size_t b_length) return val; } -static inline uint32_t parseIndex(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, JSValue arg) +const unsigned U32_MAX = std::numeric_limits().max(); + +static inline uint32_t parseIndex(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral name, JSValue arg, size_t upperBound) { - if (auto num = arg.tryGetAsUint32Index()) - return num.value(); - - if (arg.isNumber()) - throwNodeRangeError(lexicalGlobalObject, scope, "Invalid array length"_s); - else - throwTypeError(lexicalGlobalObject, scope, "Expected number"_s); - - return 0; + if (!arg.isNumber()) return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, name, "number"_s, arg); + auto num = arg.asNumber(); + if (num < 0 || std::isinf(num)) return Bun::ERR::OUT_OF_RANGE(scope, lexicalGlobalObject, name, 0, upperBound, arg); + double intpart; + if (std::modf(num, &intpart) != 0) return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, name, "integer"_s, arg); + if (intpart >= 0 && intpart <= U32_MAX) return intpart; + return Bun::ERR::OUT_OF_RANGE(scope, lexicalGlobalObject, name, 0, upperBound, arg); } static inline WebCore::BufferEncodingType parseEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, JSValue arg) { if (UNLIKELY(!arg.isString())) { - throwTypeError(lexicalGlobalObject, scope, "Expected string"_s); + Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "encoding"_s, "string"_s, arg); return WebCore::BufferEncodingType::utf8; } std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg); if (UNLIKELY(!encoded)) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); + Bun::ERR::UNKNOWN_ENCODING(scope, lexicalGlobalObject, arg); return WebCore::BufferEncodingType::utf8; } @@ -434,20 +435,15 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocUnsafeBody(JS auto throwScope = DECLARE_THROW_SCOPE(vm); - if (callFrame->argumentCount() < 1) - return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - - JSValue lengthValue = callFrame->uncheckedArgument(0); + JSValue lengthValue = callFrame->argument(0); if (UNLIKELY(!lengthValue.isNumber())) { - throwTypeError(lexicalGlobalObject, throwScope, "size argument must be a number"_s); - return {}; + return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "size"_s, "number"_s, lengthValue); } double lengthDouble = lengthValue.toIntegerWithTruncation(lexicalGlobalObject); - if (UNLIKELY(lengthDouble < 0 || lengthDouble > UINT_MAX)) { - throwNodeRangeError(lexicalGlobalObject, throwScope, "Buffer size must be 0...4294967295"_s); - return {}; + if (UNLIKELY(lengthDouble < 0 || lengthDouble > MAX_ARRAY_BUFFER_SIZE || lengthDouble != lengthDouble)) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "size"_s, 0, MAX_ARRAY_BUFFER_SIZE, lengthValue); } size_t length = static_cast(lengthDouble); @@ -546,8 +542,7 @@ static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGl if (arg1 && arg1.isString()) { std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg1); if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return {}; + return Bun::ERR::UNKNOWN_ENCODING(scope, lexicalGlobalObject, arg1); } encoding = encoded.value(); @@ -567,15 +562,14 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG auto scope = DECLARE_THROW_SCOPE(vm); JSValue lengthValue = callFrame->uncheckedArgument(0); if (UNLIKELY(!lengthValue.isNumber())) { - throwTypeError(lexicalGlobalObject, scope, "size argument must be a number"_s); + return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "size"_s, "number"_s, lengthValue); return {}; } double lengthDouble = lengthValue.toIntegerWithTruncation(lexicalGlobalObject); - if (UNLIKELY(lengthDouble < 0 || lengthDouble > UINT_MAX)) { - throwNodeRangeError(lexicalGlobalObject, scope, "Buffer size must be 0...4294967295"_s); - return {}; + if (UNLIKELY(lengthDouble < 0 || lengthDouble > MAX_ARRAY_BUFFER_SIZE || lengthDouble != lengthDouble)) { + return Bun::ERR::OUT_OF_RANGE(scope, lexicalGlobalObject, "size"_s, 0, MAX_ARRAY_BUFFER_SIZE, lengthValue); } size_t length = static_cast(lengthDouble); @@ -698,14 +692,10 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(callFrame->argumentCount() == 0)) { - throwTypeError(lexicalGlobalObject, scope, "Not enough arguments"_s); - return {}; - } - EnsureStillAliveScope arg0 = callFrame->argument(0); EnsureStillAliveScope arg1 = callFrame->argument(1); + if (callFrame->argumentCount() > 1) { if (arg1.value().isString()) { @@ -729,35 +719,28 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC return JSValue::encode(jsNumber(arrayBuffer->impl()->byteLength())); } - throwTypeError(lexicalGlobalObject, scope, "Invalid input, must be a string, Buffer, or ArrayBuffer"_s); - return {}; + return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "string"_s, "string or an instance of Buffer or ArrayBuffer"_s, callFrame->argument(0)); } static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { auto& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); - if (callFrame->argumentCount() < 2) { - throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - return {}; - } - auto castedThisValue = callFrame->uncheckedArgument(0); + auto castedThisValue = callFrame->argument(0); JSC::JSArrayBufferView* castedThis = JSC::jsDynamicCast(castedThisValue); if (UNLIKELY(!castedThis)) { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (first argument)"_s); - return {}; + return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "buf1"_s, "Buffer or Uint8Array"_s, castedThisValue); } if (UNLIKELY(castedThis->isDetached())) { throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array (first argument) is detached"_s); return {}; } - auto buffer = callFrame->uncheckedArgument(1); + auto buffer = callFrame->argument(1); JSC::JSArrayBufferView* view = JSC::jsDynamicCast(buffer); if (UNLIKELY(!view)) { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (2nd argument)"_s); - return {}; + return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "buf2"_s, "Buffer or Uint8Array"_s, buffer); } if (UNLIKELY(view->isDetached())) { throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array (second argument) is detached"_s); @@ -772,29 +755,6 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J size_t sourceEndInit = castedThis->byteLength(); size_t sourceEnd = sourceEndInit; - switch (callFrame->argumentCount()) { - default: - sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(5)); - RETURN_IF_EXCEPTION(throwScope, {}); - FALLTHROUGH; - case 5: - sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(4)); - RETURN_IF_EXCEPTION(throwScope, {}); - FALLTHROUGH; - case 4: - targetEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); - RETURN_IF_EXCEPTION(throwScope, {}); - FALLTHROUGH; - case 3: - targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); - RETURN_IF_EXCEPTION(throwScope, {}); - break; - case 2: - case 1: - case 0: - break; - } - targetStart = std::min(targetStart, std::min(targetEnd, targetEndInit)); sourceStart = std::min(sourceStart, std::min(sourceEnd, sourceEndInit)); @@ -962,16 +922,12 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_compareBody(JSC::JSG { auto& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); - if (callFrame->argumentCount() < 1) { - throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - return {}; - } - JSC::JSUint8Array* view = JSC::jsDynamicCast(callFrame->uncheckedArgument(0)); + auto arg0 = callFrame->argument(0); + JSC::JSUint8Array* view = JSC::jsDynamicCast(arg0); if (UNLIKELY(!view)) { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Uint8Array"_s); - return {}; + return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "target"_s, "Buffer or Uint8Array"_s, arg0); } if (UNLIKELY(view->isDetached())) { @@ -995,39 +951,44 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_compareBody(JSC::JSG switch (callFrame->argumentCount()) { default: sourceEndValue = callFrame->uncheckedArgument(4); + if (sourceEndValue != jsUndefined()) + sourceEnd = parseIndex(lexicalGlobalObject, throwScope, "sourceEnd"_s, sourceEndValue, sourceEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); FALLTHROUGH; case 4: sourceStartValue = callFrame->uncheckedArgument(3); + if (sourceStartValue != jsUndefined()) + sourceStart = parseIndex(lexicalGlobalObject, throwScope, "sourceStart"_s, sourceStartValue, sourceEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); FALLTHROUGH; case 3: targetEndValue = callFrame->uncheckedArgument(2); + if (targetEndValue != jsUndefined()) + targetEnd = parseIndex(lexicalGlobalObject, throwScope, "targetEnd"_s, targetEndValue, targetEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); FALLTHROUGH; case 2: targetStartValue = callFrame->uncheckedArgument(1); + if (targetStartValue != jsUndefined()) + targetStart = parseIndex(lexicalGlobalObject, throwScope, "targetStart"_s, targetStartValue, targetEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); break; case 1: case 0: break; } - if (!targetStartValue.isUndefined()) { - targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(1)); - RETURN_IF_EXCEPTION(throwScope, {}); + if (targetStart > targetEndInit && targetStart <= targetEnd) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "targetStart"_s, 0, targetEndInit, targetStartValue); } - - if (!targetEndValue.isUndefined()) { - targetEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); - RETURN_IF_EXCEPTION(throwScope, {}); + if (targetEnd > targetEndInit && targetEnd >= targetStart) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "targetEnd"_s, 0, targetEndInit, targetEndValue); } - - if (!sourceStartValue.isUndefined()) { - sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); - RETURN_IF_EXCEPTION(throwScope, {}); + if (sourceStart > sourceEndInit && sourceStart <= sourceEnd) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "sourceStart"_s, 0, sourceEndInit, sourceStartValue); } - - if (!sourceEndValue.isUndefined()) { - sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(4)); - RETURN_IF_EXCEPTION(throwScope, {}); + if (sourceEnd > sourceEndInit && sourceEnd >= sourceStart) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "sourceEnd"_s, 0, sourceEndInit, sourceEndValue); } targetStart = std::min(targetStart, std::min(targetEnd, targetEndInit)); @@ -1081,33 +1042,24 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlob switch (callFrame->argumentCount()) { default: sourceEndValue = callFrame->uncheckedArgument(3); + sourceEnd = parseIndex(lexicalGlobalObject, throwScope, "sourceEnd"_s, callFrame->uncheckedArgument(3), sourceEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); FALLTHROUGH; case 3: sourceStartValue = callFrame->uncheckedArgument(2); + sourceStart = parseIndex(lexicalGlobalObject, throwScope, "sourceStart"_s, callFrame->uncheckedArgument(2), sourceEndInit); + RETURN_IF_EXCEPTION(throwScope, {}); FALLTHROUGH; case 2: targetStartValue = callFrame->uncheckedArgument(1); + targetStart = parseIndex(lexicalGlobalObject, throwScope, "targetStart"_s, callFrame->uncheckedArgument(1), targetEnd); + RETURN_IF_EXCEPTION(throwScope, {}); break; case 1: case 0: break; } - if (!targetStartValue.isUndefined()) { - targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(1)); - RETURN_IF_EXCEPTION(throwScope, {}); - } - - if (!sourceStartValue.isUndefined()) { - sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); - RETURN_IF_EXCEPTION(throwScope, {}); - } - - if (!sourceEndValue.isUndefined()) { - sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); - RETURN_IF_EXCEPTION(throwScope, {}); - } - targetStart = std::min(targetStart, targetEnd); sourceEnd = std::min(sourceEnd, sourceEndInit); sourceStart = std::min(sourceStart, sourceEnd); @@ -1206,12 +1158,12 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob } if (!offsetValue.isUndefined()) { - start = parseIndex(lexicalGlobalObject, scope, offsetValue); + start = parseIndex(lexicalGlobalObject, scope, "start"_s, offsetValue, limit); RETURN_IF_EXCEPTION(scope, {}); } if (!lengthValue.isUndefined()) { - end = parseIndex(lexicalGlobalObject, scope, lengthValue); + end = parseIndex(lexicalGlobalObject, scope, "end"_s, lengthValue, limit - start); RETURN_IF_EXCEPTION(scope, {}); } @@ -1232,8 +1184,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob if (str.len == 0) { memset(startPtr, 0, end - start); } else if (UNLIKELY(!Bun__Buffer_fill(&str, startPtr, end - start, encoding))) { - throwTypeError(lexicalGlobalObject, scope, "Failed to decode value"_s); - return {}; + return Bun::ERR::INVALID_ARG_VALUE(scope, lexicalGlobalObject, "value"_s, value); } } else if (auto* view = JSC::jsDynamicCast(value)) { auto* startPtr = castedThis->typedVector() + start; @@ -1527,6 +1478,9 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj if (UNLIKELY(length == 0)) { RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsEmptyString(vm))); } + if (length > WTF::String::MaxLength) { + return Bun::ERR::STRING_TOO_LONG(scope, lexicalGlobalObject); + } JSC::EncodedJSValue ret = 0; @@ -2316,6 +2270,17 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi if (distinguishingArg.isAnyInt()) { throwScope.release(); return JSBuffer__bufferFromLength(lexicalGlobalObject, distinguishingArg.asAnyInt()); + } else if (distinguishingArg.isNumber()) { + double lengthDouble = distinguishingArg.toIntegerWithTruncation(lexicalGlobalObject); + if (UNLIKELY(lengthDouble < 0 || lengthDouble > MAX_ARRAY_BUFFER_SIZE || lengthDouble != lengthDouble)) { + return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "size"_s, 0, MAX_ARRAY_BUFFER_SIZE, distinguishingArg); + } + return JSBuffer__bufferFromLength(lexicalGlobalObject, lengthDouble); + } else if (distinguishingArg.isUndefinedOrNull() || distinguishingArg.isBoolean()) { + auto arg_string = distinguishingArg.toWTFString(globalObject); + auto message = makeString("The first argument must be of type string or an instance of Buffer, ArrayBuffer, Array or an Array-like object. Received "_s, arg_string); + throwTypeError(lexicalGlobalObject, throwScope, message); + return {}; } else if (distinguishingArg.isCell()) { auto type = distinguishingArg.asCell()->type(); diff --git a/src/bun.js/bindings/NodeValidator.cpp b/src/bun.js/bindings/NodeValidator.cpp index ed79007d2c..b404586c83 100644 --- a/src/bun.js/bindings/NodeValidator.cpp +++ b/src/bun.js/bindings/NodeValidator.cpp @@ -430,7 +430,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBuffer, (JSC::JSGlobalObject * globa auto ty = buffer.asCell()->type(); if (JSC::typedArrayType(ty) == NotTypedArray) { - return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "Buffer, TypedArray, or DataView"_s, buffer, true); + return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "Buffer, TypedArray, or DataView"_s, buffer); } return JSValue::encode(jsUndefined()); }