Files
zeekay 9cc8cc7bb7 contract: one cursor holds every calldata bound, one verdict cannot be faked
A precompile's input is the two-index slice Memory.GetPtr returns, and
nothing on the path to Run copies it. len is what the caller declared and
paid for; cap is the rest of EVM memory, which that same caller filled with
MSTORE. So a parser reaching past its input does not panic and does not halt
a validator -- it reads bytes the attacker chose and answers over them.
Measured, not assumed: TestCalldataCarriesCallerChosenSpareCapacity drives
the VM and reads back len=8 cap=256 with 0xA5 past the end.

Cursor bounds every read against len, never against cap and never by letting
a slice expression refuse, and caps what it returns so a field cannot be
re-sliced forward. Verdict carries the bit (bool, error) was missing -- did a
verifier run -- so "nothing checked this" stops borrowing the spelling of
"this failed", and its zero value denies. Poisoned is the one definition of
the adversarial fixture: append-built input has spare capacity too, but
zeroed, so an over-read reads as a harmless run of zeros.

p3q parses through the cursor. Its six hand-written bounds become zero; the
class now rests on the single check in Cursor.Bytes, and deleting that check
fails both suites -- including with fixtures degraded back to plain append,
which is what the class used to depend on.
2026-08-28 11:47:04 -07:00

33 lines
1.4 KiB
Go

// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package contract
// Poisoned returns b with spare capacity behind it, filled with a
// recognisable pattern. It is the one definition of what a precompile's input
// actually looks like, and every refusal test in this repository submits one.
//
// The EVM hands a precompile the two-index slice Memory.GetPtr returns
// (instructions.go opCall and its three siblings), and nothing on the path to
// Run copies it. So len is the size the caller declared and paid gas for,
// while cap is the rest of EVM memory — memory that same caller filled with
// MSTORE before making the call.
//
// A fixture built with append also carries spare capacity, but zeroed or
// stale. An over-read into it looks like a harmless run of zeros, and a test
// over such a fixture passes whether or not the bound it means to exercise is
// present. Chosen bytes are what make an over-read visible as an over-read,
// and what make the test's subject the parser rather than the allocator.
//
// It lives in non-test source because nine packages' tests need the same
// definition, and two definitions of the adversary is one too many. Nothing
// in a Run path calls it.
func Poisoned(b []byte, spare int) []byte {
full := make([]byte, len(b)+spare)
for i := range full {
full[i] = 0xA5
}
copy(full, b)
return full[:len(b)]
}