Closed Bug 1743084 Opened 4 years ago Closed 4 years ago

Crash in js::ContextChecks::check or js::TenuringTracer::onObjectEdge(JSObject*) with WebGL canvas test case

Categories

(Core :: Graphics: CanvasWebGL, defect, P1)

defect

Tracking

()

RESOLVED DUPLICATE of bug 1743767

People

(Reporter: attekett, Assigned: jgilbert)

Details

(4 keywords, Whiteboard: [bugmon:bisected,confirmed])

Attachments

(3 files)

Tested on:
OS: VMware VM Ubuntu 20.04
Firefox:

fuzzfetch --target firefox --os Linux --asan --fuzzing -n firefox
[2021-11-26 10:59:49] Identified task: https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.v2.mozilla-central.latest.firefox.linux64-fuzzing-asan-opt
[2021-11-26 10:59:49] > Task ID: FA7CXMK1TRCGYky8cAtyPQ
[2021-11-26 10:59:49] > Rank: 1637903210
[2021-11-26 10:59:49] > Changeset: 422457edff0355ba42d7d5caab19fa9eb9457ee3
[2021-11-26 10:59:49] > Build ID: 20211126050650
[2021-11-26 10:59:51] > Downloading: https://firefox-ci-tc.services.mozilla.com/api/queue/v1/task/FA7CXMK1TRCGYky8cAtyPQ/artifacts/public/build/target.tar.bz2 (381.31MiB total)

The repro-file has been reliably reproducing for me, without custom prefs.js or ffpuppet,
but just to be sure, I tested it with ffpuppet, prefpicker fuzzing prefs.js and Xvfb.

ffpuppet ~/firefox/firefox -p ~/prefs.js -u ./SEGV-load-load-headerPtr-base0.html --xvfb

Prefpicker can generate different set of prefs, so my prefs.js as an attachment.

Minimized repro-file as an attachment.

ASAN-trace:

=================================================================
==75641==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7fb1d8256eb3 bp 0x7ffe18284b80 sp 0x7ffe18284b80 T0)
==75641==The signal is caused by a READ memory access.
==75641==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7fb1d8256eb3 in load /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/atomic_base.h:396:9
    #1 0x7fb1d8256eb3 in load /builds/worker/workspace/obj-build/dist/include/mozilla/Atomics.h:195:17
    #2 0x7fb1d8256eb3 in operator unsigned long /builds/worker/workspace/obj-build/dist/include/mozilla/Atomics.h:340:31
    #3 0x7fb1d8256eb3 in headerPtr /builds/worker/checkouts/gecko/js/src/gc/Cell.h:786:46
    #4 0x7fb1d8256eb3 in base /builds/worker/checkouts/gecko/js/src/vm/Shape.h:268:36
    #5 0x7fb1d8256eb3 in compartment /builds/worker/checkouts/gecko/js/src/vm/Shape.h:379:49
    #6 0x7fb1d8256eb3 in compartment /builds/worker/checkouts/gecko/js/src/vm/JSObject.h:149:58
    #7 0x7fb1d8256eb3 in js::ContextChecks::check(JSObject*, int) /builds/worker/checkouts/gecko/js/src/vm/JSContext-inl.h:90:18
    #8 0x7fb1d8256c4d in js::ContextChecks::check(JS::CallArgs const&, int) /builds/worker/checkouts/gecko/js/src/vm/JSContext-inl.h:160:7
    #9 0x7fb1d823f5ea in checkImpl<JS::CallArgs> /builds/worker/checkouts/gecko/js/src/vm/JSContext-inl.h:213:33
    #10 0x7fb1d823f5ea in check<JS::CallArgs> /builds/worker/checkouts/gecko/js/src/vm/JSContext-inl.h:220:5
    #11 0x7fb1d823f5ea in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:384:7
    #12 0x7fb1d823f5ea in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:475:12
    #13 0x7fb1d822bc34 in CallFromStack /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:539:10
    #14 0x7fb1d822bc34 in Interpret(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:3243:16
    #15 0x7fb1d8210a61 in js::RunScript(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:357:13
    #16 0x7fb1d8243793 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, JS::Handle<JS::Value>, js::AbstractFramePtr, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:730:13
    #17 0x7fb1d84eb52b in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/CompilationAndEvaluation.cpp:488:10
    #18 0x7fb1d84eb835 in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>) /builds/worker/checkouts/gecko/js/src/vm/CompilationAndEvaluation.cpp:512:10
  ...
