diff --git a/lib/ubsan.zig b/lib/ubsan.zig index 74b8f6273d..0142adddc3 100644 --- a/lib/ubsan.zig +++ b/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);