authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-07 22:05:01+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-08 00:37:11+03:00
log99826a2ba89ccd80caaa4eeb47c59a71ddfe76b6
tree1e769aff2edca88474f60f28846da7a4a62e18f0
parent37afab2addab5809e1419a09e3be5ea4f3ee5501

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

4 files changed, 70 insertions(+), 1 deletions(-)

src/Module.zig+10
......@@ -345,6 +345,15 @@ pub const CaptureScope = struct {
345345 /// During sema, this map is backed by the gpa. Once sema completes,
346346 /// it is reallocated using the value_arena.
347347 captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{},
348
349 pub fn failed(noalias self: *const @This()) bool {
350 return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32);
351 }
352
353 pub fn fail(noalias self: *@This()) void {
354 self.captures.available = 0;
355 self.captures.size = std.math.maxInt(u32);
356 }
348357};
349358
350359pub const WipCaptureScope = struct {
......@@ -383,6 +392,7 @@ pub const WipCaptureScope = struct {
383392 pub fn deinit(noalias self: *@This()) void {
384393 if (!self.finalized) {
385394 self.scope.captures.deinit(self.gpa);
395 self.scope.fail();
386396 }
387397 self.* = undefined;
388398 }
src/Sema.zig+10-1
......@@ -5956,7 +5956,6 @@ fn analyzeCall(
59565956 error.NeededSourceLocation => {
59575957 _ = sema.inst_map.remove(inst);
59585958 const decl = sema.mod.declPtr(block.src_decl);
5959 child_block.src_decl = block.src_decl;
59605959 try sema.analyzeInlineCallArg(
59615960 block,
59625961 &child_block,
......@@ -13740,6 +13739,16 @@ fn zirClosureGet(
1374013739 const tv = while (true) {
1374113740 // Note: We don't need to add a dependency here, because
1374213741 // decls always depend on their lexical parents.
13742
13743 // Fail this decl if a scope it depended on failed.
13744 if (scope.failed()) {
13745 if (sema.owner_func) |owner_func| {
13746 owner_func.state = .dependency_failure;
13747 } else {
13748 sema.owner_decl.analysis = .dependency_failure;
13749 }
13750 return error.AnalysisFail;
13751 }
1374313752 if (scope.captures.getPtr(inst_data.inst)) |tv| {
1374413753 break tv;
1374513754 }
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig created+26
......@@ -0,0 +1,26 @@
1pub inline fn instanceRequestAdapter() void {}
2
3pub inline fn requestAdapter(
4 comptime callbackArg: fn () callconv(.Inline) void,
5) void {
6 _ = (struct {
7 pub fn callback() callconv(.C) void {
8 callbackArg();
9 }
10 }).callback;
11 instanceRequestAdapter(undefined); // note wrong number of arguments here
12}
13
14inline fn foo() void {}
15
16pub export fn entry() void {
17 requestAdapter(foo);
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :11:5: error: expected 0 argument(s), found 1
25// :1:12: note: function declared here
26// :17:19: note: called from here
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig created+24
......@@ -0,0 +1,24 @@
1fn Observable(comptime T: type) type {
2 return struct {
3 fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst {
4 _ = Src;
5 _ = function;
6 return Observable(Dst);
7 }
8 };
9}
10
11fn u32Tou64(x: u32) u64 {
12 _ = x;
13 return 0;
14}
15
16pub export fn entry() void {
17 Observable(u32).map(u32, u64, u32Tou64(0));
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :17:25: error: expected type 'u32', found 'type'