Skip to content

Commit fd6200e

Browse files
authored
Merge pull request #15431 from kcbanner/fix_decl_value_arena
sema: Rework Decl.value_arena to fix another memory corruption issue
2 parents 011bc59 + 15dafd1 commit fd6200e

File tree

3 files changed

+98
-34
lines changed

3 files changed

+98
-34
lines changed

src/Module.zig

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,46 @@ pub const WipCaptureScope = struct {
411411
}
412412
};
413413

414+
const ValueArena = struct {
415+
state: std.heap.ArenaAllocator.State,
416+
state_acquired: ?*std.heap.ArenaAllocator.State = null,
417+
418+
/// If this ValueArena replaced an existing one during re-analysis, this is the previous instance
419+
prev: ?*ValueArena = null,
420+
421+
/// Returns an allocator backed by either promoting `state`, or by the existing ArenaAllocator
422+
/// that has already promoted `state`. `out_arena_allocator` provides storage for the initial promotion,
423+
/// and must live until the matching call to release().
424+
pub fn acquire(self: *ValueArena, child_allocator: Allocator, out_arena_allocator: *std.heap.ArenaAllocator) Allocator {
425+
if (self.state_acquired) |state_acquired| {
426+
return @fieldParentPtr(std.heap.ArenaAllocator, "state", state_acquired).allocator();
427+
}
428+
429+
out_arena_allocator.* = self.state.promote(child_allocator);
430+
self.state_acquired = &out_arena_allocator.state;
431+
return out_arena_allocator.allocator();
432+
}
433+
434+
/// Releases the allocator acquired by `acquire. `arena_allocator` must match the one passed to `acquire`.
435+
pub fn release(self: *ValueArena, arena_allocator: *std.heap.ArenaAllocator) void {
436+
if (@fieldParentPtr(std.heap.ArenaAllocator, "state", self.state_acquired.?) == arena_allocator) {
437+
self.state = self.state_acquired.?.*;
438+
self.state_acquired = null;
439+
}
440+
}
441+
442+
pub fn deinit(self: ValueArena, child_allocator: Allocator) void {
443+
assert(self.state_acquired == null);
444+
445+
const prev = self.prev;
446+
self.state.promote(child_allocator).deinit();
447+
448+
if (prev) |p| {
449+
p.deinit(child_allocator);
450+
}
451+
}
452+
};
453+
414454
pub const Decl = struct {
415455
/// Allocated with Module's allocator; outlives the ZIR code.
416456
name: [*:0]const u8,
@@ -429,7 +469,7 @@ pub const Decl = struct {
429469
@"addrspace": std.builtin.AddressSpace,
430470
/// The memory for ty, val, align, linksection, and captures.
431471
/// If this is `null` then there is no memory management needed.
432-
value_arena: ?*std.heap.ArenaAllocator.State = null,
472+
value_arena: ?*ValueArena = null,
433473
/// The direct parent namespace of the Decl.
434474
/// Reference to externally owned memory.
435475
/// In the case of the Decl corresponding to a file, this is
@@ -607,15 +647,15 @@ pub const Decl = struct {
607647
variable.deinit(gpa);
608648
gpa.destroy(variable);
609649
}
610-
if (decl.value_arena) |arena_state| {
650+
if (decl.value_arena) |value_arena| {
611651
if (decl.owns_tv) {
612652
if (decl.val.castTag(.str_lit)) |str_lit| {
613653
mod.string_literal_table.getPtrContext(str_lit.data, .{
614654
.bytes = &mod.string_literal_bytes,
615655
}).?.* = .none;
616656
}
617657
}
618-
arena_state.promote(gpa).deinit();
658+
value_arena.deinit(gpa);
619659
decl.value_arena = null;
620660
decl.has_tv = false;
621661
decl.owns_tv = false;
@@ -624,9 +664,9 @@ pub const Decl = struct {
624664

625665
pub fn finalizeNewArena(decl: *Decl, arena: *std.heap.ArenaAllocator) !void {
626666
assert(decl.value_arena == null);
627-
const arena_state = try arena.allocator().create(std.heap.ArenaAllocator.State);
628-
arena_state.* = arena.state;
629-
decl.value_arena = arena_state;
667+
const value_arena = try arena.allocator().create(ValueArena);
668+
value_arena.* = .{ .state = arena.state };
669+
decl.value_arena = value_arena;
630670
}
631671

632672
/// This name is relative to the containing namespace of the decl.
@@ -4537,15 +4577,20 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45374577
// We need the memory for the Type to go into the arena for the Decl
45384578
var decl_arena = std.heap.ArenaAllocator.init(gpa);
45394579
const decl_arena_allocator = decl_arena.allocator();
4540-
4541-
const decl_arena_state = blk: {
4580+
const decl_value_arena = blk: {
45424581
errdefer decl_arena.deinit();
4543-
const s = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
4582+
const s = try decl_arena_allocator.create(ValueArena);
4583+
s.* = .{ .state = undefined };
45444584
break :blk s;
45454585
};
45464586
defer {
4547-
decl_arena_state.* = decl_arena.state;
4548-
decl.value_arena = decl_arena_state;
4587+
if (decl.value_arena) |value_arena| {
4588+
assert(value_arena.state_acquired == null);
4589+
decl_value_arena.prev = value_arena;
4590+
}
4591+
4592+
decl_value_arena.state = decl_arena.state;
4593+
decl.value_arena = decl_value_arena;
45494594
}
45504595

45514596
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -5493,9 +5538,9 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
54935538
const decl = mod.declPtr(decl_index);
54945539

54955540
// Use the Decl's arena for captured values.
5496-
var decl_arena = decl.value_arena.?.promote(gpa);
5497-
defer decl.value_arena.?.* = decl_arena.state;
5498-
const decl_arena_allocator = decl_arena.allocator();
5541+
var decl_arena: std.heap.ArenaAllocator = undefined;
5542+
const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena);
5543+
defer decl.value_arena.?.release(&decl_arena);
54995544

55005545
var sema: Sema = .{
55015546
.mod = mod,

src/Sema.zig

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2856,9 +2856,9 @@ fn zirEnumDecl(
28562856
const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index);
28572857
done = true;
28582858

2859-
var decl_arena = new_decl.value_arena.?.promote(gpa);
2860-
defer new_decl.value_arena.?.* = decl_arena.state;
2861-
const decl_arena_allocator = decl_arena.allocator();
2859+
var decl_arena: std.heap.ArenaAllocator = undefined;
2860+
const decl_arena_allocator = new_decl.value_arena.?.acquire(gpa, &decl_arena);
2861+
defer new_decl.value_arena.?.release(&decl_arena);
28622862

28632863
extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl);
28642864

@@ -26999,13 +26999,12 @@ const ComptimePtrMutationKit = struct {
2699926999

2700027000
fn beginArena(self: *ComptimePtrMutationKit, mod: *Module) Allocator {
2700127001
const decl = mod.declPtr(self.decl_ref_mut.decl_index);
27002-
self.decl_arena = decl.value_arena.?.promote(mod.gpa);
27003-
return self.decl_arena.allocator();
27002+
return decl.value_arena.?.acquire(mod.gpa, &self.decl_arena);
2700427003
}
2700527004

2700627005
fn finishArena(self: *ComptimePtrMutationKit, mod: *Module) void {
2700727006
const decl = mod.declPtr(self.decl_ref_mut.decl_index);
27008-
decl.value_arena.?.* = self.decl_arena.state;
27007+
decl.value_arena.?.release(&self.decl_arena);
2700927008
self.decl_arena = undefined;
2701027009
}
2701127010
};
@@ -27036,6 +27035,7 @@ fn beginComptimePtrMutation(
2703627035
.elem_ptr => {
2703727036
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2703827037
var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.array_ptr, elem_ptr.elem_ty);
27038+
2703927039
switch (parent.pointee) {
2704027040
.direct => |val_ptr| switch (parent.ty.zigTypeTag()) {
2704127041
.Array, .Vector => {
@@ -30653,10 +30653,9 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
3065330653
try sema.perm_arena.alloc(u32, struct_obj.fields.count())
3065430654
else blk: {
3065530655
const decl = sema.mod.declPtr(struct_obj.owner_decl);
30656-
var decl_arena = decl.value_arena.?.promote(sema.mod.gpa);
30657-
defer decl.value_arena.?.* = decl_arena.state;
30658-
const decl_arena_allocator = decl_arena.allocator();
30659-
30656+
var decl_arena: std.heap.ArenaAllocator = undefined;
30657+
const decl_arena_allocator = decl.value_arena.?.acquire(sema.mod.gpa, &decl_arena);
30658+
defer decl.value_arena.?.release(&decl_arena);
3066030659
break :blk try decl_arena_allocator.alloc(u32, struct_obj.fields.count());
3066130660
};
3066230661

@@ -30700,9 +30699,9 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
3070030699

3070130700
const decl_index = struct_obj.owner_decl;
3070230701
const decl = mod.declPtr(decl_index);
30703-
var decl_arena = decl.value_arena.?.promote(gpa);
30704-
defer decl.value_arena.?.* = decl_arena.state;
30705-
const decl_arena_allocator = decl_arena.allocator();
30702+
var decl_arena: std.heap.ArenaAllocator = undefined;
30703+
const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena);
30704+
defer decl.value_arena.?.release(&decl_arena);
3070630705

3070730706
const zir = struct_obj.namespace.file_scope.zir;
3070830707
const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
@@ -31394,9 +31393,9 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
3139431393
}
3139531394

3139631395
const decl = mod.declPtr(decl_index);
31397-
var decl_arena = decl.value_arena.?.promote(gpa);
31398-
defer decl.value_arena.?.* = decl_arena.state;
31399-
const decl_arena_allocator = decl_arena.allocator();
31396+
var decl_arena: std.heap.ArenaAllocator = undefined;
31397+
const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena);
31398+
defer decl.value_arena.?.release(&decl_arena);
3140031399

3140131400
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
3140231401
defer analysis_arena.deinit();
@@ -31734,10 +31733,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
3173431733
extra_index += body.len;
3173531734

3173631735
const decl = mod.declPtr(decl_index);
31737-
31738-
var decl_arena = decl.value_arena.?.promote(gpa);
31739-
defer decl.value_arena.?.* = decl_arena.state;
31740-
const decl_arena_allocator = decl_arena.allocator();
31736+
var decl_arena: std.heap.ArenaAllocator = undefined;
31737+
const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena);
31738+
defer decl.value_arena.?.release(&decl_arena);
3174131739

3174231740
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
3174331741
defer analysis_arena.deinit();

test/cases/decl_value_arena.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pub const Protocols: struct {
2+
list: *const fn(*Connection) void = undefined,
3+
handShake: type = struct {
4+
const stepStart: u8 = 0;
5+
},
6+
} = .{};
7+
8+
pub const Connection = struct {
9+
streamBuffer: [0]u8 = undefined,
10+
__lastReceivedPackets: [0]u8 = undefined,
11+
12+
handShakeState: u8 = Protocols.handShake.stepStart,
13+
};
14+
15+
pub fn main() void {
16+
var conn: Connection = undefined;
17+
_ = conn;
18+
}
19+
20+
// run
21+
//

0 commit comments

Comments
 (0)