Skip to content

Add regression tests for vuln-88 and harden checkSignal() - #6951

Merged
erikcorry merged 1 commit into
mainfrom
ecorry/reland-vuln-88
Aug 11, 2026
Merged

Add regression tests for vuln-88 and harden checkSignal()#6951
erikcorry merged 1 commit into
mainfrom
ecorry/reland-vuln-88

Conversation

@erikcorry

Copy link
Copy Markdown
Contributor

vuln-88 itself is already fixed: PipeLocked::releaseSource() nulls source before releasing and is idempotent, so doError()'s second release is a no-op. These tests pin that behaviour down.

The deterministic test re-pipes the readable from the destination's abort listener, so a stale source reference would tear down an unrelated pipe; it asserts rs.locked instead of relying on a crash, because PipeLocked lives inline in a kj::OneOf and is never heap freed, so ASAN cannot see the corruption. The second test is the raw crash repro: it re-locks the readable with a reader, leaving a non-polymorphic ReaderLocked where the stale reference points.

checkSignal() must not touch *this after releaseSource(), which can run the source's cancel algorithm and let user JS destroy it. That was upheld by copying three flags into locals. Copy the Flags struct once instead, and move the tail into a static finishAbortedPipe() so the compiler enforces the rule rather than a comment.

vuln-88 itself is already fixed: PipeLocked::releaseSource() nulls
`source` before releasing and is idempotent, so doError()'s second
release is a no-op. These tests pin that behaviour down.

The deterministic test re-pipes the readable from the destination's
abort listener, so a stale source reference would tear down an
unrelated pipe; it asserts rs.locked instead of relying on a crash,
because PipeLocked lives inline in a kj::OneOf and is never heap
freed, so ASAN cannot see the corruption. The second test is the
raw crash repro: it re-locks the readable with a reader, leaving a
non-polymorphic ReaderLocked where the stale reference points.

checkSignal() must not touch *this after releaseSource(), which can
run the source's cancel algorithm and let user JS destroy it. That
was upheld by copying three flags into locals. Copy the Flags struct
once instead, and move the tail into a static finishAbortedPipe() so
the compiler enforces the rule rather than a comment.
@erikcorry
erikcorry requested a review from jasnell August 10, 2026 15:44
@erikcorry
erikcorry requested review from a team as code owners August 10, 2026 15:44
@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Everything looks correct. abort takes jsg::Optional<jsg::JsValue>, and finishAbortedPipe passes reason (a jsg::JsValue) which will implicitly convert.

Let me now summarize my review findings. The C++ changes are sound:

  1. The Flags struct is small (4 bit-fields) and trivially copyable — auto flagsCopy = flags is correct and sufficient.
  2. Extracting finishAbortedPipe as a static method is a clean way to enforce no this access after releaseSource().
  3. The self reference remains valid because the controller is ref-counted and held alive by callers.
  4. The reason (jsg::JsValue) is a wrapper around v8::Local<v8::Value>, which is valid for the duration of the handle scope. In finishAbortedPipe, the .then() lambda correctly uses reason.addRef(js) to persist it.

The tests are well-structured:

  1. First test deterministically observes the stale reference without relying on ASAN.
  2. Second test reproduces the actual crash scenario.
  3. Both test names are descriptive.
  4. The .wd-test config matches the pattern of other autovuln tests.

No issues found.

LGTM

github run

@jasnell jasnell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM but let's make sure it doesn't conflict with #6949

@erikcorry
erikcorry merged commit 57e8bfc into main Aug 11, 2026
25 checks passed
@erikcorry
erikcorry deleted the ecorry/reland-vuln-88 branch August 11, 2026 08:15
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.

2 participants