Skip to content

fix: add maximum length limit to prevent OOM (resolves #4409)#4731

Open
souravk29 wants to merge 7 commits intoboa-dev:mainfrom
souravk29:fix/oom-crash-file-4409
Open

fix: add maximum length limit to prevent OOM (resolves #4409)#4731
souravk29 wants to merge 7 commits intoboa-dev:mainfrom
souravk29:fix/oom-crash-file-4409

Conversation

@souravk29
Copy link

@souravk29 souravk29 commented Feb 25, 2026

resolves #4409

for :

var s = '\u1234--synchronized-----';
for (var i = 0; i < 17; i++) {
  try {
    s += s;
    s += s;
  } catch (e) {
  }
}
s.replace(/a/g);

reached state:

Iteration 0: length = 40
Iteration 1: length = 80
Iteration 2: length = 160
Iteration 3: length = 320
Iteration 4: length = 640
Iteration 5: length = 1280
Iteration 6: length = 2560
Iteration 7: length = 5120
Iteration 8: length = 10240
Iteration 9: length = 20480
Iteration 10: length = 40960
Iteration 11: length = 81920
Iteration 12: length = 163840
Iteration 13: length = 327680
Iteration 14: length = 655360
Iteration 15: length = 1310720
Iteration 16: length = 2621440
Iteration 17: length = 5242880
Iteration 18: length = 10485760
Iteration 19: length = 20971520
Iteration 20: length = 41943040
Iteration 21: length = 83886080
Iteration 22: length = 167772160
Iteration 23: length = 335544320
Iteration 24: length = 671088640
Iteration 25: length = 1342177280
Iteration 26: length = 2684354560
Caught error at iteration 27: RangeError: Invalid string length: requested 5368709120 code units, maximum is 4294967295

i tried to solve this issue, please tell me further changes required

@souravk29 souravk29 requested a review from a team as a code owner February 25, 2026 20:28
@souravk29
Copy link
Author

souravk29 commented Feb 25, 2026

i am working to solve the broken tests, please leave your suggestions if any

@github-actions
Copy link

github-actions bot commented Feb 25, 2026

Test262 conformance changes

Test result main count PR count difference
Total 52,862 52,862 0
Passed 49,504 49,481 -23
Ignored 2,262 2,262 0
Failed 1,096 1,119 +23
Panics 0 0 0
Conformance 93.65% 93.60% -0.04%
Broken tests (23):
test/built-ins/String/prototype/replace/cstm-replace-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/replace/cstm-replace-on-boolean-primitive.js (previously Passed)
test/built-ins/String/prototype/replace/cstm-replace-on-number-primitive.js (previously Passed)
test/built-ins/String/prototype/replace/cstm-replace-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/matchAll/cstm-matchall-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/matchAll/cstm-matchall-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/matchAll/cstm-matchall-on-number-primitive.js (previously Passed)
test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-number-primitive.js (previously Passed)
test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-boolean-primitive.js (previously Passed)
test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/search/cstm-search-on-boolean-primitive.js (previously Passed)
test/built-ins/String/prototype/search/cstm-search-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/search/cstm-search-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/search/cstm-search-on-number-primitive.js (previously Passed)
test/built-ins/String/prototype/match/cstm-matcher-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/match/cstm-matcher-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/match/cstm-matcher-on-number-primitive.js (previously Passed)
test/built-ins/String/prototype/match/cstm-matcher-on-boolean-primitive.js (previously Passed)
test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js (previously Passed)
test/built-ins/String/prototype/split/cstm-split-on-bigint-primitive.js (previously Passed)
test/built-ins/String/prototype/split/cstm-split-on-boolean-primitive.js (previously Passed)
test/built-ins/String/prototype/split/cstm-split-on-number-primitive.js (previously Passed)

@souravk29
Copy link
Author

souravk29 commented Feb 27, 2026

@jedel1043 please review the PR and help me pass these failing tests .i have been trying to resolve it but not getting to the endpoint, i tried by modifying the is_object() to is_null_or_undefined() in mod.rs

names-valid.js (previously Ignored)
Broken tests (10):
test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-yes-to-true.js (previously Passed)
test/intl402/PluralRules/prototype/select/notation.js (previously Passed)
test/built-ins/RegExp/prototype/exec/regexp-builtin-exec-v-u-flag.js (previously Passed)
test/built-ins/String/prototype/replace/regexp-prototype-replace-v-u-flag.js (previously Passed)
test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js (previously Passed)
test/built-ins/String/prototype/search/regexp-prototype-search-v-u-flag.js (previously Passed)
test/built-ins/String/prototype/search/regexp-prototype-search-v-flag.js (previously Passed)
test/built-ins/String/prototype/match/regexp-prototype-match-v-u-flag.js (previously Passed)
test/staging/sm/RegExp/unicode-raw.js (previously Passed)
test/staging/sm/RegExp/unicode-class-raw.js (previously Passed) . 

@souravk29
Copy link
Author

my latest commit causes 23 failures(prev 10). please guide

Copy link
Contributor

@zhuzhu81998 zhuzhu81998 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general i believe you changed a few places too many which also caused your test failures with the primitives (see comments below) .

At the same time you missed some places including but not limited to core/string/src/builder.rs (which in the current version will still successfully build a JsString that is too long). Additionally, the replace function still does separate length checking which i think should be get rid of with a PR like this (although that require another way of implementing the function ig).

//
// `allocate_seq` must return a valid pointer to newly allocated memory, meaning
// `ptr` and all `string`s should never overlap.
// SAFETY: See original safety comments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well i think it would be better if you had just kept the original comment?


// Validate against maximum string length BEFORE attempting allocation
if count > MAX_STRING_LENGTH {
alloc_overflow();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason you let it panic here? seems to me that it should be a recoverable error here.

Comment on lines +1011 to 1013
// 2. If searchValue is neither undefined nor null, then
if !search_value.is_null_or_undefined() {
// a. Let replacer be ? GetMethod(searchValue, @@replace).
Copy link
Contributor

@zhuzhu81998 zhuzhu81998 Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this path is not for primitives so you need to check if it is an object.
i fail to understand why you modified this in the first place. this does not have anything to do with MAX_STRING_LENGTH ?

if you change all this back, it will solve your failing tests problem.

EDIT: it seems that is_object check was added by #4665 , so probably you branched before that and kept the old version of !search_value.is_null_or_undefined() and then got hit by failed tests.

Comment on lines +1119 to 1121
// 2. If searchValue is neither undefined nor null, then
if !search_value.is_null_or_undefined() {
// a. Let isRegExp be ? IsRegExp(searchValue).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also check is_object here

Comment on lines +1477 to 1478
if !regexp.is_null_or_undefined() {
// a. Let matcher be ? GetMethod(regexp, @@match).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here is wrong.

Comment on lines +2060 to 2061
if !regexp.is_null_or_undefined() {
// a. Let isRegExp be ? IsRegExp(regexp).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment on lines +2202 to 2203
if !regexp.is_null_or_undefined() {
// a. Let searcher be ? GetMethod(regexp, @@search).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+another 1

Comment on lines +683 to +684
Err(_) => alloc_overflow(), // Keep existing panic behavior for now
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well why keep this for now? the point was to make the construction of JsStrings fallible

Comment on lines +55 to 56
// Numeric addition - KEEP ORIGINAL LOGIC
match (x.to_numeric(context)?, y.to_numeric(context)?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like a comment that should have been kept to yourself and not part of a PR.

Comment on lines 88 to +93
pub(crate) fn try_allocate(len: usize) -> Result<NonNull<Self>, Option<Layout>> {
// Validate length BEFORE attempting Layout calculation
if len > crate::MAX_STRING_LENGTH {
return Err(None); // Signal overflow error
}
// Calculate layout using the original method
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm this check looks abundant and at the wrong layer. the attempted string length should have been checked already before try_allocate is called.

@jedel1043
Copy link
Member

what @zhuzhu81998 said

@jedel1043
Copy link
Member

jedel1043 commented Feb 28, 2026

Also I'm just gonna say that we kinda don't need to return an error on the js_string("string")-style macros. I doubt someone will pass a 2684354560-length string literal there (famous last words).

EDIT: we should also remove all uses of js_string that provide two or more arguments. It would be kinda weird to have js_string be infallible for single arguments but fallible for two or more arguments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add maximum length limit for JsString

3 participants