52 lines
1.5 KiB
Plaintext
52 lines
1.5 KiB
Plaintext
// issue-0011: Assigning to List(T).items corrupts adjacent memory when T is large
|
|
//
|
|
// Writing `list.items = xx 0` overwrites memory beyond the 8-byte items pointer
|
|
// when the List's element type T is larger than 32 bytes. The corruption spills
|
|
// into the struct field that follows the List in memory.
|
|
//
|
|
// Works correctly when size_of(T) <= 32.
|
|
// Fails when size_of(T) > 32 (e.g., 40 bytes).
|
|
//
|
|
// Likely cause: codegen confuses size_of(T) with size_of([*]T) when generating
|
|
// the store instruction for the items field.
|
|
|
|
#import "modules/std.sx";
|
|
|
|
// 40-byte struct — triggers the bug.
|
|
// Shrink to [4]s64 (32 bytes) and the bug goes away.
|
|
BigNode :: struct {
|
|
data: [5]s64; // 40 bytes
|
|
}
|
|
|
|
Tree :: struct {
|
|
nodes: List(BigNode); // items(8) + len(8) + cap(8) = 24 bytes
|
|
generation: s64; // 8 bytes — total 32 bytes
|
|
}
|
|
|
|
Container :: struct {
|
|
tree: Tree;
|
|
sentinel: s64;
|
|
|
|
do_work :: (self: *Container) {
|
|
self.tree.nodes.items = xx 0; // BUG: corrupts self.sentinel
|
|
}
|
|
}
|
|
|
|
main :: () -> void {
|
|
obj : *Container = xx context.allocator.alloc(size_of(Container));
|
|
memset(obj, 0, size_of(Container));
|
|
obj.sentinel = 0xDEADBEEF;
|
|
|
|
print("size_of BigNode = {}\n", size_of(BigNode));
|
|
print("before: sentinel = {}\n", obj.sentinel);
|
|
|
|
obj.do_work();
|
|
|
|
print("after: sentinel = {}\n", obj.sentinel);
|
|
if obj.sentinel != 0xDEADBEEF {
|
|
print("BUG: sentinel was corrupted!\n");
|
|
} else {
|
|
out("OK\n");
|
|
}
|
|
}
|