Commit 99826a2ba8

Veikka Tuominen <git@vexu.eu>
2022-09-07 21:05:01
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
1 parent 37afab2
src/Module.zig
@@ -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;
     }
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;
         }
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig
@@ -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
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig
@@ -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'