| // run |
| |
| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "runtime" |
| "unsafe" |
| ) |
| |
| // 1 of these is 28 bytes. |
| // When allocating 1 of them in a 32-byte size class, |
| // we accidentally write 2 pointer bitmasks, which |
| // marks the first 6 fields of the following object |
| // as pointers. |
| type E struct { |
| a [7]*byte |
| } |
| |
| // 32 bytes, and has pointers. |
| // First field is not a pointer, but will contain |
| // a badPtr value. |
| type Victim struct { |
| a uintptr |
| b [6]int32 |
| c *byte |
| } |
| |
| //go:noinline |
| func f(n int) []E { |
| var r []E |
| for range n { |
| r = append(r, E{}) |
| } |
| return r |
| } |
| |
| //go:noinline |
| func newVictim() *Victim { |
| return &Victim{a: badPtr} |
| } |
| |
| var badPtr uintptr |
| |
| func init() { |
| x := make([]byte, 1<<18-8) |
| sink = &x[0] |
| badPtr = uintptr(unsafe.Pointer(&x[len(x)-1])) + 5 |
| } |
| |
| func main() { |
| fs := make([]*Victim, 1000) |
| |
| // Allocate a bunch of Victims. |
| for i := range fs { |
| fs[i] = newVictim() |
| } |
| // Deallocate every other one. |
| for i := range fs { |
| if i%2 == 1 { |
| fs[i] = nil |
| } |
| } |
| runtime.GC() |
| |
| // Allocate Es in the deallocated slots. |
| // Those allocations will incorrectly set the |
| // pointer bit for the first field of all the |
| // Victims we allocated. |
| for range len(fs) / 2 { |
| _ = f(1) |
| } |
| runtime.GC() |
| |
| // Keep fs alive. |
| sink = &fs[0] |
| } |
| |
| var sink any |