ubsan: implement more checks

This commit is contained in:
David Rubin 2024-12-26 02:27:22 -08:00 committed by David Rubin
parent fc77678339
commit 590c613182

View File

@ -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);