Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

launderpkg.gno

5.56 Kb · 151 lines
  1// Package launderpkg defines a struct type that the laundervictim
  2// realm uses as the type of its package-level state. The Set method
  3// is the "innocent /p/-helper" that an attacker tries to weaponize
  4// via the receiver-borrow rule (PushFrameCall borrow rule 2):
  5// when the receiver is owned by /r/X, calling Set borrows m.Realm to
  6// /r/X, and the write inside Set runs with /r/X authority.
  7package launderpkg
  8
  9type Object struct {
 10	Field string
 11}
 12
 13// PInitData is a /p/-init-allocated package-level data var. Its
 14// StructValue carries ObjectInfo.PkgID = /p/demo/tests/launderpkg.
 15// Used by zrealm_launder_pdata_* filetests to probe the /p/-source
 16// read patterns that the existing 62 /r/-source launder filetests
 17// don't cover. Empirically, direct value-read and pointer-deref of
 18// PInitData from a /r/-caller both panic readonly tainted — the
 19// /p/-source bytes are not adoptable into /r/-authority via these
 20// patterns.
 21var PInitData = Object{Field: "p-init"}
 22
 23// Set is the canonical "/p/ helper that mutates /r/-owned state"
 24// pattern. Looks benign — like list.Set, avl.Set, etc. — but if a
 25// foreign caller obtains a pointer to a victim realm's Object, they
 26// can invoke Set on it and the borrow rule grants them victim
 27// authority for the call. Exposing a *Object out of a realm is
 28// equivalent to consenting to mutation by any caller that holds the
 29// pointer.
 30func (o *Object) Set(s string) {
 31	o.Field = s
 32}
 33
 34// Read is a read-only accessor.
 35func (o *Object) Read() string {
 36	return o.Field
 37}
 38
 39// Mutator is an interface — callers can pass any implementation. A
 40// realistic pattern: /p/orig defines a hook interface and exposes a
 41// method that lets callers register an impl for some operation.
 42type Mutator interface {
 43	Run(*Immutable)
 44}
 45
 46// UseMutator is a /p/-method on *Object that dispatches an interface
 47// method (Mutator.Run) with target as the argument. This is the
 48// realistic shape — a /p/-library invoking a user-supplied hook on
 49// /r/-owned data.
 50func (o *Object) UseMutator(target *Immutable, m Mutator) {
 51	m.Run(target)
 52}
 53
 54// SliceMutator's Run takes a SLICE of pointers — exercises the
 55// "non-pointer parameter that still conveys writable foreign data"
 56// shape that Attack K explores.
 57type SliceMutator interface {
 58	Run([]*Immutable)
 59}
 60
 61// UseSliceMutator dispatches a SliceMutator on a single-element
 62// slice carrying target.
 63func (o *Object) UseSliceMutator(target *Immutable, m SliceMutator) {
 64	m.Run([]*Immutable{target})
 65}
 66
 67// AnyMutator's Run takes `any` — interface-typed parameter. The
 68// caller can box a *Immutable into the interface and an attacker
 69// impl can type-assert back to write. Tests whether the predicate
 70// needs to treat interface-typed params as potentially foreign.
 71type AnyMutator interface {
 72	Run(any)
 73}
 74
 75// UseAnyMutator boxes target into `any` before dispatching.
 76func (o *Object) UseAnyMutator(target *Immutable, m AnyMutator) {
 77	m.Run(target)
 78}
 79
 80// Bare is a /p/-declared struct type with NO methods. Used to test
 81// whether embedding/fielding a methods-less /p/-type inside an
 82// /r/-declared container exposes any laundering vector through
 83// DIRECT field writes (no method dispatch, no Apply callback). The
 84// expectation: readonly taint on the /r/-container propagates to
 85// inner /p/-typed fields and direct writes panic.
 86type Bare struct {
 87	Field string
 88}
 89
 90// Immutable is a deliberately read-only /p/ type: same struct layout
 91// as Object, but no mutator method. A realm using Immutable as the
 92// type of an exposed field intends "read-only API." The launder game
 93// variation explores whether an attacker /p/ package can convert
 94// *Immutable to a type with a mutator method declared elsewhere.
 95type Immutable struct {
 96	Field string
 97}
 98
 99// Read is a read-only accessor.
100func (i *Immutable) Read() string {
101	return i.Field
102}
103
104// Apply is a higher-order helper that hands the *Immutable to a
105// caller-supplied callback. The signature looks read-only (no
106// mutator method on *Immutable itself), but Apply hands out an
107// addressable pointer to victim-owned memory while m.Realm is
108// borrowed to the victim. A /p/-declared callback substituted by
109// the caller therefore runs with victim authority. This is the
110// avl.Tree.Iterate / list.ForEach shape — common in /p/ libraries.
111func (i *Immutable) Apply(fn func(*Immutable)) {
112	fn(i)
113}
114
115// BumpToPwn is a no-arg, no-return /p/-method that mutates the
116// receiver. Used by stored-bound-method-value laundering probes:
117// `mv := victimImmPtr.BumpToPwn` has type `func()`, which fits
118// PlainHook = func(). When the bound method value is stored and
119// invoked later from /r/-victim context, recv-borrow borrow rule #2 fires on
120// the /r/-victim-stamped recv → m.Realm = /r/-victim → write
121// commits.
122func (i *Immutable) BumpToPwn() {
123	i.Field = "pwnd-via-bound-mv"
124}
125
126// DeferApply is the defer-variant of Apply: schedules fn(i) as a
127// defer instead of calling synchronously. Used to probe whether
128// the borrow rules apply the same when a callback is invoked from
129// inside a /p/-function's defer queue.
130func (i *Immutable) DeferApply(fn func(*Immutable)) {
131	defer fn(i)
132}
133
134// PanicAfterApply: invokes fn(i) synchronously, then panics after
135// return. If fn ran without panicking (write succeeded), the panic
136// here is /p/-realm panic propagating up.
137func (i *Immutable) PanicAfterApply(fn func(*Immutable)) {
138	fn(i)
139	panic("post-apply panic")
140}
141
142// RecoverApply: defers a recover(), then calls fn(i). If fn panics
143// with readonly, recover catches it (within the same /p/-pkg
144// frame). Returns the recovered value.
145func (i *Immutable) RecoverApply(fn func(*Immutable)) (rec any) {
146	defer func() {
147		rec = recover()
148	}()
149	fn(i)
150	return
151}