Skip to content

fix[venom]: port augassign read/write overlap guard#5099

Open
banteg wants to merge 2 commits into
vyperlang:masterfrom
banteg:fix/venom-augassign-overlap
Open

fix[venom]: port augassign read/write overlap guard#5099
banteg wants to merge 2 commits into
vyperlang:masterfrom
banteg:fix/venom-augassign-overlap

Conversation

@banteg

@banteg banteg commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What I did

Fixes #5051.

The Venom pipeline's lower_AugAssign was missing the read/write overlap guard from GHSA-4w26-8p97-f4jp: the target pointer is computed (and bounds-checked) before the RHS executes, so programs like self.a[1] += self.a.pop() compiled under --experimental-codegen and could write out of bounds after the array shrank. Legacy rejects this class at codegen time (parse_AugAssign, added in #4487, relaxed in #4497).

How I did it

Legacy's check reads legacy-IRnode properties (referenced_variables, variable_writes, contains_writeable_call), which don't exist in the AST-based venom lowering — but each has a faithful AST-level equivalent seeded from the same frontend analysis legacy itself seeds from:

  • referenced_variables → recursive collection of _expr_info._reads over the reduced target AST (the frontend folds internal callees' state reads into call nodes);
  • variable_writesget_expr_writes(rhs) (the exact function legacy uses to seed IRnodes; internal-call writes are tracked transitively by the frontend);
  • contains_writeable_callextcall nodes, non-static raw_call (via get_mutability_at_call_site, so is_static_call=True stays allowed like legacy), send, and create builtins, recursing transitively through internal callees via reachable_internal_functions.

Same prim-word-target relaxation, same CodegenPanic("unreachable").

How to verify it

The GHSA source lists in test_assignment.py are extracted into module constants and exercised under Settings(experimental_codegen=True): 6 rejection cases (all fail on master — they compile when they shouldn't) plus legacy-accepted overlap-OK sources still compiling. A 12-case differential confirmed venom now matches legacy exactly (6 rejected / 6 accepted), including a state-writing extcall hidden behind a transitive internal call. tests/unit/compiler/ plus a 2094-test venom functional sweep pass with no over-rejection.

Commit message

the venom pipeline's augassign lowering was missing the read/write
overlap guard from GHSA-4w26-8p97-f4jp: the target pointer is
computed and bounds-checked before the RHS is evaluated, so e.g.
`self.a[1] += self.a.pop()` compiled and could write out of bounds
after the array shrank. legacy rejects this class at codegen time.

port the legacy guard using AST-level equivalents of the legacy
IRnode properties, seeded from the same frontend analysis legacy
seeds from: target reads via `_expr_info._reads`, rhs writes via
`get_expr_writes`, and writeable-call detection covering extcall,
non-static raw_call, send and create builtins, recursing transitively
through internal callees. the same source programs are rejected with
the same exception, and everything legacy accepts still compiles.

Description for the changelog

Fix: experimental codegen rejects augmented assignments whose RHS can mutate the assignment target (GHSA-4w26-8p97-f4jp parity).

Cute Animal Picture

cute animal

banteg added 2 commits June 12, 2026 16:00
legacy codegen rejects augmented assignments whose RHS can mutate the
assignment target (GHSA-4w26-8p97-f4jp), since the target pointer is
computed and bounds-checked before the RHS is evaluated. the venom
pipeline was missing this guard, so e.g. `self.a[1] += self.a.pop()`
would compile and write out-of-bounds at runtime.

port the guard from vyper/codegen/stmt.py:parse_AugAssign to
lower_AugAssign using AST-level equivalents of the legacy IRnode
properties (referenced_variables, variable_writes,
contains_writeable_call), so the same source programs are rejected with
the same CodegenPanic.
@charles-cooper

Copy link
Copy Markdown
Member

i think for venom we should actually just do the right thing rather than rejecting the program. i'd be ok with this as an interim fix tho

@banteg

banteg commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Looked at this alongside #5100. I left this PR as the interim augassign guard for now: unlike rvalue subscript lowering, augassign writes back after RHS evaluation, so making it non-rejecting needs a broader write-semantics decision rather than the stale-base-pointer fix. #5100 now implements the narrower stale-base handling requested there.

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.

Venom: augmented assignment missing read/write overlap guard

2 participants