authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-12-26 02:27:22-08:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2025-02-25 11:22:33-08:00
log590c613182d096e150dd9c882ab3bc6b20028011
tree602602f66e9ea23802608f8019ac19f72a6c915f
parentfc776783391b16ef77df05581412746bdb83ffa6

ubsan: implement more checks


1 files changed, 84 insertions(+), 7 deletions(-)

lib/ubsan.zig+84-7
...@@ -26,6 +26,7 @@ const TypeDescriptor = extern struct {...@@ -26,6 +26,7 @@ const TypeDescriptor = extern struct {
26 signed: bool,26 signed: bool,
27 bit_width: u15,27 bit_width: u15,
28 },28 },
29 float: u16,
29 };30 };
3031
31 fn getIntegerSize(desc: TypeDescriptor) u64 {32 fn getIntegerSize(desc: TypeDescriptor) u64 {
...@@ -80,10 +81,25 @@ const Value = extern struct {...@@ -80,10 +81,25 @@ const Value = extern struct {
80 return switch (size) {81 return switch (size) {
81 64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*,82 64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*,
82 128 => @as(*const i128, @alignCast(@ptrCast(value.handle))).*,83 128 => @as(*const i128, @alignCast(@ptrCast(value.handle))).*,
83 else => unreachable,84 else => @trap(),
84 };85 };
85 }86 }
8687
88 fn getFloat(value: Value) c_longdouble {
89 assert(value.type_descriptor.kind == .float);
90 const size = value.type_descriptor.info.float;
91 const max_inline_size = @bitSizeOf(ValueHandle);
92 if (size <= max_inline_size) {
93 return @bitCast(@intFromPtr(value.handle));
94 }
95 return @floatCast(switch (size) {
96 64 => @as(*const f64, @alignCast(@ptrCast(value.handle))).*,
97 80 => @as(*const f80, @alignCast(@ptrCast(value.handle))).*,
98 128 => @as(*const f128, @alignCast(@ptrCast(value.handle))).*,
99 else => @trap(),
100 });
101 }
102
87 fn isMinusOne(value: Value) bool {103 fn isMinusOne(value: Value) bool {
88 return value.type_descriptor.isSigned() and104 return value.type_descriptor.isSigned() and
89 value.getSignedInteger() == -1;105 value.getSignedInteger() == -1;
...@@ -120,7 +136,7 @@ const Value = extern struct {...@@ -120,7 +136,7 @@ const Value = extern struct {
120 try writer.print("{}", .{value.getUnsignedInteger()});136 try writer.print("{}", .{value.getUnsignedInteger()});
121 }137 }
122 },138 },
123 .float => @panic("TODO: write float"),139 .float => try writer.print("{}", .{value.getFloat()}),
124 .unknown => try writer.writeAll("(unknown)"),140 .unknown => try writer.writeAll("(unknown)"),
125 }141 }
126 }142 }
...@@ -176,11 +192,10 @@ fn divRemHandler(...@@ -176,11 +192,10 @@ fn divRemHandler(
176 lhs_handle: ValueHandle,192 lhs_handle: ValueHandle,
177 rhs_handle: ValueHandle,193 rhs_handle: ValueHandle,
178) callconv(.c) noreturn {194) callconv(.c) noreturn {
179 const is_signed = data.type_descriptor.isSigned();
180 const lhs = lhs_handle.getValue(data);195 const lhs = lhs_handle.getValue(data);
181 const rhs = rhs_handle.getValue(data);196 const rhs = rhs_handle.getValue(data);
182197
183 if (is_signed and rhs.getSignedInteger() == -1) {198 if (rhs.isMinusOne()) {
184 logMessage(199 logMessage(
185 "division of {} by -1 cannot be represented in type {s}",200 "division of {} by -1 cannot be represented in type {s}",
186 .{ lhs, data.type_descriptor.getName() },201 .{ lhs, data.type_descriptor.getName() },
...@@ -409,6 +424,68 @@ fn loadInvalidValue(...@@ -409,6 +424,68 @@ fn loadInvalidValue(
409 });424 });
410}425}
411426
427const InvalidBuiltinData = extern struct {
428 loc: SourceLocation,
429 kind: enum(u8) {
430 ctz,
431 clz,
432 },
433};
434
435fn invalidBuiltin(data: *const InvalidBuiltinData) callconv(.c) noreturn {
436 logMessage(
437 "passing zero to {s}(), which is not a valid argument",
438 .{@tagName(data.kind)},
439 );
440}
441
442const VlaBoundNotPositive = extern struct {
443 loc: SourceLocation,
444 type_descriptor: *const TypeDescriptor,
445};
446
447fn vlaBoundNotPositive(
448 data: *const VlaBoundNotPositive,
449 bound_handle: ValueHandle,
450) callconv(.c) noreturn {
451 logMessage("variable length array bound evaluates to non-positive value {}", .{
452 bound_handle.getValue(data),
453 });
454}
455
456const FloatCastOverflowData = extern struct {
457 from: *const TypeDescriptor,
458 to: *const TypeDescriptor,
459};
460
461const FloatCastOverflowDataV2 = extern struct {
462 loc: SourceLocation,
463 from: *const TypeDescriptor,
464 to: *const TypeDescriptor,
465};
466
467fn floatCastOverflow(
468 data_handle: *align(8) const anyopaque,
469 from_handle: ValueHandle,
470) callconv(.c) noreturn {
471 // See: https://github.com/llvm/llvm-project/blob/release/19.x/compiler-rt/lib/ubsan/ubsan_handlers.cpp#L463
472 // for more information on this check.
473 const ptr: [*]const u8 = @ptrCast(data_handle);
474 if (ptr[0] + ptr[1] < 2 or ptr[0] == 0xFF or ptr[1] == 0xFF) {
475 const data: *const FloatCastOverflowData = @ptrCast(data_handle);
476 const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from };
477 logMessage("{} is outside the range of representable values of type {s}", .{
478 from_value, data.to.getName(),
479 });
480 } else {
481 const data: *const FloatCastOverflowDataV2 = @ptrCast(data_handle);
482 const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from };
483 logMessage("{} is outside the range of representable values of type {s}", .{
484 from_value, data.to.getName(),
485 });
486 }
487}
488
412fn SimpleHandler(comptime error_name: []const u8) type {489fn SimpleHandler(comptime error_name: []const u8) type {
413 return struct {490 return struct {
414 fn handler() callconv(.c) noreturn {491 fn handler() callconv(.c) noreturn {
...@@ -479,10 +556,10 @@ comptime {...@@ -479,10 +556,10 @@ comptime {
479 exportHandler(&nonNullReturn, "nonnull_return_v1", true);556 exportHandler(&nonNullReturn, "nonnull_return_v1", true);
480 exportHandler(&nonNullArg, "nonnull_arg", true);557 exportHandler(&nonNullArg, "nonnull_arg", true);
481 exportHandler(&loadInvalidValue, "load_invalid_value", true);558 exportHandler(&loadInvalidValue, "load_invalid_value", true);
559 exportHandler(&invalidBuiltin, "invalid_builtin", true);
560 exportHandler(&vlaBoundNotPositive, "vla_bound_not_positive", true);
561 exportHandler(&floatCastOverflow, "float_cast_overflow", true);
482562
483 exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true);
484 exportHelper("float-cast-overflow", "float_cast_overflow", true);
485 exportHelper("invalid-builtin", "invalid_builtin", true);
486 exportHelper("function-type-mismatch", "function_type_mismatch", true);563 exportHelper("function-type-mismatch", "function_type_mismatch", true);
487 exportHelper("implicit-conversion", "implicit_conversion", true);564 exportHelper("implicit-conversion", "implicit_conversion", true);
488 exportHelper("nullability-arg", "nullability_arg", true);565 exportHelper("nullability-arg", "nullability_arg", true);