Skip to content

feat(#563): seed an unplaced access's secrecy per access - #565

Merged
octalide merged 3 commits into
devfrom
feat/563
Oct 31, 2026
Merged

octalide merged 3 commits into
devfrom
feat/563

Conversation

@octalide

@octalide octalide commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Closes #563

What

The constant-time walk takes the producer's statement of what an unplaced memory access reads, in place of may-alias with every held extent.

  • walk.Seed.access: walk.Access is the new seed. Access{at, access, secret} names the instruction offset and the access, with whether what it reads may hold a secret. walk.Accessor names the access as operand{u8} on register machines (the operand the footprint states the access through, or the memory operand) or as index{u32}, its position in fp.memory, for an access no operand names, as on wasm.
  • Each touch of the instruction walked takes the seeds naming it unless the walk places it off a symbol or in the frame (off the stack pointer's value on entry). A placed access keeps the extent rule. An unseeded unplaced access keeps the conservative rule.
  • Every read of a touch's secrecy (the instruction's join, latency, control) goes through one reads_secret, which answers the seed for a seeded touch. So a load's written register, a wasm load's pushed value, and the old value an atomic rmw or cmpxchg returns take the seed's secrecy, joined with the rest of what the instruction reads.
  • Stores follow the seed (see Decisions). A secret stored through a seeded-public access is refused with the new reason ct.STORED.
  • An access seed at or past the code's end is UNSEEDABLE. One within an instruction, or naming an access its instruction does not make, is the new UNACCESSED.
  • masc walk gains --access <offset>:<n>=secret|public (operand n) and --access <offset>:@<n>=secret|public (footprint position n). Offsets read decimal or 0x hex.

Decisions

  • Stores follow the seed. A store through a seeded unplaced access whose seed is public, by an instruction whose stored value may hold a secret (the walk's usual join of what the instruction reads), is refused with ct.STORED ("secret value stored to memory stated public"). This checks the producer's claim where it could be broken, so a public seed never hides a secret the walk saw stored. A secret stored through a seeded-secret access is placed as any other store is today (held, or everywhere when unlocated), because unseeded and placed accesses still read through the extents. A public store does nothing, as today, so a public-seeded access never marks memory everywhere.
  • "Unplaced" means placed off no symbol and in no frame slot. An access off a register's version, such as a pointer parameter, counts as unplaced even though the walk names it by that register. That is the issue's main case. A producer that seeds both Seed.memory off a register and an access off it gets the access seed for that access.
  • Atomic results join. The register an atomic writes takes the seed's secrecy joined with its other inputs (for example amoadd's rs2), not the seed's alone. That is conservative and needs no knowledge of which written register holds the old value.
  • Seeds naming one access agree by join. Several seeds on one access: it is secret when any says secret.
  • Unreached access seeds are not refused. Unlike frame seeds, a seed whose instruction no path reaches holds nowhere silently. The producer may seed every access it emitted, dead code included.
  • ct.STORED is a new ct.Reason (5, REASONS = 6). It is additive. mirl reports refusals through ct.message/ct.spell, which word it.

Consumer changes

mirl builds, per unplaced access whose secrecy its types state:

walk.Seed.access{walk.Access{at: <instruction offset>, access: walk.Accessor.operand{<operand position>}, secret: <bool>}}

and on wasm, where the address is popped:

walk.Seed.access{walk.Access{at: <instruction offset>, access: walk.Accessor.index{<index in footprint.Footprint.memory>}, secret: <bool>}}

Notes

  • Checks were host-only: machd check . -a '*' and machd fmt .. No artifact builds, no gen, no new unit tests and no tool comparison, per the v7 rules.
  • Gap: an atomic's written register is the join, not the seed alone (see Decisions). Narrowing it would need the footprint to say which written register holds the value read.

…ess seed

walk.Seed.access names an instruction offset and an access, by operand or
by its position in the footprint, with whether what it reads is secret.
an access the walk places off no symbol and in no frame slot takes it in
place of may-alias with every held extent, for loads, pushed values and
atomic results alike, and a secret stored where it states none is refused.
@octalide
octalide marked this pull request as ready for review October 31, 2026 23:23
@octalide
octalide added this pull request to the merge queue Oct 31, 2026
Merged via the queue into dev with commit c32ab37 Oct 31, 2026
2 checks passed
@octalide
octalide deleted the feat/563 branch October 31, 2026 23:51
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.

walk: an unplaced access takes the producer's per-access secrecy as a seed, not may-alias with every seeded extent

1 participant