/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
    #48 0x7fb1d7f5c462 in XRE_InitChildProcess(int, char**, XREChildData const*) /builds/worker/checkouts/gecko/toolkit/xre/nsEmbedFunctions.cpp:701:34
    #49 0x55efe3ee2dfd in content_process_main(mozilla::Bootstrap*, int, char**) /builds/worker/checkouts/gecko/browser/app/../../ipc/contentproc/plugin-container.cpp:57:28
    #50 0x55efe3ee3228 in main /builds/worker/checkouts/gecko/browser/app/nsBrowserApp.cpp:327:18
    #51 0x7fb1e9eb50b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
    #52 0x55efe3e31ec9 in _start (/home/attekett/Downloads/firefox/firefox+0x5cec9)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/atomic_base.h:396:9 in load
==75641==ABORTING
Attached file prefs.js

Attaching prefs.js file generated via prefpicker.

This issue is likely to be related to this issue, as the repro-file is almost identical. I can make another bug for it, if it is a separate issue.

If you change the repro-file to:

<canvas id="canvas" width="4" height="4"></canvas>
<script>
var N = 4;
var gl = canvas.getContext('webgl')
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  var result_1 = new Uint8Array(N * N * 4);
  var result_2 = new Uint8Array(N * N * 4);
  gl.readPixels(0, -1, N, N, gl.RGBA, gl.UNSIGNED_BYTE, result_1);
</script>

You get a similar high address segfault, but with very different stack trace:

==86175==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7f1b595f3c6d bp 0x7ffe32390720 sp 0x7ffe32390720 T0)
==86175==The signal is caused by a READ memory access.
==86175==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7f1b595f3c6d in incTenuredCount /builds/worker/checkouts/gecko/js/src/gc/Pretenuring.h:130:24
    #1 0x7f1b595f3c6d in UpdateAllocSiteOnTenure /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:63:9
    #2 0x7f1b595f3c6d in js::TenuringTracer::onObjectEdge(JSObject*) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:76:3
    #3 0x7f1b595f5e24 in js::TenuringTracer::traverse(JS::Value*) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:145:29
    #4 0x7f1b595f78bf in traceSlots /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:438:5
    #5 0x7f1b595f78bf in operator() /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:431:5
    #6 0x7f1b595f78bf in forEachSlotRangeUnchecked<(lambda at /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:430:21)> /builds/worker/checkouts/gecko/js/src/vm/NativeObject.h:672:7
    #7 0x7f1b595f78bf in forEachSlotRange<(lambda at /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:430:21)> /builds/worker/checkouts/gecko/js/src/vm/NativeObject.h:679:5
    #8 0x7f1b595f78bf in js::TenuringTracer::traceObjectSlots(js::NativeObject*, unsigned int, unsigned int) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:433:9
    #9 0x7f1b595f6468 in js::gc::StoreBuffer::SlotsEdge::trace(js::TenuringTracer&) const /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:212:11
    #10 0x7f1b595f62b1 in js::gc::StoreBuffer::MonoTypeBuffer<js::gc::StoreBuffer::SlotsEdge>::trace(js::TenuringTracer&) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:167:15
...

From there, removing

6   gl.clear(gl.COLOR_BUFFER_BIT);

Leads to a null+offset segfault, with similar stack-trace as the previous:

