zig/test/runtime_safety.zig
Andrew Kelley 537f905216 fix bad runtime safety test cases
The "slicing operator with sentinel" runtime safety test cases should
all have been compile errors in their current forms. In this commit I
adjust them to use runtime-known slices before triggering the runtime
safety.

Furthermore the test cases did not actually have unique names.
2022-05-12 21:15:41 -07:00

1207 lines
39 KiB
Zig

const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
{
const check_panic_msg =
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "reached unreachable code")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
;
cases.addRuntimeSafety("switch on corrupted enum value",
\\const std = @import("std");
++ check_panic_msg ++
\\const E = enum(u32) {
\\ X = 1,
\\};
\\pub fn main() void {
\\ var e: E = undefined;
\\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
\\ switch (e) {
\\ .X => @breakpoint(),
\\ }
\\}
);
cases.addRuntimeSafety("switch on corrupted union value",
\\const std = @import("std");
++ check_panic_msg ++
\\const U = union(enum(u32)) {
\\ X: u8,
\\};
\\pub fn main() void {
\\ var u: U = undefined;
\\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
\\ switch (u) {
\\ .X => @breakpoint(),
\\ }
\\}
);
}
{
const check_panic_msg =
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "invalid enum value")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
;
cases.addRuntimeSafety("@tagName on corrupted enum value",
\\const std = @import("std");
++ check_panic_msg ++
\\const E = enum(u32) {
\\ X = 1,
\\};
\\pub fn main() void {
\\ var e: E = undefined;
\\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
\\ var n = @tagName(e);
\\ _ = n;
\\}
);
cases.addRuntimeSafety("@tagName on corrupted union value",
\\const std = @import("std");
++ check_panic_msg ++
\\const U = union(enum(u32)) {
\\ X: u8,
\\};
\\pub fn main() void {
\\ var u: U = undefined;
\\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
\\ var t: @typeInfo(U).Union.tag_type.? = u;
\\ var n = @tagName(t);
\\ _ = n;
\\}
);
}
{
const check_panic_msg =
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "index out of bounds")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
;
cases.addRuntimeSafety("slice with sentinel out of bounds",
\\const std = @import("std");
++ check_panic_msg ++
\\pub fn main() void {
\\ var buf = [4]u8{'a','b','c',0};
\\ const input: []u8 = &buf;
\\ const slice = input[0..4 :0];
\\ _ = slice;
\\}
);
cases.addRuntimeSafety("empty slice with sentinel out of bounds",
\\const std = @import("std");
++ check_panic_msg ++
\\pub fn main() void {
\\ var buf_zero = [0]u8{};
\\ const input: []u8 = &buf_zero;
\\ const slice = input[0..0 :0];
\\ _ = slice;
\\}
);
}
cases.addRuntimeSafety("truncating vector cast",
\\const std = @import("std");
\\const V = @import("std").meta.Vector;
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "integer cast truncated bits")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x = @splat(4, @as(u32, 0xdeadbeef));
\\ var y = @intCast(V(4, u16), x);
\\ _ = y;
\\}
);
cases.addRuntimeSafety("unsigned-signed vector cast",
\\const std = @import("std");
\\const V = @import("std").meta.Vector;
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "integer cast truncated bits")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x = @splat(4, @as(u32, 0x80000000));
\\ var y = @intCast(V(4, i32), x);
\\ _ = y;
\\}
);
cases.addRuntimeSafety("signed-unsigned vector cast",
\\const std = @import("std");
\\const V = @import("std").meta.Vector;
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x = @splat(4, @as(i32, -2147483647));
\\ var y = @intCast(V(4, u32), x);
\\ _ = y;
\\}
);
cases.addRuntimeSafety("shift left by huge amount",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x: u24 = 42;
\\ var y: u5 = 24;
\\ var z = x >> y;
\\ _ = z;
\\}
);
cases.addRuntimeSafety("shift right by huge amount",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x: u24 = 42;
\\ var y: u5 = 24;
\\ var z = x << y;
\\ _ = z;
\\}
);
cases.addRuntimeSafety("slice sentinel mismatch - optional pointers",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var buf: [4]?*i32 = undefined;
\\ const slice = buf[0..3 :null];
\\ _ = slice;
\\}
);
cases.addRuntimeSafety("slice sentinel mismatch - floats",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var buf: [4]f32 = undefined;
\\ const slice = buf[0..3 :1.2];
\\ _ = slice;
\\}
);
cases.addRuntimeSafety("pointer slice sentinel mismatch",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var buf: [4]u8 = undefined;
\\ const ptr: [*]u8 = &buf;
\\ const slice = ptr[0..3 :0];
\\ _ = slice;
\\}
);
cases.addRuntimeSafety("slice slice sentinel mismatch",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var buf: [4]u8 = undefined;
\\ const slice = buf[0..];
\\ const slice2 = slice[0..3 :0];
\\ _ = slice2;
\\}
);
cases.addRuntimeSafety("array slice sentinel mismatch",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
\\ std.process.exit(126); // good
\\ }
\\ std.process.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var buf: [4]u8 = undefined;
\\ const slice = buf[0..3 :0];
\\ _ = slice;
\\}
);
cases.addRuntimeSafety("intToPtr with misaligned address",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "incorrect alignment")) {
\\ std.os.exit(126); // good
\\ }
\\ std.os.exit(0); // test failed
\\}
\\pub fn main() void {
\\ var x: usize = 5;
\\ var y = @intToPtr([*]align(4) u8, x);
\\ _ = y;
\\}
);
cases.addRuntimeSafety("resuming a non-suspended function which never been suspended",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\fn foo() void {
\\ var f = async bar(@frame());
\\ _ = f;
\\ std.os.exit(0);
\\}
\\
\\fn bar(frame: anyframe) void {
\\ suspend {
\\ resume frame;
\\ }
\\ std.os.exit(0);
\\}
\\
\\pub fn main() void {
\\ _ = async foo();
\\}
);
cases.addRuntimeSafety("resuming a non-suspended function which has been suspended and resumed",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\fn foo() void {
\\ suspend {
\\ global_frame = @frame();
\\ }
\\ var f = async bar(@frame());
\\ _ = f;
\\ std.os.exit(0);
\\}
\\
\\fn bar(frame: anyframe) void {
\\ suspend {
\\ resume frame;
\\ }
\\ std.os.exit(0);
\\}
\\
\\var global_frame: anyframe = undefined;
\\pub fn main() void {
\\ _ = async foo();
\\ resume global_frame;
\\ std.os.exit(0);
\\}
);
cases.addRuntimeSafety("nosuspend function call, callee suspends",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ _ = nosuspend add(101, 100);
\\}
\\fn add(a: i32, b: i32) i32 {
\\ if (a > 100) {
\\ suspend {}
\\ }
\\ return a + b;
\\}
);
cases.addRuntimeSafety("awaiting twice",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\var frame: anyframe = undefined;
\\
\\pub fn main() void {
\\ _ = async amain();
\\ resume frame;
\\}
\\
\\fn amain() void {
\\ var f = async func();
\\ await f;
\\ await f;
\\}
\\
\\fn func() void {
\\ suspend {
\\ frame = @frame();
\\ }
\\}
);
cases.addRuntimeSafety("@asyncCall with too small a frame",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var bytes: [1]u8 align(16) = undefined;
\\ var ptr = other;
\\ var frame = @asyncCall(&bytes, {}, ptr, .{});
\\ _ = frame;
\\}
\\fn other() callconv(.Async) void {
\\ suspend {}
\\}
);
cases.addRuntimeSafety("resuming a function which is awaiting a frame",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var frame = async first();
\\ resume frame;
\\}
\\fn first() void {
\\ var frame = async other();
\\ await frame;
\\}
\\fn other() void {
\\ suspend {}
\\}
);
cases.addRuntimeSafety("resuming a function which is awaiting a call",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var frame = async first();
\\ resume frame;
\\}
\\fn first() void {
\\ other();
\\}
\\fn other() void {
\\ suspend {}
\\}
);
cases.addRuntimeSafety("invalid resume of async function",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var p = async suspendOnce();
\\ resume p; //ok
\\ resume p; //bad
\\}
\\fn suspendOnce() void {
\\ suspend {}
\\}
);
cases.addRuntimeSafety(".? operator on null pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var ptr: ?*i32 = null;
\\ var b = ptr.?;
\\ _ = b;
\\}
);
cases.addRuntimeSafety(".? operator on C pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var ptr: [*c]i32 = null;
\\ var b = ptr.?;
\\ _ = b;
\\}
);
cases.addRuntimeSafety("@intToPtr address zero to non-optional pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var zero: usize = 0;
\\ var b = @intToPtr(*i32, zero);
\\ _ = b;
\\}
);
cases.addRuntimeSafety("@intToPtr address zero to non-optional byte-aligned pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var zero: usize = 0;
\\ var b = @intToPtr(*u8, zero);
\\ _ = b;
\\}
);
cases.addRuntimeSafety("pointer casting null to non-optional pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var c_ptr: [*c]u8 = 0;
\\ var zig_ptr: *u8 = c_ptr;
\\ _ = zig_ptr;
\\}
);
cases.addRuntimeSafety("@intToEnum - no matching tag value",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\const Foo = enum {
\\ A,
\\ B,
\\ C,
\\};
\\pub fn main() void {
\\ baz(bar(3));
\\}
\\fn bar(a: u2) Foo {
\\ return @intToEnum(Foo, a);
\\}
\\fn baz(_: Foo) void {}
);
cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ baz(bar(-1.1));
\\}
\\fn bar(a: f32) u8 {
\\ return @floatToInt(u8, a);
\\}
\\fn baz(_: u8) void { }
);
cases.addRuntimeSafety("@floatToInt cannot fit - negative out of range",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ baz(bar(-129.1));
\\}
\\fn bar(a: f32) i8 {
\\ return @floatToInt(i8, a);
\\}
\\fn baz(_: i8) void { }
);
cases.addRuntimeSafety("@floatToInt cannot fit - positive out of range",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ baz(bar(256.2));
\\}
\\fn bar(a: f32) u8 {
\\ return @floatToInt(u8, a);
\\}
\\fn baz(_: u8) void { }
);
cases.addRuntimeSafety("calling panic",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ @panic("oh no");
\\}
);
cases.addRuntimeSafety("out of bounds slice access",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ const a = [_]i32{1, 2, 3, 4};
\\ baz(bar(&a));
\\}
\\fn bar(a: []const i32) i32 {
\\ return a[4];
\\}
\\fn baz(_: i32) void { }
);
cases.addRuntimeSafety("integer addition overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = add(65530, 10);
\\ if (x == 0) return error.Whatever;
\\}
\\fn add(a: u16, b: u16) u16 {
\\ return a + b;
\\}
);
cases.addRuntimeSafety("vector integer addition overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var a: @Vector(4, i32) = [_]i32{ 1, 2, 2147483643, 4 };
\\ var b: @Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
\\ const x = add(a, b);
\\ _ = x;
\\}
\\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
\\ return a + b;
\\}
);
cases.addRuntimeSafety("vector integer subtraction overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var a: @Vector(4, u32) = [_]u32{ 1, 2, 8, 4 };
\\ var b: @Vector(4, u32) = [_]u32{ 5, 6, 7, 8 };
\\ const x = sub(b, a);
\\ _ = x;
\\}
\\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) {
\\ return a - b;
\\}
);
cases.addRuntimeSafety("vector integer multiplication overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var a: @Vector(4, u8) = [_]u8{ 1, 2, 200, 4 };
\\ var b: @Vector(4, u8) = [_]u8{ 5, 6, 2, 8 };
\\ const x = mul(b, a);
\\ _ = x;
\\}
\\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) {
\\ return a * b;
\\}
);
cases.addRuntimeSafety("vector integer negation overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var a: @Vector(4, i16) = [_]i16{ 1, -32768, 200, 4 };
\\ const x = neg(a);
\\ _ = x;
\\}
\\fn neg(a: @Vector(4, i16)) @Vector(4, i16) {
\\ return -a;
\\}
);
cases.addRuntimeSafety("integer subtraction overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = sub(10, 20);
\\ if (x == 0) return error.Whatever;
\\}
\\fn sub(a: u16, b: u16) u16 {
\\ return a - b;
\\}
);
cases.addRuntimeSafety("integer multiplication overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = mul(300, 6000);
\\ if (x == 0) return error.Whatever;
\\}
\\fn mul(a: u16, b: u16) u16 {
\\ return a * b;
\\}
);
cases.addRuntimeSafety("integer negation overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = neg(-32768);
\\ if (x == 32767) return error.Whatever;
\\}
\\fn neg(a: i16) i16 {
\\ return -a;
\\}
);
cases.addRuntimeSafety("signed integer division overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = div(-32768, -1);
\\ if (x == 32767) return error.Whatever;
\\}
\\fn div(a: i16, b: i16) i16 {
\\ return @divTrunc(a, b);
\\}
);
cases.addRuntimeSafety("signed integer division overflow - vectors",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 };
\\ var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 };
\\ const x = div(a, b);
\\ if (x[2] == 32767) return error.Whatever;
\\}
\\fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {
\\ return @divTrunc(a, b);
\\}
);
cases.addRuntimeSafety("signed shift left overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shl(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: i16, b: u4) i16 {
\\ return @shlExact(a, b);
\\}
);
cases.addRuntimeSafety("unsigned shift left overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shl(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: u16, b: u4) u16 {
\\ return @shlExact(a, b);
\\}
);
cases.addRuntimeSafety("signed shift right overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shr(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: i16, b: u4) i16 {
\\ return @shrExact(a, b);
\\}
);
cases.addRuntimeSafety("unsigned shift right overflow",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shr(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: u16, b: u4) u16 {
\\ return @shrExact(a, b);
\\}
);
cases.addRuntimeSafety("integer division by zero",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ const x = div0(999, 0);
\\ _ = x;
\\}
\\fn div0(a: i32, b: i32) i32 {
\\ return @divTrunc(a, b);
\\}
);
cases.addRuntimeSafety("integer division by zero - vectors",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444};
\\ var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444};
\\ const x = div0(a, b);
\\ _ = x;
\\}
\\fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
\\ return @divTrunc(a, b);
\\}
);
cases.addRuntimeSafety("exact division failure",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = divExact(10, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn divExact(a: i32, b: i32) i32 {
\\ return @divExact(a, b);
\\}
);
cases.addRuntimeSafety("exact division failure - vectors",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444};
\\ var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441};
\\ const x = divExact(a, b);
\\ _ = x;
\\}
\\fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
\\ return @divExact(a, b);
\\}
);
cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = widenSlice(&[_]u8{1, 2, 3, 4, 5});
\\ if (x.len == 0) return error.Whatever;
\\}
\\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 {
\\ return std.mem.bytesAsSlice(i32, slice);
\\}
);
cases.addRuntimeSafety("value does not fit in shortening cast",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shorten_cast(200);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shorten_cast(x: i32) i8 {
\\ return @intCast(i8, x);
\\}
);
cases.addRuntimeSafety("value does not fit in shortening cast - u0",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = shorten_cast(1);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shorten_cast(x: u8) u0 {
\\ return @intCast(u0, x);
\\}
);
cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ const x = unsigned_cast(-10);
\\ if (x == 0) return error.Whatever;
\\}
\\fn unsigned_cast(x: i32) u32 {
\\ return @intCast(u32, x);
\\}
);
cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer - widening",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var value: c_short = -1;
\\ var casted = @intCast(u32, value);
\\ _ = casted;
\\}
);
cases.addRuntimeSafety("unsigned integer not fitting in cast to signed integer - same bit count",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ var value: u8 = 245;
\\ var casted = @intCast(i8, value);
\\ _ = casted;
\\}
);
cases.addRuntimeSafety("unwrap error",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = stack_trace;
\\ if (std.mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
\\ std.os.exit(126); // good
\\ }
\\ std.os.exit(0); // test failed
\\}
\\pub fn main() void {
\\ bar() catch unreachable;
\\}
\\fn bar() !void {
\\ return error.Whatever;
\\}
);
cases.addRuntimeSafety("cast integer to global error and no code matches",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() void {
\\ bar(9999) catch {};
\\}
\\fn bar(x: u16) anyerror {
\\ return @intToError(x);
\\}
);
cases.addRuntimeSafety("@errSetCast error not present in destination",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\const Set1 = error{A, B};
\\const Set2 = error{A, C};
\\pub fn main() void {
\\ foo(Set1.B) catch {};
\\}
\\fn foo(set1: Set1) Set2 {
\\ return @errSetCast(Set2, set1);
\\}
);
cases.addRuntimeSafety("@alignCast misaligned",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\pub fn main() !void {
\\ var array align(4) = [_]u32{0x11111111, 0x11111111};
\\ const bytes = std.mem.sliceAsBytes(array[0..]);
\\ if (foo(bytes) != 0x11111111) return error.Wrong;
\\}
\\fn foo(bytes: []u8) u32 {
\\ const slice4 = bytes[1..5];
\\ const int_slice = std.mem.bytesAsSlice(u32, @alignCast(4, slice4));
\\ return int_slice[0];
\\}
);
cases.addRuntimeSafety("bad union field access",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\
\\const Foo = union {
\\ float: f32,
\\ int: u32,
\\};
\\
\\pub fn main() void {
\\ var f = Foo { .int = 42 };
\\ bar(&f);
\\}
\\
\\fn bar(f: *Foo) void {
\\ f.float = 12.34;
\\}
);
// @intCast a runtime integer to u0 actually results in a comptime-known value,
// but we still emit a safety check to ensure the integer was 0 and thus
// did not truncate information.
cases.addRuntimeSafety("@intCast to u0",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\
\\pub fn main() void {
\\ bar(1, 1);
\\}
\\
\\fn bar(one: u1, not_zero: i32) void {
\\ var x = one << @intCast(u0, not_zero);
\\ _ = x;
\\}
);
// This case makes sure that the code compiles and runs. There is not actually a special
// runtime safety check having to do specifically with error return traces across suspend points.
cases.addRuntimeSafety("error return trace across suspend points",
\\const std = @import("std");
\\
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\
\\var failing_frame: @Frame(failing) = undefined;
\\
\\pub fn main() void {
\\ const p = nonFailing();
\\ resume p;
\\ const p2 = async printTrace(p);
\\ _ = p2;
\\}
\\
\\fn nonFailing() anyframe->anyerror!void {
\\ failing_frame = async failing();
\\ return &failing_frame;
\\}
\\
\\fn failing() anyerror!void {
\\ suspend {}
\\ return second();
\\}
\\
\\fn second() callconv(.Async) anyerror!void {
\\ return error.Fail;
\\}
\\
\\fn printTrace(p: anyframe->anyerror!void) void {
\\ (await p) catch unreachable;
\\}
);
// Slicing a C pointer returns a non-allowzero slice, thus we need to emit
// a safety check to ensure the pointer is not null.
cases.addRuntimeSafety("slicing null C pointer",
\\const std = @import("std");
\\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
\\ _ = message;
\\ _ = stack_trace;
\\ std.os.exit(126);
\\}
\\
\\pub fn main() void {
\\ var ptr: [*c]const u32 = null;
\\ var slice = ptr[0..3];
\\ _ = slice;
\\}
);
}