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(
140140 data: *OverflowData,
141141 lhs_handle: ValueHandle,
142142 rhs_handle: ValueHandle,
143 ) callconv(.C) noreturn {
143 ) callconv(.c) noreturn {
144144 const lhs = lhs_handle.getValue(data);
145145 const rhs = rhs_handle.getValue(data);
146146
......@@ -163,7 +163,7 @@ fn overflowHandler(
163163fn negationHandler(
164164 data: *const OverflowData,
165165 old_value_handle: ValueHandle,
166) callconv(.C) noreturn {
166) callconv(.c) noreturn {
167167 const old_value = old_value_handle.getValue(data);
168168 logMessage(
169169 "negation of {} cannot be represented in type {s}",
......@@ -175,7 +175,7 @@ fn divRemHandler(
175175 data: *const OverflowData,
176176 lhs_handle: ValueHandle,
177177 rhs_handle: ValueHandle,
178) callconv(.C) noreturn {
178) callconv(.c) noreturn {
179179 const is_signed = data.type_descriptor.isSigned();
180180 const lhs = lhs_handle.getValue(data);
181181 const rhs = rhs_handle.getValue(data);
......@@ -199,7 +199,7 @@ fn alignmentAssumptionHandler(
199199 pointer: ValueHandle,
200200 alignment: ValueHandle,
201201 maybe_offset: ?ValueHandle,
202) callconv(.C) noreturn {
202) callconv(.c) noreturn {
203203 _ = pointer;
204204 // TODO: add the hint here?
205205 // const real_pointer = @intFromPtr(pointer) - @intFromPtr(maybe_offset);
......@@ -233,7 +233,7 @@ fn shiftOob(
233233 data: *const ShiftOobData,
234234 lhs_handle: ValueHandle,
235235 rhs_handle: ValueHandle,
236) callconv(.C) noreturn {
236) callconv(.c) noreturn {
237237 const lhs: Value = .{ .handle = lhs_handle, .type_descriptor = data.lhs_type };
238238 const rhs: Value = .{ .handle = rhs_handle, .type_descriptor = data.rhs_type };
239239
......@@ -266,7 +266,7 @@ const OutOfBoundsData = extern struct {
266266 index_type: *const TypeDescriptor,
267267};
268268
269fn outOfBounds(data: *const OutOfBoundsData, index_handle: ValueHandle) callconv(.C) noreturn {
269fn outOfBounds(data: *const OutOfBoundsData, index_handle: ValueHandle) callconv(.c) noreturn {
270270 const index: Value = .{ .handle = index_handle, .type_descriptor = data.index_type };
271271 logMessage(
272272 "index {} out of bounds for type {s}",
......@@ -282,7 +282,7 @@ fn pointerOverflow(
282282 _: *const PointerOverflowData,
283283 base: usize,
284284 result: usize,
285) callconv(.C) noreturn {
285) callconv(.c) noreturn {
286286 if (base == 0) {
287287 if (result == 0) {
288288 logMessage("applying zero offset to null pointer", .{});
......@@ -318,12 +318,100 @@ const TypeMismatchData = extern struct {
318318 upcast_to_virtual_base,
319319 nonnull_assign,
320320 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 }
321337 },
322338};
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
324412fn SimpleHandler(comptime error_name: []const u8) type {
325413 return struct {
326 fn handler() callconv(.C) noreturn {
414 fn handler() callconv(.c) noreturn {
327415 logMessage("{s}", .{error_name});
328416 }
329417 };
......@@ -350,10 +438,11 @@ fn exportHandler(
350438}
351439
352440fn exportMinimal(
353 handler: anytype,
441 err_name: anytype,
354442 comptime sym_name: []const u8,
355443 comptime abort: bool,
356444) void {
445 const handler = &SimpleHandler(err_name).handler;
357446 const linkage = if (builtin.is_test) .internal else .weak;
358447 {
359448 const N = "__ubsan_handle_" ++ sym_name ++ "_minimal";
......@@ -371,7 +460,7 @@ fn exportHelper(
371460 comptime abort: bool,
372461) void {
373462 exportHandler(&SimpleHandler(err_name).handler, sym_name, abort);
374 exportMinimal(&SimpleHandler(err_name).handler, sym_name, abort);
463 exportMinimal(err_name, sym_name, abort);
375464}
376465
377466comptime {
......@@ -384,35 +473,35 @@ comptime {
384473 exportHandler(&shiftOob, "shift_out_of_bounds", true);
385474 exportHandler(&outOfBounds, "out_of_bounds", true);
386475 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);
403483 exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true);
404484 exportHelper("float-cast-overflow", "float_cast_overflow", true);
405 exportHelper("load-invalid-value", "load_invalid_value", true);
406485 exportHelper("invalid-builtin", "invalid_builtin", true);
407486 exportHelper("function-type-mismatch", "function_type_mismatch", true);
408487 exportHelper("implicit-conversion", "implicit_conversion", true);
409 exportHelper("nonnull-arg", "nonnull_arg", true);
410 exportHelper("nonnull-return", "nonnull_return", true);
411488 exportHelper("nullability-arg", "nullability_arg", true);
412489 exportHelper("nullability-return", "nullability_return", true);
413490 exportHelper("cfi-check-fail", "cfi_check_fail", true);
414491 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
416505 // these checks are nearly impossible to duplicate in zig, as they rely on nuances
417506 // in the Itanium C++ ABI.
418507 // exportHelper("dynamic_type_cache_miss", "dynamic-type-cache-miss", true);