Ion skips Proxy get validation for non-writable array length
Categories
(Core :: JavaScript Engine, defect, P3)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox158 | --- | fixed |
People
(Reporter: bugmon, Assigned: iain)
References
(Blocks 1 open bug)
Details
(Keywords: sec-other, testcase, Whiteboard: [adv-main158-])
Attachments
(3 files, 1 external file)
|
847 bytes,
patch
|
Details | Diff | Splinter Review | |
|
1.15 KB,
text/plain
|
Details | |
|
432 bytes,
text/plain
|
Details |
GetObjectFlagsForNewProperty uses isDataProperty() when setting NeedsProxyGetSetResultValidation, excluding custom data properties such as array length. Making length non-writable therefore leaves the validation flag unset, causing Ion's scripted Proxy getter to skip the required result check.
A webpage can bypass JavaScript Proxy invariants by exercising Ion's optimized getter with an array whose length is non-writable. The verified result is 17 for a target length fixed at 0, where a TypeError is required. No memory corruption or sandbox escape was demonstrated.
This finding was discovered during variant analysis seeded by bug 1989978.
Build Info
- Branch: main
- Revision: 383240b1c47fcbe804430e196a78f57abf4e1dfb
- Timestamp: 2026-09-18T04:20:17+00:00
Affected Code
File: js/src/vm/ObjectFlags-inl.h, line 41-42
if (propFlags.isDataProperty() && !propFlags.writable()) {
flags.setFlag(ObjectFlag::NeedsProxyGetSetResultValidation);
File: js/src/vm/PropertyInfo.h, line 66-68
bool isDataProperty() const {
return !isAccessorProperty() && !isCustomDataProperty();
}
File: js/src/jit/CodeGenerator.cpp, line 17960-17963
masm.unboxObject(target, scratch);
masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
scratch2, ool->entry());
masm.bind(ool->rejoin());
ArraySetLength preserves the custom-data classification when clearing Writable (js/src/builtin/Array.cpp:915-923). The generated validation gate tests NeedsProxyGetSetResultValidation (js/src/jit/MacroAssembler.cpp:6233-6235). The interpreter instead checks the exposed data descriptor and rejects unequal values (js/src/proxy/ScriptedProxyHandler.cpp:1195-1203).
Exploit Chain
- Create an empty array and a Proxy whose get trap returns 17.
- Warm a function reading proxy.length until Ion compiles the optimized getter.
- Make the target array's length non-writable.
- Read through the optimized getter; it returns 17 instead of throwing the required TypeError.
Steps to Reproduce
- Use /app/output/verification-proxy-readonly-length/proxy-readonly-length.js.
- Build clean source using build_firefox with firefox_dir=/firefox and mozconfig_path=/app/mozconfigs/mozconfig.js.linux.debug.
- Run js_shell_differential_evaluator with /firefox/obj-js-debug/dist/bin/js for both shells, flags_a=[--no-threads,--no-jit-backend], and flags_b=[--no-threads,--ion-warmup-threshold=100]. The evaluator adds --fuzzing-safe.
- Observe differs: true, shell A exit 0, and shell B exit 3 with Error: Missing TypeError for immutable array length.
- Repeat with --differential-testing added to both flag sets; observe the same discrepancy without timeouts.
- Independent controls pass with Ion disabled or --cache-ir-stubs=off. Adding an ordinary non-configurable, non-writable target property restores validation; adding a configurable property does not.
- Exact evaluator commands, captured logs, and control testcases are preserved in /app/output/verification-proxy-readonly-length/evidence.json.
Security Impact
- Severity: Low
- Attacker capability: Obtain an incorrect Proxy result that violates an immutable property's invariant. The demonstrated capability is limited to JavaScript semantics; no memory corruption, disclosure, or privilege escalation was established.
- Preconditions: The property read must reach Ion's optimized scripted Proxy getter. The target array has non-writable length and lacks another property that sets the validation flag. Reproduction required no source patches, browser preferences, external services, or shell-only helpers.
ASAN Report
No sanitizer crash occurred; this finding is js_differential. Both required comparisons reported differs: true, with interpreter exit 0 and Ion exit 3. Ion stderr reported: Error: Missing TypeError for immutable array length. A diagnostic control confirmed returned value 17 with target length descriptor {"value":0,"writable":false,"enumerable":false,"configurable":false}.
| Reporter | ||
Comment 1•1 month ago
|
||
| Reporter | ||
Comment 2•1 month ago
|
||
| Reporter | ||
Comment 3•1 month ago
|
||
Comment 4•1 month ago
|
||
This bug was filed automatically by an AI-powered security testing tool. The analysis, testcase, and any suggested patches were generated by an AI model and have NOT been verified by a human.
Please review all details carefully before triaging. The issue was confirmed by an independent verifier, but for non-ASAN issues, the verifier has been made less strict. Additionally, the root cause analysis and suggested fix may contain errors.
Updated•1 month ago
|
Updated•1 month ago
|
| Assignee | ||
Comment 5•1 month ago
|
||
This isn't security-sensitive, but sure, the patch looks correct.
Updated•1 month ago
|
| Assignee | ||
Comment 6•1 month ago
|
||
Updated•1 month ago
|
Updated•1 month ago
|
Updated•1 month ago
|
Description
•