Skip to content

Commit 9adf42b

Browse files
Handle oom in Buffer.byteLength (#13746)
1 parent d3bc0ca commit 9adf42b

File tree

2 files changed

+98
-65
lines changed

2 files changed

+98
-65
lines changed

src/bun.js/bindings/JSBuffer.cpp

+92-65
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
12
#include "root.h"
23

4+
#include "JavaScriptCore/ExceptionHelpers.h"
35
#include "JavaScriptCore/JSString.h"
46
#include "JavaScriptCore/Error.h"
57
#include "JavaScriptCore/JSArrayBufferView.h"
@@ -105,6 +107,86 @@ static JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_toString);
105107
static JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_write);
106108
#pragma clang diagnostic pop
107109

110+
namespace Bun {
111+
112+
// Use a JSString* here to avoid unnecessarily joining the rope string.
113+
// If we're only getting the length property, it won't join the rope string.
114+
std::optional<double> byteLength(JSC::JSString* str, WebCore::BufferEncodingType encoding)
115+
{
116+
if (str->length() == 0)
117+
return 0;
118+
119+
switch (encoding) {
120+
121+
case WebCore::BufferEncodingType::ucs2:
122+
case WebCore::BufferEncodingType::utf16le: {
123+
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L600
124+
return str->length() * 2;
125+
}
126+
127+
case WebCore::BufferEncodingType::latin1:
128+
case WebCore::BufferEncodingType::ascii: {
129+
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L627
130+
return str->length();
131+
}
132+
133+
case WebCore::BufferEncodingType::base64:
134+
case WebCore::BufferEncodingType::base64url: {
135+
int64_t length = str->length();
136+
const auto& view = str->tryGetValue(true);
137+
if (UNLIKELY(view->isNull())) {
138+
return std::nullopt;
139+
}
140+
141+
if (view->is8Bit()) {
142+
const auto span = view->span8();
143+
if (span.data()[length - 1] == 0x3D) {
144+
length--;
145+
146+
if (length > 1 && span.data()[length - 1] == '=')
147+
length--;
148+
}
149+
} else {
150+
const auto span = view->span16();
151+
if (span.data()[length - 1] == 0x3D) {
152+
length--;
153+
154+
if (length > 1 && span.data()[length - 1] == '=')
155+
length--;
156+
}
157+
}
158+
159+
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L579
160+
return static_cast<double>((length * 3) >> 2);
161+
}
162+
163+
case WebCore::BufferEncodingType::hex: {
164+
return str->length() >> 1;
165+
}
166+
167+
case WebCore::BufferEncodingType::utf8: {
168+
const auto& view = str->tryGetValue(true);
169+
if (UNLIKELY(view->isNull())) {
170+
return std::nullopt;
171+
}
172+
173+
if (view->is8Bit()) {
174+
const auto span = view->span8();
175+
return Bun__encoding__byteLengthLatin1(span.data(), span.size(), static_cast<uint8_t>(encoding));
176+
} else {
177+
const auto span = view->span16();
178+
return Bun__encoding__byteLengthUTF16(span.data(), span.size(), static_cast<uint8_t>(encoding));
179+
}
180+
}
181+
default: {
182+
RELEASE_ASSERT_NOT_REACHED();
183+
}
184+
}
185+
186+
return std::nullopt;
187+
}
188+
}
189+
108190
static JSUint8Array* allocBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t byteLength)
109191
{
110192
#if ASSERT_ENABLED
@@ -252,7 +334,11 @@ static inline JSC::EncodedJSValue writeToBuffer(JSC::JSGlobalObject* lexicalGlob
252334
if (UNLIKELY(str->length() == 0))
253335
return JSC::JSValue::encode(JSC::jsNumber(0));
254336

255-
const auto& view = str->tryGetValue(lexicalGlobalObject);
337+
const auto& view = str->value(lexicalGlobalObject);
338+
if (view->isNull()) {
339+
return {};
340+
}
341+
256342
size_t written = 0;
257343

258344
switch (encoding) {
@@ -590,73 +676,14 @@ static inline JSC::EncodedJSValue jsBufferByteLengthFromStringAndEncoding(JSC::J
590676
return JSC::JSValue::encode(jsUndefined());
591677
}
592678

593-
if (str->length() == 0)
594-
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(0)));
595-
596-
int64_t written = 0;
597-
598-
switch (encoding) {
599-
600-
case WebCore::BufferEncodingType::ucs2:
601-
case WebCore::BufferEncodingType::utf16le: {
602-
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L600
603-
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(str->length() * 2)));
604-
}
605-
606-
case WebCore::BufferEncodingType::latin1:
607-
case WebCore::BufferEncodingType::ascii: {
608-
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L627
609-
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(str->length())));
610-
}
611-
612-
case WebCore::BufferEncodingType::base64:
613-
case WebCore::BufferEncodingType::base64url: {
614-
int64_t length = str->length();
615-
const auto& view = str->tryGetValue(lexicalGlobalObject);
616-
617-
if (view->is8Bit()) {
618-
const auto span = view->span8();
619-
if (span.data()[length - 1] == 0x3D) {
620-
length--;
621-
622-
if (length > 1 && span.data()[length - 1] == '=')
623-
length--;
624-
}
625-
} else {
626-
const auto span = view->span16();
627-
if (span.data()[length - 1] == 0x3D) {
628-
length--;
629-
630-
if (length > 1 && span.data()[length - 1] == '=')
631-
length--;
632-
}
633-
}
634-
635-
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L579
636-
return JSValue::encode(jsNumber(static_cast<double>((length * 3) >> 2)));
637-
}
638-
639-
case WebCore::BufferEncodingType::hex: {
640-
return JSValue::encode(jsNumber(str->length() >> 1));
641-
}
642-
643-
case WebCore::BufferEncodingType::utf8: {
644-
const auto& view = str->tryGetValue(lexicalGlobalObject);
645-
if (view->is8Bit()) {
646-
const auto span = view->span8();
647-
written = Bun__encoding__byteLengthLatin1(span.data(), span.size(), static_cast<uint8_t>(encoding));
648-
} else {
649-
const auto span = view->span16();
650-
written = Bun__encoding__byteLengthUTF16(span.data(), span.size(), static_cast<uint8_t>(encoding));
651-
}
652-
break;
653-
}
654-
default: {
655-
break;
679+
if (auto length = Bun::byteLength(str, encoding)) {
680+
return JSValue::encode(jsNumber(*length));
656681
}
682+
if (!scope.exception()) {
683+
throwOutOfMemoryError(lexicalGlobalObject, scope);
657684
}
658685

659-
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(written)));
686+
return {};
660687
}
661688
static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
662689
{

src/bun.js/bindings/JSBuffer.h

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ extern "C" JSC::EncodedJSValue Bun__encoding__toStringUTF8(const uint8_t* input,
3535
extern "C" bool Bun__Buffer_fill(ZigString*, void*, size_t, WebCore::BufferEncodingType);
3636
extern "C" bool JSBuffer__isBuffer(JSC::JSGlobalObject*, JSC::EncodedJSValue);
3737

38+
namespace Bun {
39+
40+
std::optional<double> byteLength(JSC::JSString* str, WebCore::BufferEncodingType encoding);
41+
42+
}
43+
3844
namespace WebCore {
3945

4046
JSC::JSUint8Array* createUninitializedBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t length);

0 commit comments

Comments
 (0)