Closed Bug 1997029 Opened 11 months ago Closed 11 months ago

Assertion failure: result.index <= linear->length(), at js/src/vm/TypedArrayObject.cpp:5220 [OOBR]

Categories

(Core :: JavaScript: Standard Library, defect, P1)

defect

Tracking

()

RESOLVED FIXED
146 Branch
Tracking Status
firefox-esr115 --- unaffected
firefox-esr140 --- unaffected
firefox144 --- unaffected
firefox145 --- unaffected
firefox146 --- fixed

People

(Reporter: sakura, Assigned: anba)

References

(Blocks 1 open bug, Regression)

Details

(4 keywords, Whiteboard: [client-bounty-form])

Attachments

(1 file)

48 bytes, text/x-phabricator-request
Details | Review

Reproduce

  1. Clone the Firefox mirror from https://github.com/mozilla-firefox/firefox
  2. Run build command in the js/src directory of the firefox checkout:
    mkdir fuzzbuild_OPT.OBJ && cd fuzzbuild_OPT.OBJ && ../configure --enable-address-sanitizer --disable-jemalloc --enable-debug --enable-optimize --disable-shared-js --enable-application=js --enable-gczeal && make -j64
    
  3. Run poc: fuzzbuild_OPT.OBJ/dist/bin/js poc.js

Test SpiderMonkey commit hash: 75f89f7dee3f30c371af717da54ba8bbf046e5fd

Crash Stack Trace

--------------------------------------------------------------------------------
[1706037] Assertion failure: result.index <= linear->length(), at /home/sakura/gecko-dev/js/src/vm/TypedArrayObject.cpp:5220
#01: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x256e4e2]
#02: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e01e8f]
#03: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e01453]
#04: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e18331]
#05: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e00353]
#06: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e04b91]
#07: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1e053cd]
#08: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1fe9eea]
#09: JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>)[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1fea140]
#10: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1d5d6ad]
#11: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1d5ca16]
#12: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1cc2b6a]
#13: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1cb64a8]
#14: ???[/lib/x86_64-linux-gnu/libc.so.6 +0x29d90]
#15: __libc_start_main[/lib/x86_64-linux-gnu/libc.so.6 +0x29e40]
#16: ???[/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js +0x1c76dd9]
#17: ??? (???:???)
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==1706037==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5589a68f44fa bp 0x7ffd013fbca0 sp 0x7ffd013fbb90 T1706037)
==1706037==The signal is caused by a WRITE memory access.
==1706037==Hint: address points to the zero page.
#0 0x5589a68f44fa in MOZ_CrashSequence(void*, long) /home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/include/mozilla/Assertions.h:237:3
#1 0x5589a68f44fa in uint8array_fromBase64(JSContext*, unsigned int, JS::Value*) /home/sakura/gecko-dev/js/src/vm/TypedArrayObject.cpp:5220:3
#2 0x5589a6187e8e in CallJSNative(JSContext*, bool (*)(JSContext*, unsigned int, JS::Value*), js::CallReason, JS::CallArgs const&) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:490:13
#3 0x5589a6187452 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:586:12
#4 0x5589a619e330 in js::CallFromStack(JSContext*, JS::CallArgs const&, js::CallReason) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:658:10
#5 0x5589a619e330 in js::Interpret(JSContext*, js::RunState&) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:3276:16
#6 0x5589a6186352 in js::RunScript(JSContext*, js::RunState&) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:460:13
#7 0x5589a618ab90 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, js::AbstractFramePtr, JS::MutableHandle<JS::Value>) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:851:13
#8 0x5589a618b3cc in js::Execute(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, JS::MutableHandle<JS::Value>) /home/sakura/gecko-dev/js/src/vm/Interpreter.cpp:884:10
#9 0x5589a636fee9 in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) /home/sakura/gecko-dev/js/src/vm/CompilationAndEvaluation.cpp:548:10
#10 0x5589a637013f in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>) /home/sakura/gecko-dev/js/src/vm/CompilationAndEvaluation.cpp:572:10
#11 0x5589a60e36ac in RunFile(JSContext*, char const*, _IO_FILE*, CompileUtf8, bool, bool) /home/sakura/gecko-dev/js/src/shell/js.cpp:1315:10
#12 0x5589a60e2a15 in Process(JSContext*, char const*, bool, FileKind) /home/sakura/gecko-dev/js/src/shell/js.cpp:2106:14
#13 0x5589a6048b69 in ProcessArgs(JSContext*, js::cli::OptionParser*) /home/sakura/gecko-dev/js/src/shell/js.cpp:12051:10
#14 0x5589a6048b69 in Shell(JSContext*, js::cli::OptionParser*) /home/sakura/gecko-dev/js/src/shell/js.cpp:12305:12
#15 0x5589a603c4a7 in main /home/sakura/gecko-dev/js/src/shell/js.cpp:12708:12
#16 0x7f4ec2a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#17 0x7f4ec2a29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#18 0x5589a5ffcdd8 in _start (/home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/bin/js+0x1c76dd8) (BuildId: aa541a5979c2c6bc3b0eca2fc9cd1eb3)