=================================================================
==86874==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000001c (pc 0x7f6b9e827c7d bp 0x7ffd8a668fe0 sp 0x7ffd8a668fe0 T0)
==86874==The signal is caused by a READ memory access.
==86874==Hint: address points to the zero page.
    #0 0x7f6b9e827c7d in incTenuredCount /builds/worker/checkouts/gecko/js/src/gc/Pretenuring.h:130:24
    #1 0x7f6b9e827c7d in UpdateAllocSiteOnTenure /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:63:9
    #2 0x7f6b9e827c7d in js::TenuringTracer::onObjectEdge(JSObject*) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:76:3
    #3 0x7f6b9e829e24 in js::TenuringTracer::traverse(JS::Value*) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:145:29
    #4 0x7f6b9e82b8bf in traceSlots /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:438:5
    #5 0x7f6b9e82b8bf in operator() /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:431:5
    #6 0x7f6b9e82b8bf in forEachSlotRangeUnchecked<(lambda at /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:430:21)> /builds/worker/checkouts/gecko/js/src/vm/NativeObject.h:672:7
    #7 0x7f6b9e82b8bf in forEachSlotRange<(lambda at /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:430:21)> /builds/worker/checkouts/gecko/js/src/vm/NativeObject.h:679:5
    #8 0x7f6b9e82b8bf in js::TenuringTracer::traceObjectSlots(js::NativeObject*, unsigned int, unsigned int) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:433:9
    #9 0x7f6b9e82a468 in js::gc::StoreBuffer::SlotsEdge::trace(js::TenuringTracer&) const /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:212:11
....
Group: firefox-core-security → gfx-core-security
Component: General → Canvas: WebGL
Product: Firefox → Core
Keywords: bugmon, crash, testcase

Experimental prefs are turned on, but the testcase is simple and maybe that doesn't matter?
user_pref("webgl.enable-draft-extensions", true);
user_pref("webgl.enable-privileged-extensions", true);

Although the testcase is WebGL, the crash involves compartment checks on TypedArrays so moving to JS.

Component: Canvas: WebGL → JavaScript Engine
Group: gfx-core-security → javascript-core-security
Summary: High address SEGV in WebGL readPixels → Crash in js::ContextChecks::check or js::TenuringTracer::onObjectEdge(JSObject*) with WebGL canvas test case

Ah, I only just noticed the very GC-looking crashes in comment 2. Maybe this will be of interest to Jon or Steve.

Flags: needinfo?(sphink)
Flags: needinfo?(jcoppeard)

FWIW, The issue reproduces with ffpuppet without usage of the prefs.js. Also I tested that it reproduces with the mentioned webgl.enable-draft-extensions and webgl.enable-privileged-extensions set to false, from the attached prefs.js.

Bugmon Analysis
Verified bug as reproducible on mozilla-central 20211202215058-260e2362bbc3.
Failed to bisect testcase (Testcase reproduces on start build!):

Start: ee7cd95a414c7307a137c07e0c367c336a74a737 (20201204033450)
End: 422457edff0355ba42d7d5caab19fa9eb9457ee3 (20211126050650)
BuildFlags: BuildFlags(asan=True, tsan=False, debug=False, fuzzing=False, coverage=False, valgrind=False, no_opt=False, fuzzilli=False)

Whiteboard: [bugmon:bisected,confirmed]

The crashes happen when dereferencing pointers read from the first word of a GC thing, or the nursery header for a nursery-allocated GC thing for the second crash in comment 2. These pointers are either wrong or have been corrupted.

The testcase creates webGL canvas and reads pixel data from it into a buffer. Comment 2 demonstrates that changing the color of the canvas changes the crash that happens afterwards. This is pretty suggestive that there's some kind of buffer overrun happening.

I verified this by changing the color to suitably chosen values, and made 0xdeadbeef show up in the RAX register:

Crash reason:  SIGSEGV / SI_KERNEL
Crash address: 0x0
Process uptime: not available

Thread 0 file:// Content (crashed)
 0  libxul.so!void js::CheckTracedThing<JSObject>(JSTracer*, JSObject*) [Marking.cpp : 190 + 0xf]
    rax = 0xdeadbeefdea00000   rdx = 0x00007f57d427f4a0

Looking first call to readPixels, it is reading from a rectangle that is partially outside the canvas. The WebGL spec says "For any pixel lying outside the frame buffer, the corresponding destination buffer range remains untouched". My guess is that something is going wrong handling that.

Group: javascript-core-security → core-security
Component: JavaScript Engine → Graphics
Flags: needinfo?(sphink)
Flags: needinfo?(jcoppeard)
Keywords: sec-high
Group: core-security → gfx-core-security
Component: Graphics → Canvas: WebGL
Flags: sec-bounty?

Kelsey, this is a sec-high bug with a testcase, can you take a look at it?

Assignee: nobody → jgilbert
Severity: -- → S2
Priority: -- → P1

Please retest now that we've landed bug 1750310.

Flags: needinfo?(attekett)

