Skip to content

Buffer: fix use-after-free on detach during argument coercion - #1106

Open
basavaraj-sm05 wants to merge 1 commit into
nginx:masterfrom
basavaraj-sm05:qjs-buffer-detach-uaf
Open

Buffer: fix use-after-free on detach during argument coercion#1106
basavaraj-sm05 wants to merge 1 commit into
nginx:masterfrom
basavaraj-sm05:qjs-buffer-detach-uaf

Conversation

@basavaraj-sm05

Copy link
Copy Markdown

Proposed changes

Several Buffer.prototype methods, in both engines, fetch the underlying ArrayBuffer's raw data pointer before finishing the coercion of their own offset/length/value arguments. In QuickJS (qjs_buffer.c) this affects the read/write integer and float accessors, write(), copy(), compare(), indexOf(), and toString(); in njs (njs_buffer.c) it affects the shared range helper behind compare()/equals() and the source side of copy(). Any of those arguments can be an object with a valueOf, and the callback it triggers can detach the very buffer whose pointer was already captured, via ArrayBuffer.prototype.transfer() or, in a test context, $262.detachArrayBuffer(). The subsequent bounds check and memory access then run against that stale pointer: a straightforward heap-use-after-free in QuickJS, since transfer() frees the backing store, and a read through a NULL-based address in njs, since its own detach only clears the data pointer. Both are reachable from ordinary script, for example buf.compare(other, {valueOf(){other.buffer.transfer(); return 0}}). I found the QuickJS side first, and since njs's own read/write-int functions already re-fetch the buffer after coercion, went looking for the same gap in njs's remaining Buffer methods and found it in the compare/copy range helper too. The fix in both files is the same: resolve every user-controllable argument first, and only look up the buffer pointer immediately before the bounds check and the actual read or write, so a detach mid-conversion now surfaces as the ordinary "detached buffer" error instead of touching freed or invalid memory. I added a regression suite to buffer.t.js that detaches the buffer from inside a valueOf during each affected call and asserts a TypeError is thrown.

Checklist

Before creating a PR, run through this checklist and mark each as complete:

  • I have read the CONTRIBUTING document
  • If applicable, I have added tests that prove my fix is effective or that my feature works
  • If applicable, I have checked that any relevant tests pass after adding my changes

Several Buffer.prototype methods fetched the underlying ArrayBuffer's
data pointer before finishing the coercion of their own offset,
length, or value arguments.  In QuickJS (qjs_buffer.c) this affected
the read/write integer and float accessors, write(), copy(),
compare(), indexOf(), and toString(); in njs (njs_buffer.c) it
affected the shared range helper behind compare()/equals() and the
source side of copy().

Any of those arguments can be an object with a valueOf, and the
callback it triggers can detach the very buffer whose pointer was
already captured (via ArrayBuffer.prototype.transfer() or
$262.detachArrayBuffer()).  The following bounds check and memory
access then ran against that stale pointer: a heap-use-after-free in
QuickJS, since transfer() frees the backing store, and a read through
a NULL-based address in njs, since its detach only clears the data
pointer.  Both are reachable from ordinary script, e.g.
buf.compare(other, {valueOf(){other.buffer.transfer(); return 0}}).

The fix resolves every user-controllable argument first and only
looks up the buffer pointer immediately before the bounds check and
the actual read or write, so a detach mid-conversion now surfaces as
the ordinary "detached buffer" error.  Added a regression suite that
detaches the buffer from a valueOf during each affected call.
@github-actions

Copy link
Copy Markdown

🎉 Thank you for your contribution! It appears you have not yet signed the F5 Contributor License Agreement (CLA), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the F5 CLA and reply on a new comment with the following text to agree:


I have hereby read the F5 CLA and agree to its terms


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@vinaykumar-1591 vinaykumar-1591 moved this from New to External Pull Requests in NGINX OSS Unified Workspace Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: External Pull Requests

Development

Successfully merging this pull request may close these issues.

2 participants