package winpwn import "testing" // TestSprayAndFindSeededTarget exercises the examples/heap_lfh shape: a // single pre-seeded target (the freed victim's leaked address), spraying // replacements until one happens to equal it. func TestSprayAndFindSeededTarget(t *testing.T) { const victimAddr = uint64(0xdead0000) replacements := []uint64{0x1111, 0x2222, victimAddr, 0x3333} older, newer, attempts, ok, err := SprayAndFind( []SprayResult[uint64]{{ID: -1, Key: victimAddr}}, len(replacements), func(attempt int) (SprayResult[uint64], error) { return SprayResult[uint64]{ID: attempt, Key: replacements[attempt-1]}, nil }, func(a, b uint64) bool { return a == b }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } if !ok { t.Fatal("expected a match") } if attempts != 3 { t.Errorf("attempts = %d, want 3 (the index where victimAddr reappears)", attempts) } if older.ID != -1 || older.Key != victimAddr { t.Errorf("older = %+v, want the seeded victim", older) } if newer.Key != victimAddr { t.Errorf("newer.Key = 0x%x, want 0x%x", newer.Key, victimAddr) } } // TestSprayAndFindAdjacentPair exercises the examples/heap_segment shape: no // seed, searching every sprayed sample against every other for a relation // (here, "exactly 32 apart"). func TestSprayAndFindAdjacentPair(t *testing.T) { const profileSize = 32 addrs := []uint64{0x1000, 0x1080, 0x1300, 0x1300 + profileSize} // last two are 32 apart older, newer, _, ok, err := SprayAndFind( nil, len(addrs), func(attempt int) (SprayResult[uint64], error) { return SprayResult[uint64]{ID: attempt, Key: addrs[attempt-1]}, nil }, func(a, b uint64) bool { d := int64(b) - int64(a) return d == profileSize || d == -profileSize }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } if !ok { t.Fatal("expected an adjacent pair to be found") } if older.ID != 3 || newer.ID != 4 { t.Errorf("got pair ids (%d, %d), want (3, 4)", older.ID, newer.ID) } } func TestSprayAndFindExhaustsAttempts(t *testing.T) { _, _, attempts, ok, err := SprayAndFind( nil, 5, func(attempt int) (SprayResult[uint64], error) { return SprayResult[uint64]{ID: attempt, Key: uint64(attempt)}, nil }, func(a, b uint64) bool { return false }, // never matches ) if err != nil { t.Fatalf("unexpected error: %v", err) } if ok { t.Fatal("expected no match") } if attempts != 5 { t.Errorf("attempts = %d, want 5 (maxAttempts exhausted)", attempts) } } func TestSprayAndFindPropagatesSprayError(t *testing.T) { wantErr := errSprayTest _, _, attempts, ok, err := SprayAndFind( nil, 5, func(attempt int) (SprayResult[uint64], error) { if attempt == 2 { return SprayResult[uint64]{}, wantErr } return SprayResult[uint64]{ID: attempt, Key: uint64(attempt)}, nil }, func(a, b uint64) bool { return false }, ) if err != wantErr { t.Fatalf("err = %v, want %v", err, wantErr) } if ok { t.Fatal("ok should be false on a spray error") } if attempts != 2 { t.Errorf("attempts = %d, want 2 (the attempt that errored)", attempts) } } var errSprayTest = errSprayTestSentinel{} type errSprayTestSentinel struct{} func (errSprayTestSentinel) Error() string { return "spray error" }