Still reproduces.

Tested with:

:$ fuzzfetch --target firefox --central --os Linux --asan --fuzzing -n firefox
[2022-02-24 23:42:17] Identified task: https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.v2.mozilla-central.latest.firefox.linux64-fuzzing-asan-opt
[2022-02-24 23:42:17] > Task ID: OuiYrcUqRpyXyRqT52NfQQ
[2022-02-24 23:42:17] > Rank: 1645739161
[2022-02-24 23:42:17] > Changeset: fb443d9a5f9cfaa17acc81c25473d7093d5cf696
[2022-02-24 23:42:17] > Build ID: 20220224214601
[2022-02-24 23:42:17] > Downloading: https://firefox-ci-tc.services.mozilla.com/api/queue/v1/task/OuiYrcUqRpyXyRqT52NfQQ/artifacts/public/build/target.tar.bz2 (436.42MiB total)
[2022-02-24 23:42:31] .. downloaded (32.73MB/s)
[2022-02-24 23:42:31] .. extracting
[2022-02-24 23:46:07] Extracted into /firefox

Reproduce:

:$ ffpuppet ./firefox/firefox -u ./SEGV-load-load-headerPtr-base.html -d
[2022-02-25 00:06:28] Launching Firefox...
[2022-02-25 00:06:43] Running Firefox (pid: 3218588)...
[2022-02-25 00:06:45] Shutting down...
[2022-02-25 00:06:47] Firefox process is closed. (Reason: ALERT)
[2022-02-25 00:06:47] Displaying logs...
===
=== Dumping 'log_ffp_asan_3218586.log.3218676.txt' (7.89KB)
===
=================================================================
==3218676==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7f5e50177653 bp 0x7ffc596de9e0 sp 0x7ffc596de9e0 T0)
==3218676==The signal is caused by a READ memory access.
==3218676==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7f5e50177653 in load /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/atomic_base.h:396:9
    #1 0x7f5e50177653 in load /builds/worker/workspace/obj-build/dist/include/mozilla/Atomics.h:195:17
    #2 0x7f5e50177653 in operator unsigned long /builds/worker/workspace/obj-build/dist/include/mozilla/Atomics.h:340:31
    #3 0x7f5e50177653 in headerPtr /builds/worker/checkouts/gecko/js/src/gc/Cell.h:788:46
...
Flags: needinfo?(attekett)

Thanks!

Bugmon Analysis
Testcase crashes using the initial build (mozilla-central 20211126050650-422457edff03) but not with tip (mozilla-central 20220415213125-86271ddb1099.)
The bug appears to have been fixed in the following build range:

Start: d6aabaf771d4c6ee0a63b24e79b3afe379ce7bd6 (20220301204359)
End: 1edd1e4056e62592fe5f457926e5012ad30003d6 (20220301205259)
Pushlog: https://hg.mozilla.org/integration/autoland/pushloghtml?fromchange=d6aabaf771d4c6ee0a63b24e79b3afe379ce7bd6&tochange=1edd1e4056e62592fe5f457926e5012ad30003d6
Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.

Keywords: bugmon

Bugmon thinks it's fixed!
Seems like it was fixed by enabling oop-webgl on linux, which probably means we've moved this corruption into the parent process, but the crash is gone.

Looking at the testcase, I believe this is bug 1743767, OOB-write in ReadPixels with negative offsets.

Status: NEW → RESOLVED
Closed: 4 years ago
Resolution: --- → DUPLICATE

Atte: does it appear the patch in bug 1743767 also fixes this bug? (note: it has not landed yet) Or do you dispute the duplicate resolution?

Flags: needinfo?(attekett)

Based on the testcase, this issue is very likely to be duplicate of bug 1743767, but as mentioned earlier this variant was already "fixed" by oop-webgl on Linux. I haven't seen this on my cluster since then.

So not disputing that the root cause is the same, but one could argue that from bounty point-of-view these issues were fixed by a different patches, and the root cause could have been missed without the other report.

I'll leave it to your consideration.

Flags: needinfo?(attekett)

The 'oop-webgl' work was already in progress, and I don't think you can call it a "fix" even though it disrupted this particular path to triggering the vulnerability.

Flags: sec-bounty? → sec-bounty-
Group: gfx-core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: