mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Sema: fix UAF in zirClosureGet
Previously if a decl failed its capture scope would be deallocated and set to undefined which would then lead to invalid dereference in `zirClosureGet`. To avoid this set the capture scope to a special failed state and fail the current decl with dependency failure if the failed state is encountered in `zirClosureGet`. Closes #12433 Closes #12530 Closes #12593
This commit is contained in:
parent
37afab2add
commit
99826a2ba8
@ -345,6 +345,15 @@ pub const CaptureScope = struct {
|
||||
/// During sema, this map is backed by the gpa. Once sema completes,
|
||||
/// it is reallocated using the value_arena.
|
||||
captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{},
|
||||
|
||||
pub fn failed(noalias self: *const @This()) bool {
|
||||
return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32);
|
||||
}
|
||||
|
||||
pub fn fail(noalias self: *@This()) void {
|
||||
self.captures.available = 0;
|
||||
self.captures.size = std.math.maxInt(u32);
|
||||
}
|
||||
};
|
||||
|
||||
pub const WipCaptureScope = struct {
|
||||
@ -383,6 +392,7 @@ pub const WipCaptureScope = struct {
|
||||
pub fn deinit(noalias self: *@This()) void {
|
||||
if (!self.finalized) {
|
||||
self.scope.captures.deinit(self.gpa);
|
||||
self.scope.fail();
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
11
src/Sema.zig
11
src/Sema.zig
@ -5956,7 +5956,6 @@ fn analyzeCall(
|
||||
error.NeededSourceLocation => {
|
||||
_ = sema.inst_map.remove(inst);
|
||||
const decl = sema.mod.declPtr(block.src_decl);
|
||||
child_block.src_decl = block.src_decl;
|
||||
try sema.analyzeInlineCallArg(
|
||||
block,
|
||||
&child_block,
|
||||
@ -13740,6 +13739,16 @@ fn zirClosureGet(
|
||||
const tv = while (true) {
|
||||
// Note: We don't need to add a dependency here, because
|
||||
// decls always depend on their lexical parents.
|
||||
|
||||
// Fail this decl if a scope it depended on failed.
|
||||
if (scope.failed()) {
|
||||
if (sema.owner_func) |owner_func| {
|
||||
owner_func.state = .dependency_failure;
|
||||
} else {
|
||||
sema.owner_decl.analysis = .dependency_failure;
|
||||
}
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
if (scope.captures.getPtr(inst_data.inst)) |tv| {
|
||||
break tv;
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
pub inline fn instanceRequestAdapter() void {}
|
||||
|
||||
pub inline fn requestAdapter(
|
||||
comptime callbackArg: fn () callconv(.Inline) void,
|
||||
) void {
|
||||
_ = (struct {
|
||||
pub fn callback() callconv(.C) void {
|
||||
callbackArg();
|
||||
}
|
||||
}).callback;
|
||||
instanceRequestAdapter(undefined); // note wrong number of arguments here
|
||||
}
|
||||
|
||||
inline fn foo() void {}
|
||||
|
||||
pub export fn entry() void {
|
||||
requestAdapter(foo);
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :11:5: error: expected 0 argument(s), found 1
|
||||
// :1:12: note: function declared here
|
||||
// :17:19: note: called from here
|
||||
@ -0,0 +1,24 @@
|
||||
fn Observable(comptime T: type) type {
|
||||
return struct {
|
||||
fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst {
|
||||
_ = Src;
|
||||
_ = function;
|
||||
return Observable(Dst);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn u32Tou64(x: u32) u64 {
|
||||
_ = x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub export fn entry() void {
|
||||
Observable(u32).map(u32, u64, u32Tou64(0));
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :17:25: error: expected type 'u32', found 'type'
|
||||
Loading…
x
Reference in New Issue
Block a user