╭ ubsan_rt.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const assert = std.debug.assert; 4 const panic = std.debug.panicExtra; 5 6 pub const std_options: std.Options = .{ 7 .networking = false, 8 }; 9 10 const SourceLocation = extern struct { 11 file_name: ?[*:0]const u8, 12 line: u32, 13 col: u32, 14 }; 15 16 const TypeDescriptor = extern struct { 17 kind: Kind, 18 info: Info, 19 // name: [?:0]u8 20 21 const Kind = enum(u16) { 22 integer = 0x0000, 23 float = 0x0001, 24 unknown = 0xFFFF, 25 }; 26 27 const Info = extern union { 28 integer: packed struct(u16) { 29 signed: bool, 30 bit_width: u15, 31 }, 32 float: u16, 33 }; 34 35 fn getIntegerSize(desc: TypeDescriptor) u64 { 36 assert(desc.kind == .integer); 37 const bit_width = desc.info.integer.bit_width; 38 return @as(u64, 1) << @intCast(bit_width); 39 } 40 41 fn isSigned(desc: TypeDescriptor) bool { 42 return desc.kind == .integer and desc.info.integer.signed; 43 } 44 45 fn getName(desc: *const TypeDescriptor) [:0]const u8 { 46 return std.mem.span(@as([*:0]const u8, @ptrCast(desc)) + @sizeOf(TypeDescriptor)); 47 } 48 }; 49 50 const ValueHandle = *const opaque {}; 51 52 const Value = extern struct { 53 td: *const TypeDescriptor, 54 handle: ValueHandle, 55 56 fn getUnsignedInteger(value: Value) u128 { 57 assert(!value.td.isSigned()); 58 const size = value.td.getIntegerSize(); 59 const max_inline_size = @bitSizeOf(ValueHandle); 60 if (size <= max_inline_size) { 61 return @intFromPtr(value.handle); 62 } 63 64 return switch (size) { 65 64 => @as(*const u64, @ptrCast(@alignCast(value.handle))).*, 66 128 => @as(*const u128, @ptrCast(@alignCast(value.handle))).*, 67 else => @trap(), 68 }; 69 } 70 71 fn getSignedInteger(value: Value) i128 { 72 assert(value.td.isSigned()); 73 const size = value.td.getIntegerSize(); 74 const max_inline_size = @bitSizeOf(ValueHandle); 75 if (size <= max_inline_size) { 76 const extra_bits: std.math.Log2Int(usize) = @intCast(max_inline_size - size); 77 const handle: isize = @bitCast(@intFromPtr(value.handle)); 78 return (handle << extra_bits) >> extra_bits; 79 } 80 return switch (size) { 81 64 => @as(*const i64, @ptrCast(@alignCast(value.handle))).*, 82 128 => @as(*const i128, @ptrCast(@alignCast(value.handle))).*, 83 else => @trap(), 84 }; 85 } 86 87 fn getFloat(value: Value) f128 { 88 assert(value.td.kind == .float); 89 const size = value.td.info.float; 90 const max_inline_size = @bitSizeOf(ValueHandle); 91 if (size <= max_inline_size) { 92 return @as(switch (@bitSizeOf(usize)) { 93 32 => f32, 94 64 => f64, 95 else => @compileError("unsupported target"), 96 }, @bitCast(@intFromPtr(value.handle))); 97 } 98 return @floatCast(switch (size) { 99 64 => @as(*const f64, @ptrCast(@alignCast(value.handle))).*, 100 80 => @as(*const f80, @ptrCast(@alignCast(value.handle))).*, 101 128 => @as(*const f128, @ptrCast(@alignCast(value.handle))).*, 102 else => @trap(), 103 }); 104 } 105 106 fn isMinusOne(value: Value) bool { 107 return value.td.isSigned() and 108 value.getSignedInteger() == -1; 109 } 110 111 fn isNegative(value: Value) bool { 112 return value.td.isSigned() and 113 value.getSignedInteger() < 0; 114 } 115 116 fn getPositiveInteger(value: Value) u128 { 117 if (value.td.isSigned()) { 118 const signed = value.getSignedInteger(); 119 assert(signed >= 0); 120 return @intCast(signed); 121 } else { 122 return value.getUnsignedInteger(); 123 } 124 } 125 126 pub fn format(value: Value, writer: *std.Io.Writer) std.Io.Writer.Error!void { 127 switch (value.td.kind) { 128 .integer => { 129 if (value.td.isSigned()) { 130 try writer.print("{d}", .{value.getSignedInteger()}); 131 } else { 132 try writer.print("{d}", .{value.getUnsignedInteger()}); 133 } 134 }, 135 .float => try writer.print("{d}", .{value.getFloat()}), 136 .unknown => try writer.writeAll("(unknown)"), 137 } 138 } 139 }; 140 141 const OverflowData = extern struct { 142 loc: SourceLocation, 143 td: *const TypeDescriptor, 144 }; 145 146 fn overflowHandler( 147 comptime sym_name: []const u8, 148 comptime operator: []const u8, 149 ) void { 150 const S = struct { 151 fn abort( 152 data: *const OverflowData, 153 lhs_handle: ValueHandle, 154 rhs_handle: ValueHandle, 155 ) callconv(.c) noreturn { 156 handler(data, lhs_handle, rhs_handle); 157 } 158 159 fn handler( 160 data: *const OverflowData, 161 lhs_handle: ValueHandle, 162 rhs_handle: ValueHandle, 163 ) callconv(.c) noreturn { 164 const lhs: Value = .{ .handle = lhs_handle, .td = data.td }; 165 const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; 166 const signed_str = if (data.td.isSigned()) "signed" else "unsigned"; 167 panic( 168 @returnAddress(), 169 "{s} integer overflow: {f} " ++ operator ++ " {f} cannot be represented in type {s}", 170 .{ signed_str, lhs, rhs, data.td.getName() }, 171 ); 172 } 173 }; 174 175 exportHandlerWithAbort(&S.handler, &S.abort, sym_name); 176 } 177 178 fn negationHandlerAbort( 179 data: *const OverflowData, 180 value_handle: ValueHandle, 181 ) callconv(.c) noreturn { 182 negationHandler(data, value_handle); 183 } 184 185 fn negationHandler( 186 data: *const OverflowData, 187 value_handle: ValueHandle, 188 ) callconv(.c) noreturn { 189 const value: Value = .{ .handle = value_handle, .td = data.td }; 190 panic(@returnAddress(), "negation of {f} cannot be represented in type {s}", .{ 191 value, data.td.getName(), 192 }); 193 } 194 195 fn divRemHandlerAbort( 196 data: *const OverflowData, 197 lhs_handle: ValueHandle, 198 rhs_handle: ValueHandle, 199 ) callconv(.c) noreturn { 200 divRemHandler(data, lhs_handle, rhs_handle); 201 } 202 203 fn divRemHandler( 204 data: *const OverflowData, 205 lhs_handle: ValueHandle, 206 rhs_handle: ValueHandle, 207 ) callconv(.c) noreturn { 208 const lhs: Value = .{ .handle = lhs_handle, .td = data.td }; 209 const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; 210 211 if (rhs.isMinusOne()) { 212 panic(@returnAddress(), "division of {f} by -1 cannot be represented in type {s}", .{ 213 lhs, data.td.getName(), 214 }); 215 } else panic(@returnAddress(), "division by zero", .{}); 216 } 217 218 const AlignmentAssumptionData = extern struct { 219 loc: SourceLocation, 220 assumption_loc: SourceLocation, 221 td: *const TypeDescriptor, 222 }; 223 224 fn alignmentAssumptionHandlerAbort( 225 data: *const AlignmentAssumptionData, 226 pointer: ValueHandle, 227 alignment_handle: ValueHandle, 228 maybe_offset: ?ValueHandle, 229 ) callconv(.c) noreturn { 230 alignmentAssumptionHandler( 231 data, 232 pointer, 233 alignment_handle, 234 maybe_offset, 235 ); 236 } 237 238 fn alignmentAssumptionHandler( 239 data: *const AlignmentAssumptionData, 240 pointer: ValueHandle, 241 alignment_handle: ValueHandle, 242 maybe_offset: ?ValueHandle, 243 ) callconv(.c) noreturn { 244 const real_pointer = @intFromPtr(pointer) - @intFromPtr(maybe_offset); 245 const lsb = @ctz(real_pointer); 246 const actual_alignment = @as(u64, 1) << @intCast(lsb); 247 const mask = @intFromPtr(alignment_handle) - 1; 248 const misalignment_offset = real_pointer & mask; 249 const alignment: Value = .{ .handle = alignment_handle, .td = data.td }; 250 251 if (maybe_offset) |offset| { 252 panic( 253 @returnAddress(), 254 "assumption of {f} byte alignment (with offset of {d} byte) for pointer of type {s} failed\n" ++ 255 "offset address is {d} aligned, misalignment offset is {d} bytes", 256 .{ 257 alignment, 258 @intFromPtr(offset), 259 data.td.getName(), 260 actual_alignment, 261 misalignment_offset, 262 }, 263 ); 264 } else { 265 panic( 266 @returnAddress(), 267 "assumption of {f} byte alignment for pointer of type {s} failed\n" ++ 268 "address is {d} aligned, misalignment offset is {d} bytes", 269 .{ 270 alignment, 271 data.td.getName(), 272 actual_alignment, 273 misalignment_offset, 274 }, 275 ); 276 } 277 } 278 279 const ShiftOobData = extern struct { 280 loc: SourceLocation, 281 lhs_type: *const TypeDescriptor, 282 rhs_type: *const TypeDescriptor, 283 }; 284 285 fn shiftOobAbort( 286 data: *const ShiftOobData, 287 lhs_handle: ValueHandle, 288 rhs_handle: ValueHandle, 289 ) callconv(.c) noreturn { 290 shiftOob(data, lhs_handle, rhs_handle); 291 } 292 293 fn shiftOob( 294 data: *const ShiftOobData, 295 lhs_handle: ValueHandle, 296 rhs_handle: ValueHandle, 297 ) callconv(.c) noreturn { 298 const lhs: Value = .{ .handle = lhs_handle, .td = data.lhs_type }; 299 const rhs: Value = .{ .handle = rhs_handle, .td = data.rhs_type }; 300 301 if (rhs.isNegative() or 302 rhs.getPositiveInteger() >= data.lhs_type.getIntegerSize()) 303 { 304 if (rhs.isNegative()) { 305 panic(@returnAddress(), "shift exponent {f} is negative", .{rhs}); 306 } else { 307 panic( 308 @returnAddress(), 309 "shift exponent {f} is too large for {d}-bit type {s}", 310 .{ rhs, data.lhs_type.getIntegerSize(), data.lhs_type.getName() }, 311 ); 312 } 313 } else { 314 if (lhs.isNegative()) { 315 panic(@returnAddress(), "left shift of negative value {f}", .{lhs}); 316 } else { 317 panic( 318 @returnAddress(), 319 "left shift of {f} by {f} places cannot be represented in type {s}", 320 .{ lhs, rhs, data.lhs_type.getName() }, 321 ); 322 } 323 } 324 } 325 326 const OutOfBoundsData = extern struct { 327 loc: SourceLocation, 328 array_type: *const TypeDescriptor, 329 index_type: *const TypeDescriptor, 330 }; 331 332 fn outOfBoundsAbort( 333 data: *const OutOfBoundsData, 334 index_handle: ValueHandle, 335 ) callconv(.c) noreturn { 336 outOfBounds(data, index_handle); 337 } 338 339 fn outOfBounds( 340 data: *const OutOfBoundsData, 341 index_handle: ValueHandle, 342 ) callconv(.c) noreturn { 343 const index: Value = .{ .handle = index_handle, .td = data.index_type }; 344 panic(@returnAddress(), "index {f} out of bounds for type {s}", .{ 345 index, 346 data.array_type.getName(), 347 }); 348 } 349 350 const PointerOverflowData = extern struct { 351 loc: SourceLocation, 352 }; 353 354 fn pointerOverflowAbort( 355 data: *const PointerOverflowData, 356 base: usize, 357 result: usize, 358 ) callconv(.c) noreturn { 359 pointerOverflow(data, base, result); 360 } 361 362 fn pointerOverflow( 363 _: *const PointerOverflowData, 364 base: usize, 365 result: usize, 366 ) callconv(.c) noreturn { 367 if (base == 0) { 368 if (result == 0) { 369 panic(@returnAddress(), "applying zero offset to null pointer", .{}); 370 } else { 371 panic(@returnAddress(), "applying non-zero offset {d} to null pointer", .{result}); 372 } 373 } else { 374 if (result == 0) { 375 panic( 376 @returnAddress(), 377 "applying non-zero offset to non-null pointer 0x{x} produced null pointer", 378 .{base}, 379 ); 380 } else { 381 const signed_base: isize = @bitCast(base); 382 const signed_result: isize = @bitCast(result); 383 if ((signed_base >= 0) == (signed_result >= 0)) { 384 if (base > result) { 385 panic( 386 @returnAddress(), 387 "addition of unsigned offset to 0x{x} overflowed to 0x{x}", 388 .{ base, result }, 389 ); 390 } else { 391 panic( 392 @returnAddress(), 393 "subtraction of unsigned offset to 0x{x} overflowed to 0x{x}", 394 .{ base, result }, 395 ); 396 } 397 } else { 398 panic( 399 @returnAddress(), 400 "pointer index expression with base 0x{x} overflowed to 0x{x}", 401 .{ base, result }, 402 ); 403 } 404 } 405 } 406 } 407 408 const TypeMismatchData = extern struct { 409 loc: SourceLocation, 410 td: *const TypeDescriptor, 411 log_alignment: u8, 412 kind: enum(u8) { 413 load, 414 store, 415 reference_binding, 416 member_access, 417 member_call, 418 constructor_call, 419 downcast_pointer, 420 downcast_reference, 421 upcast, 422 upcast_to_virtual_base, 423 nonnull_assign, 424 dynamic_operation, 425 426 fn getName(kind: @This()) []const u8 { 427 return switch (kind) { 428 .load => "load of", 429 .store => "store of", 430 .reference_binding => "reference binding to", 431 .member_access => "member access within", 432 .member_call => "member call on", 433 .constructor_call => "constructor call on", 434 .downcast_pointer, .downcast_reference => "downcast of", 435 .upcast => "upcast of", 436 .upcast_to_virtual_base => "cast to virtual base of", 437 .nonnull_assign => "_Nonnull binding to", 438 .dynamic_operation => "dynamic operation on", 439 }; 440 } 441 }, 442 }; 443 444 fn typeMismatchAbort( 445 data: *const TypeMismatchData, 446 pointer: ?ValueHandle, 447 ) callconv(.c) noreturn { 448 typeMismatch(data, pointer); 449 } 450 451 fn typeMismatch( 452 data: *const TypeMismatchData, 453 pointer: ?ValueHandle, 454 ) callconv(.c) noreturn { 455 const alignment = @as(usize, 1) << @intCast(data.log_alignment); 456 const handle: usize = @intFromPtr(pointer); 457 458 if (pointer == null) { 459 panic( 460 @returnAddress(), 461 "{s} null pointer of type {s}", 462 .{ data.kind.getName(), data.td.getName() }, 463 ); 464 } else if (!std.mem.isAligned(handle, alignment)) { 465 panic( 466 @returnAddress(), 467 "{s} misaligned address 0x{x} for type {s}, which requires {d} byte alignment", 468 .{ data.kind.getName(), handle, data.td.getName(), alignment }, 469 ); 470 } else { 471 panic( 472 @returnAddress(), 473 "{s} address 0x{x} with insufficient space for an object of type {s}", 474 .{ data.kind.getName(), handle, data.td.getName() }, 475 ); 476 } 477 } 478 479 const UnreachableData = extern struct { 480 loc: SourceLocation, 481 }; 482 483 fn builtinUnreachable(_: *const UnreachableData) callconv(.c) noreturn { 484 panic(@returnAddress(), "execution reached an unreachable program point", .{}); 485 } 486 487 fn missingReturn(_: *const UnreachableData) callconv(.c) noreturn { 488 panic(@returnAddress(), "execution reached the end of a value-returning function without returning a value", .{}); 489 } 490 491 const NonNullReturnData = extern struct { 492 attribute_loc: SourceLocation, 493 }; 494 495 fn nonNullReturnAbort(data: *const NonNullReturnData, where: *const SourceLocation) callconv(.c) noreturn { 496 nonNullReturn(data, where); 497 } 498 fn nonNullReturn(_: *const NonNullReturnData, _: *const SourceLocation) callconv(.c) noreturn { 499 panic(@returnAddress(), "null pointer returned from function declared to never return null", .{}); 500 } 501 502 const NonNullArgData = extern struct { 503 loc: SourceLocation, 504 attribute_loc: SourceLocation, 505 arg_index: i32, 506 }; 507 508 fn nonNullArgAbort(data: *const NonNullArgData) callconv(.c) noreturn { 509 nonNullArg(data); 510 } 511 512 fn nonNullArg(data: *const NonNullArgData) callconv(.c) noreturn { 513 panic( 514 @returnAddress(), 515 "null pointer passed as argument {d}, which is declared to never be null", 516 .{data.arg_index}, 517 ); 518 } 519 520 const InvalidValueData = extern struct { 521 loc: SourceLocation, 522 td: *const TypeDescriptor, 523 }; 524 525 fn loadInvalidValueAbort( 526 data: *const InvalidValueData, 527 value_handle: ValueHandle, 528 ) callconv(.c) noreturn { 529 loadInvalidValue(data, value_handle); 530 } 531 532 fn loadInvalidValue( 533 data: *const InvalidValueData, 534 value_handle: ValueHandle, 535 ) callconv(.c) noreturn { 536 const value: Value = .{ .handle = value_handle, .td = data.td }; 537 panic(@returnAddress(), "load of value {f}, which is not valid for type {s}", .{ 538 value, data.td.getName(), 539 }); 540 } 541 542 const InvalidBuiltinData = extern struct { 543 loc: SourceLocation, 544 kind: enum(u8) { 545 ctz, 546 clz, 547 }, 548 }; 549 fn invalidBuiltinAbort(data: *const InvalidBuiltinData) callconv(.c) noreturn { 550 invalidBuiltin(data); 551 } 552 553 fn invalidBuiltin(data: *const InvalidBuiltinData) callconv(.c) noreturn { 554 panic( 555 @returnAddress(), 556 "passing zero to {s}(), which is not a valid argument", 557 .{@tagName(data.kind)}, 558 ); 559 } 560 561 const VlaBoundNotPositive = extern struct { 562 loc: SourceLocation, 563 td: *const TypeDescriptor, 564 }; 565 566 fn vlaBoundNotPositiveAbort( 567 data: *const VlaBoundNotPositive, 568 bound_handle: ValueHandle, 569 ) callconv(.c) noreturn { 570 vlaBoundNotPositive(data, bound_handle); 571 } 572 573 fn vlaBoundNotPositive( 574 data: *const VlaBoundNotPositive, 575 bound_handle: ValueHandle, 576 ) callconv(.c) noreturn { 577 const bound: Value = .{ .handle = bound_handle, .td = data.td }; 578 panic(@returnAddress(), "variable length array bound evaluates to non-positive value {f}", .{bound}); 579 } 580 581 const FloatCastOverflowData = extern struct { 582 from: *const TypeDescriptor, 583 to: *const TypeDescriptor, 584 }; 585 586 const FloatCastOverflowDataV2 = extern struct { 587 loc: SourceLocation, 588 from: *const TypeDescriptor, 589 to: *const TypeDescriptor, 590 }; 591 592 fn floatCastOverflowAbort( 593 data_handle: *align(8) const anyopaque, 594 from_handle: ValueHandle, 595 ) callconv(.c) noreturn { 596 floatCastOverflow(data_handle, from_handle); 597 } 598 599 fn floatCastOverflow( 600 data_handle: *align(8) const anyopaque, 601 from_handle: ValueHandle, 602 ) callconv(.c) noreturn { 603 // See: https://github.com/llvm/llvm-project/blob/release/19.x/compiler-rt/lib/ubsan/ubsan_handlers.cpp#L463 604 // for more information on this check. 605 const ptr: [*]const u8 = @ptrCast(data_handle); 606 if (@as(u16, ptr[0]) + @as(u16, ptr[1]) < 2 or ptr[0] == 0xFF or ptr[1] == 0xFF) { 607 const data: *const FloatCastOverflowData = @ptrCast(data_handle); 608 const from_value: Value = .{ .handle = from_handle, .td = data.from }; 609 panic(@returnAddress(), "{f} is outside the range of representable values of type {s}", .{ 610 from_value, data.to.getName(), 611 }); 612 } else { 613 const data: *const FloatCastOverflowDataV2 = @ptrCast(data_handle); 614 const from_value: Value = .{ .handle = from_handle, .td = data.from }; 615 panic(@returnAddress(), "{f} is outside the range of representable values of type {s}", .{ 616 from_value, data.to.getName(), 617 }); 618 } 619 } 620 621 fn exportHandler( 622 handler: anytype, 623 comptime sym_name: []const u8, 624 ) void { 625 @export(handler, .{ 626 .name = "__ubsan_handle_" ++ sym_name, 627 .linkage = .weak, 628 .visibility = .hidden, 629 }); 630 } 631 632 fn exportHandlerWithAbort( 633 handler: anytype, 634 abort_handler: anytype, 635 comptime sym_name: []const u8, 636 ) void { 637 @export(handler, .{ 638 .name = "__ubsan_handle_" ++ sym_name, 639 .linkage = .weak, 640 .visibility = .hidden, 641 }); 642 @export(abort_handler, .{ 643 .name = "__ubsan_handle_" ++ sym_name ++ "_abort", 644 .linkage = .weak, 645 .visibility = .hidden, 646 }); 647 } 648 649 const can_build_ubsan = switch (builtin.zig_backend) { 650 .stage2_loongarch, 651 .stage2_powerpc, 652 .stage2_riscv64, 653 => false, 654 else => true, 655 }; 656 657 comptime { 658 if (can_build_ubsan) { 659 overflowHandler("add_overflow", "+"); 660 overflowHandler("mul_overflow", "*"); 661 overflowHandler("sub_overflow", "-"); 662 exportHandlerWithAbort(&alignmentAssumptionHandler, &alignmentAssumptionHandlerAbort, "alignment_assumption"); 663 664 exportHandlerWithAbort(&divRemHandler, &divRemHandlerAbort, "divrem_overflow"); 665 exportHandlerWithAbort(&floatCastOverflow, &floatCastOverflowAbort, "float_cast_overflow"); 666 exportHandlerWithAbort(&invalidBuiltin, &invalidBuiltinAbort, "invalid_builtin"); 667 exportHandlerWithAbort(&loadInvalidValue, &loadInvalidValueAbort, "load_invalid_value"); 668 669 exportHandlerWithAbort(&negationHandler, &negationHandlerAbort, "negate_overflow"); 670 exportHandlerWithAbort(&nonNullArg, &nonNullArgAbort, "nonnull_arg"); 671 exportHandlerWithAbort(&nonNullReturn, &nonNullReturnAbort, "nonnull_return_v1"); 672 exportHandlerWithAbort(&outOfBounds, &outOfBoundsAbort, "out_of_bounds"); 673 exportHandlerWithAbort(&pointerOverflow, &pointerOverflowAbort, "pointer_overflow"); 674 exportHandlerWithAbort(&shiftOob, &shiftOobAbort, "shift_out_of_bounds"); 675 exportHandlerWithAbort(&typeMismatch, &typeMismatchAbort, "type_mismatch_v1"); 676 exportHandlerWithAbort(&vlaBoundNotPositive, &vlaBoundNotPositiveAbort, "vla_bound_not_positive"); 677 678 exportHandler(&builtinUnreachable, "builtin_unreachable"); 679 exportHandler(&missingReturn, "missing_return"); 680 } 681 682 // these checks are nearly impossible to replicate in zig, as they rely on nuances 683 // in the Itanium C++ ABI. 684 // exportHandlerWithAbort(&dynamicTypeCacheMiss, &dynamicTypeCacheMissAbort, "dynamic-type-cache-miss"); 685 // exportHandlerWithAbort(&vptrTypeCache, &vptrTypeCacheAbort, "vptr-type-cache"); 686 687 // we disable -fsanitize=function for reasons explained in src/Compilation.zig 688 // exportHandlerWithAbort(&functionTypeMismatch, &functionTypeMismatchAbort, "function-type-mismatch"); 689 // exportHandlerWithAbort(&functionTypeMismatchV1, &functionTypeMismatchV1Abort, "function-type-mismatch-v1"); 690 } 691