authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-12-25 03:52:12-08:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2025-02-25 11:22:33-08:00
logbabee5f73c01c220e3fb3901eb1a70149f94258b
treec750d7f862cc236381a8fb357fb7a3aa6f4f073a
parentc27b7973c9a54cd4ae0f8c9aa489fca50e13542b

ubsan: implement some more checks


1 files changed, 117 insertions(+), 28 deletions(-)

lib/std/ubsan.zig+117-28
...@@ -140,7 +140,7 @@ fn overflowHandler(...@@ -140,7 +140,7 @@ fn overflowHandler(
140 data: *OverflowData,140 data: *OverflowData,
141 lhs_handle: ValueHandle,141 lhs_handle: ValueHandle,
142 rhs_handle: ValueHandle,142 rhs_handle: ValueHandle,
143 ) callconv(.C) noreturn {143 ) callconv(.c) noreturn {
144 const lhs = lhs_handle.getValue(data);144 const lhs = lhs_handle.getValue(data);
145 const rhs = rhs_handle.getValue(data);145 const rhs = rhs_handle.getValue(data);
146146
...@@ -163,7 +163,7 @@ fn overflowHandler(...@@ -163,7 +163,7 @@ fn overflowHandler(
163fn negationHandler(163fn negationHandler(
164 data: *const OverflowData,164 data: *const OverflowData,
165 old_value_handle: ValueHandle,165 old_value_handle: ValueHandle,
166) callconv(.C) noreturn {166) callconv(.c) noreturn {
167 const old_value = old_value_handle.getValue(data);167 const old_value = old_value_handle.getValue(data);
168 logMessage(168 logMessage(
169 "negation of {} cannot be represented in type {s}",169 "negation of {} cannot be represented in type {s}",
...@@ -175,7 +175,7 @@ fn divRemHandler(...@@ -175,7 +175,7 @@ fn divRemHandler(
175 data: *const OverflowData,175 data: *const OverflowData,
176 lhs_handle: ValueHandle,176 lhs_handle: ValueHandle,
177 rhs_handle: ValueHandle,177 rhs_handle: ValueHandle,
178) callconv(.C) noreturn {178) callconv(.c) noreturn {
179 const is_signed = data.type_descriptor.isSigned();179 const is_signed = data.type_descriptor.isSigned();
180 const lhs = lhs_handle.getValue(data);180 const lhs = lhs_handle.getValue(data);
181 const rhs = rhs_handle.getValue(data);181 const rhs = rhs_handle.getValue(data);
...@@ -199,7 +199,7 @@ fn alignmentAssumptionHandler(...@@ -199,7 +199,7 @@ fn alignmentAssumptionHandler(
199 pointer: ValueHandle,199 pointer: ValueHandle,
200 alignment: ValueHandle,200 alignment: ValueHandle,
201 maybe_offset: ?ValueHandle,201 maybe_offset: ?ValueHandle,
202) callconv(.C) noreturn {202) callconv(.c) noreturn {
203 _ = pointer;203 _ = pointer;
204 // TODO: add the hint here?204 // TODO: add the hint here?
205 // const real_pointer = @intFromPtr(pointer) - @intFromPtr(maybe_offset);205 // const real_pointer = @intFromPtr(pointer) - @intFromPtr(maybe_offset);
...@@ -233,7 +233,7 @@ fn shiftOob(...@@ -233,7 +233,7 @@ fn shiftOob(
233 data: *const ShiftOobData,233 data: *const ShiftOobData,
234 lhs_handle: ValueHandle,234 lhs_handle: ValueHandle,
235 rhs_handle: ValueHandle,235 rhs_handle: ValueHandle,
236) callconv(.C) noreturn {236) callconv(.c) noreturn {
237 const lhs: Value = .{ .handle = lhs_handle, .type_descriptor = data.lhs_type };237 const lhs: Value = .{ .handle = lhs_handle, .type_descriptor = data.lhs_type };
238 const rhs: Value = .{ .handle = rhs_handle, .type_descriptor = data.rhs_type };238 const rhs: Value = .{ .handle = rhs_handle, .type_descriptor = data.rhs_type };
239239
...@@ -266,7 +266,7 @@ const OutOfBoundsData = extern struct {...@@ -266,7 +266,7 @@ const OutOfBoundsData = extern struct {
266 index_type: *const TypeDescriptor,266 index_type: *const TypeDescriptor,
267};267};
268268
269fn outOfBounds(data: *const OutOfBoundsData, index_handle: ValueHandle) callconv(.C) noreturn {269fn outOfBounds(data: *const OutOfBoundsData, index_handle: ValueHandle) callconv(.c) noreturn {
270 const index: Value = .{ .handle = index_handle, .type_descriptor = data.index_type };270 const index: Value = .{ .handle = index_handle, .type_descriptor = data.index_type };
271 logMessage(271 logMessage(
272 "index {} out of bounds for type {s}",272 "index {} out of bounds for type {s}",
...@@ -282,7 +282,7 @@ fn pointerOverflow(...@@ -282,7 +282,7 @@ fn pointerOverflow(
282 _: *const PointerOverflowData,282 _: *const PointerOverflowData,
283 base: usize,283 base: usize,
284 result: usize,284 result: usize,
285) callconv(.C) noreturn {285) callconv(.c) noreturn {
286 if (base == 0) {286 if (base == 0) {
287 if (result == 0) {287 if (result == 0) {
288 logMessage("applying zero offset to null pointer", .{});288 logMessage("applying zero offset to null pointer", .{});
...@@ -318,12 +318,100 @@ const TypeMismatchData = extern struct {...@@ -318,12 +318,100 @@ const TypeMismatchData = extern struct {
318 upcast_to_virtual_base,318 upcast_to_virtual_base,
319 nonnull_assign,319 nonnull_assign,
320 dynamic_operation,320 dynamic_operation,
321
322 fn getName(kind: @This()) []const u8 {
323 return switch (kind) {
324 .load => "load of",
325 .store => "store of",
326 .reference_binding => "reference binding to",
327 .member_access => "member access within",
328 .member_call => "member call on",
329 .constructor_call => "constructor call on",
330 .downcast_pointer, .downcast_reference => "downcast of",
331 .upcast => "upcast of",
332 .upcast_to_virtual_base => "cast to virtual base of",
333 .nonnull_assign => "_Nonnull binding to",
334 .dynamic_operation => "dynamic operation on",
335 };
336 }
321 },337 },
322};338};
323339
340fn typeMismatch(
341 data: *const TypeMismatchData,
342 pointer: ?ValueHandle,
343) callconv(.c) noreturn {
344 const alignment = @as(usize, 1) << @intCast(data.log_alignment);
345 const handle: usize = @intFromPtr(pointer);
346
347 if (pointer == null) {
348 logMessage(
349 "{s} null pointer of type {s}",
350 .{ data.kind.getName(), data.type_descriptor.getName() },
351 );
352 } else if (!std.mem.isAligned(handle, alignment)) {
353 logMessage(
354 "{s} misaligned address 0x{x} for type {s}, which requires {} byte alignment",
355 .{ data.kind.getName(), handle, data.type_descriptor.getName(), alignment },
356 );
357 } else {
358 logMessage(
359 "{s} address 0x{x} with insufficient space for an object of type {s}",
360 .{ data.kind.getName(), handle, data.type_descriptor.getName() },
361 );
362 }
363}
364
365const UnreachableData = extern struct {
366 loc: SourceLocation,
367};
368
369fn builtinUnreachable(_: *const UnreachableData) callconv(.c) noreturn {
370 logMessage("execution reached an unreachable program point", .{});
371}
372
373fn missingReturn(_: *const UnreachableData) callconv(.c) noreturn {
374 logMessage("execution reached the end of a value-returning function without returning a value", .{});
375}
376
377const NonNullReturnData = extern struct {
378 attribute_loc: SourceLocation,
379};
380
381fn nonNullReturn(_: *const NonNullReturnData) callconv(.c) noreturn {
382 logMessage("null pointer returned from function declared to never return null", .{});
383}
384
385const NonNullArgData = extern struct {
386 loc: SourceLocation,
387 attribute_loc: SourceLocation,
388 arg_index: i32,
389};
390
391fn nonNullArg(data: *const NonNullArgData) callconv(.c) noreturn {
392 logMessage(
393 "null pointer passed as argument {}, which is declared to never be null",
394 .{data.arg_index},
395 );
396}
397
398const InvalidValueData = extern struct {
399 loc: SourceLocation,
400 type_descriptor: *const TypeDescriptor,
401};
402
403fn loadInvalidValue(
404 data: *const InvalidValueData,
405 value_handle: ValueHandle,
406) callconv(.c) noreturn {
407 logMessage("load of value {}, which is not valid for type {s}", .{
408 value_handle.getValue(data), data.type_descriptor.getName(),
409 });
410}
411
324fn SimpleHandler(comptime error_name: []const u8) type {412fn SimpleHandler(comptime error_name: []const u8) type {
325 return struct {413 return struct {
326 fn handler() callconv(.C) noreturn {414 fn handler() callconv(.c) noreturn {
327 logMessage("{s}", .{error_name});415 logMessage("{s}", .{error_name});
328 }416 }
329 };417 };
...@@ -350,10 +438,11 @@ fn exportHandler(...@@ -350,10 +438,11 @@ fn exportHandler(
350}438}
351439
352fn exportMinimal(440fn exportMinimal(
353 handler: anytype,441 err_name: anytype,
354 comptime sym_name: []const u8,442 comptime sym_name: []const u8,
355 comptime abort: bool,443 comptime abort: bool,
356) void {444) void {
445 const handler = &SimpleHandler(err_name).handler;
357 const linkage = if (builtin.is_test) .internal else .weak;446 const linkage = if (builtin.is_test) .internal else .weak;
358 {447 {
359 const N = "__ubsan_handle_" ++ sym_name ++ "_minimal";448 const N = "__ubsan_handle_" ++ sym_name ++ "_minimal";
...@@ -371,7 +460,7 @@ fn exportHelper(...@@ -371,7 +460,7 @@ fn exportHelper(
371 comptime abort: bool,460 comptime abort: bool,
372) void {461) void {
373 exportHandler(&SimpleHandler(err_name).handler, sym_name, abort);462 exportHandler(&SimpleHandler(err_name).handler, sym_name, abort);
374 exportMinimal(&SimpleHandler(err_name).handler, sym_name, abort);463 exportMinimal(err_name, sym_name, abort);
375}464}
376465
377comptime {466comptime {
...@@ -384,35 +473,35 @@ comptime {...@@ -384,35 +473,35 @@ comptime {
384 exportHandler(&shiftOob, "shift_out_of_bounds", true);473 exportHandler(&shiftOob, "shift_out_of_bounds", true);
385 exportHandler(&outOfBounds, "out_of_bounds", true);474 exportHandler(&outOfBounds, "out_of_bounds", true);
386 exportHandler(&pointerOverflow, "pointer_overflow", true);475 exportHandler(&pointerOverflow, "pointer_overflow", true);
476 exportHandler(&typeMismatch, "type_mismatch_v1", true);
477 exportHandler(&builtinUnreachable, "builtin_unreachable", false);
478 exportHandler(&missingReturn, "missing_return", false);
479 exportHandler(&nonNullReturn, "nonnull_return_v1", true);
480 exportHandler(&nonNullArg, "nonnull_arg", true);
481 exportHandler(&loadInvalidValue, "load_invalid_value", true);
387482
388 exportMinimal("add-overflow", "add_overflow", true);
389 exportMinimal("sub-overflow", "sub_overflow", true);
390 exportMinimal("mul-overflow", "mul_overflow", true);
391 exportMinimal("negation-handler", "negate_overflow", true);
392 exportMinimal("divrem-handler", "divrem_overflow", true);
393 exportMinimal("alignment-assumption-handler", "alignment_assumption", true);
394 exportMinimal("shift-oob", "shift_out_of_bounds", true);
395 exportMinimal("out-of-bounds", "out_of_bounds", true);
396 exportMinimal("pointer-overflow", "pointer_overflow", true);
397
398 exportHandler(&SimpleHandler("type-mismatch-v1").handler, "type_mismatch_v1", true);
399 exportMinimal(&SimpleHandler("type-mismatch").handler, "type_mismatch", true);
400
401 exportHelper("builtin-unreachable", "builtin_unreachable", true);
402 exportHelper("missing-return", "missing_return", false);
403 exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true);483 exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true);
404 exportHelper("float-cast-overflow", "float_cast_overflow", true);484 exportHelper("float-cast-overflow", "float_cast_overflow", true);
405 exportHelper("load-invalid-value", "load_invalid_value", true);
406 exportHelper("invalid-builtin", "invalid_builtin", true);485 exportHelper("invalid-builtin", "invalid_builtin", true);
407 exportHelper("function-type-mismatch", "function_type_mismatch", true);486 exportHelper("function-type-mismatch", "function_type_mismatch", true);
408 exportHelper("implicit-conversion", "implicit_conversion", true);487 exportHelper("implicit-conversion", "implicit_conversion", true);
409 exportHelper("nonnull-arg", "nonnull_arg", true);
410 exportHelper("nonnull-return", "nonnull_return", true);
411 exportHelper("nullability-arg", "nullability_arg", true);488 exportHelper("nullability-arg", "nullability_arg", true);
412 exportHelper("nullability-return", "nullability_return", true);489 exportHelper("nullability-return", "nullability_return", true);
413 exportHelper("cfi-check-fail", "cfi_check_fail", true);490 exportHelper("cfi-check-fail", "cfi_check_fail", true);
414 exportHelper("function-type-mismatch-v1", "function_type_mismatch_v1", true);491 exportHelper("function-type-mismatch-v1", "function_type_mismatch_v1", true);
415492
493 exportMinimal("builtin-unreachable", "builtin_unreachable", false);
494 exportMinimal("add-overflow", "add_overflow", true);
495 exportMinimal("sub-overflow", "sub_overflow", true);
496 exportMinimal("mul-overflow", "mul_overflow", true);
497 exportMinimal("negation-handler", "negate_overflow", true);
498 exportMinimal("divrem-handler", "divrem_overflow", true);
499 exportMinimal("alignment-assumption-handler", "alignment_assumption", true);
500 exportMinimal("shift-oob", "shift_out_of_bounds", true);
501 exportMinimal("out-of-bounds", "out_of_bounds", true);
502 exportMinimal("pointer-overflow", "pointer_overflow", true);
503 exportMinimal("type-mismatch", "type_mismatch", true);
504
416 // these checks are nearly impossible to duplicate in zig, as they rely on nuances505 // these checks are nearly impossible to duplicate in zig, as they rely on nuances
417 // in the Itanium C++ ABI.506 // in the Itanium C++ ABI.
418 // exportHelper("dynamic_type_cache_miss", "dynamic-type-cache-miss", true);507 // exportHelper("dynamic_type_cache_miss", "dynamic-type-cache-miss", true);