==1706037==Register values:
rax = 0x0000000000000000  rbx = 0x00007f4ec273c200  rcx = 0x0000000000001464  rdx = 0x00007f4ec2c1b723
rdi = 0x00007f4ec2c1ca60  rsi = 0x0000000000000000  rbp = 0x00007ffd013fbca0  rsp = 0x00007ffd013fbb90
r8 = 0x0000000000000000   r9 = 0x0000000000000003  r10 = 0x00005589a86cb070  r11 = 0x0000000000000293
r12 = 0x0000000000000000  r13 = 0x00007ffd013fbbc0  r14 = 0x00007f4ec275a0a8  r15 = 0x0000251e73b62100
UndefinedBehaviorSanitizer can not provide additional info.
SUMMARY: UndefinedBehaviorSanitizer: SEGV /home/sakura/gecko-dev/js/src/fuzzbuild_OPT.OBJ/dist/include/mozilla/Assertions.h:237:3 in MOZ_CrashSequence(void*, long)
==1706037==ABORTING



================================================================================

PoC

try {
  var v3 = 'No error';
} catch (e) {}
v9 = v3;
try {
  Uint8Array.fromBase64(v9);
} catch (e) {}
function fn4() {}
// Flags: 
Flags: sec-bounty?
Group: firefox-core-security → core-security
Component: Security → JavaScript: Standard Library
Product: Firefox → Core
Group: core-security → javascript-core-security

This assertion looks like it was added recently in bug 1994067, so maybe anba can take a look. Thanks.

Flags: needinfo?(andrebargull)
Keywords: csectype-bounds
Attached file (secure) —
Assignee: nobody → andrebargull
Status: NEW → ASSIGNED
  1. The fast-path loop in FromBase64 relies solely on alignedLength = length & ~0x3 to decide whether another four-character chunk can be decoded. Once the slow path skips whitespace and executes index = i, alignedLength is not reduced, so index may move beyond the original string length yet still pass the index < alignedLength guard, resulting in decode4Chars(chars + index) being called (js/src/vm/TypedArrayObject.cpp:4755-4816).
  2. The extra decode4Chars invocation reads directly from chars[index + {0,1,2}], which allows an out-of-bounds access past the end of the JSLinearString buffer. If the stray bytes happen to form valid base64 digits, the decoded data is copied into the output buffer.
  3. The function returns Base64Result::Ok(read, …) with read derived from the incorrect index, triggering MOZ_ASSERT(result.index <= linear->length()); in Uint8Array.fromBase64 (js/src/vm/TypedArrayObject.cpp:5215-5221). Release builds omit the assertion, turning the bug into a controllable out-of-bounds read.
// js/src/vm/TypedArrayObject.cpp:4755-4816
size_t alignedLength = length & ~0x3;
while (canAppend(3) && index < alignedLength) {
  uint32_t chunk = decode4Chars(chars + index);
  if (MOZ_LIKELY(int32_t(chunk) >= 0)) {
    decodeChunk(chunk);
    index += 4;
    continue;
  }

  CharT part[4];
  size_t i = index;
  size_t j = 0;
  while (i < length && j < 4) {
    auto ch = chars[i++];
    if (mozilla::IsAsciiWhitespace(ch)) {
      continue;
    }
    part[j++] = ch;
  }

  if (MOZ_LIKELY(j == 4)) {
    uint32_t chunk = decode4Chars(part);
    if (MOZ_LIKELY(int32_t(chunk) >= 0)) {
      decodeChunk(chunk);
      index = i;  // index can advance past `length`
      continue;
    }
  }
  break;
}

A bounds-ish assertion doesn't always mean an out of bounds issue, but based on comment 3, it sounds like it could lead to that in this case, so I'll mark this sec-high.

Keywords: sec-high

This can only read up to two characters after the end of the string. And the content of those characters can only be read when they happen to be valid Base64 characters.

Flags: needinfo?(andrebargull)
Blocks: sm-security
Severity: -- → S2
Priority: -- → P1

Can you confirm, that this was caused by bug 1995626?

Flags: needinfo?(andrebargull)
Keywords: regression
Regressed by: 1995626

Yes, it's a regression from bug 1995626.

Flags: needinfo?(andrebargull)
Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 11 months ago
Resolution: --- → FIXED
Target Milestone: --- → 146 Branch
QA Whiteboard: [qa-triage-done-c147/b146]
QA Whiteboard: [qa-triage-done-c147/b146] → [sec] [qa-triage-done-c147/b146]

We're lowering the severity to sec-moderate because this is limited to an OOBR, not a write, and of a very limited amount of constrained data that's unlikely to be useful in an attack escalation or data disclosure.

Flags: sec-bounty? → sec-bounty+
Keywords: sec-high → sec-moderate
Summary: Assertion failure: result.index <= linear->length(), at js/src/vm/TypedArrayObject.cpp:5220 → Assertion failure: result.index <= linear->length(), at js/src/vm/TypedArrayObject.cpp:5220 [OOBR]
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: