Commit 590c613182

David Rubin <daviru007@icloud.com>
2024-12-26 11:27:22
ubsan: implement more checks
1 parent fc77678
Changed files (1)
lib/ubsan.zig
@@ -26,6 +26,7 @@ const TypeDescriptor = extern struct {
             signed: bool,
             bit_width: u15,
         },
+        float: u16,
     };
 
     fn getIntegerSize(desc: TypeDescriptor) u64 {
@@ -80,10 +81,25 @@ const Value = extern struct {
         return switch (size) {
             64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*,
             128 => @as(*const i128, @alignCast(@ptrCast(value.handle))).*,
-            else => unreachable,
+            else => @trap(),
         };
     }
 
+    fn getFloat(value: Value) c_longdouble {
+        assert(value.type_descriptor.kind == .float);
+        const size = value.type_descriptor.info.float;
+        const max_inline_size = @bitSizeOf(ValueHandle);
+        if (size <= max_inline_size) {
+            return @bitCast(@intFromPtr(value.handle));
+        }
+        return @floatCast(switch (size) {
+            64 => @as(*const f64, @alignCast(@ptrCast(value.handle))).*,
+            80 => @as(*const f80, @alignCast(@ptrCast(value.handle))).*,
+            128 => @as(*const f128, @alignCast(@ptrCast(value.handle))).*,
+            else => @trap(),
+        });
+    }
+
     fn isMinusOne(value: Value) bool {
         return value.type_descriptor.isSigned() and
             value.getSignedInteger() == -1;
@@ -120,7 +136,7 @@ const Value = extern struct {
                     try writer.print("{}", .{value.getUnsignedInteger()});
                 }
             },
-            .float => @panic("TODO: write float"),
+            .float => try writer.print("{}", .{value.getFloat()}),
             .unknown => try writer.writeAll("(unknown)"),
         }
     }
@@ -176,11 +192,10 @@ fn divRemHandler(
     lhs_handle: ValueHandle,
     rhs_handle: ValueHandle,
 ) callconv(.c) noreturn {
-    const is_signed = data.type_descriptor.isSigned();
     const lhs = lhs_handle.getValue(data);
     const rhs = rhs_handle.getValue(data);
 
-    if (is_signed and rhs.getSignedInteger() == -1) {
+    if (rhs.isMinusOne()) {
         logMessage(
             "division of {} by -1 cannot be represented in type {s}",
             .{ lhs, data.type_descriptor.getName() },
@@ -409,6 +424,68 @@ fn loadInvalidValue(
     });
 }
 
+const InvalidBuiltinData = extern struct {
+    loc: SourceLocation,
+    kind: enum(u8) {
+        ctz,
+        clz,
+    },
+};
+
+fn invalidBuiltin(data: *const InvalidBuiltinData) callconv(.c) noreturn {
+    logMessage(
+        "passing zero to {s}(), which is not a valid argument",
+        .{@tagName(data.kind)},
+    );
+}
+
+const VlaBoundNotPositive = extern struct {
+    loc: SourceLocation,
+    type_descriptor: *const TypeDescriptor,
+};
+
+fn vlaBoundNotPositive(
+    data: *const VlaBoundNotPositive,
+    bound_handle: ValueHandle,
+) callconv(.c) noreturn {
+    logMessage("variable length array bound evaluates to non-positive value {}", .{
+        bound_handle.getValue(data),
+    });
+}
+
+const FloatCastOverflowData = extern struct {
+    from: *const TypeDescriptor,
+    to: *const TypeDescriptor,
+};
+
+const FloatCastOverflowDataV2 = extern struct {
+    loc: SourceLocation,
+    from: *const TypeDescriptor,
+    to: *const TypeDescriptor,
+};
+
+fn floatCastOverflow(
+    data_handle: *align(8) const anyopaque,
+    from_handle: ValueHandle,
+) callconv(.c) noreturn {
+    // See: https://github.com/llvm/llvm-project/blob/release/19.x/compiler-rt/lib/ubsan/ubsan_handlers.cpp#L463
+    // for more information on this check.
+    const ptr: [*]const u8 = @ptrCast(data_handle);
+    if (ptr[0] + ptr[1] < 2 or ptr[0] == 0xFF or ptr[1] == 0xFF) {
+        const data: *const FloatCastOverflowData = @ptrCast(data_handle);
+        const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from };
+        logMessage("{} is outside the range of representable values of type {s}", .{
+            from_value, data.to.getName(),
+        });
+    } else {
+        const data: *const FloatCastOverflowDataV2 = @ptrCast(data_handle);
+        const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from };
+        logMessage("{} is outside the range of representable values of type {s}", .{
+            from_value, data.to.getName(),
+        });
+    }
+}
+
 fn SimpleHandler(comptime error_name: []const u8) type {
     return struct {
         fn handler() callconv(.c) noreturn {
@@ -479,10 +556,10 @@ comptime {
     exportHandler(&nonNullReturn, "nonnull_return_v1", true);
     exportHandler(&nonNullArg, "nonnull_arg", true);
     exportHandler(&loadInvalidValue, "load_invalid_value", true);
+    exportHandler(&invalidBuiltin, "invalid_builtin", true);
+    exportHandler(&vlaBoundNotPositive, "vla_bound_not_positive", true);
+    exportHandler(&floatCastOverflow, "float_cast_overflow", true);
 
-    exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true);
-    exportHelper("float-cast-overflow", "float_cast_overflow", true);
-    exportHelper("invalid-builtin", "invalid_builtin", true);
     exportHelper("function-type-mismatch", "function_type_mismatch", true);
     exportHelper("implicit-conversion", "implicit_conversion", true);
     exportHelper("nullability-arg", "nullability_arg", true);