diff --git a/CMakeLists.txt b/CMakeLists.txt index b41640e72a..012ad075aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,11 +439,10 @@ set(ZIG_STD_FILES "os/darwin_errno.zig" "os/get_user_id.zig" "os/index.zig" - "os/linux.zig" - "os/linux_errno.zig" - "os/linux_random.zig" - "os/linux_i386.zig" - "os/linux_x86_64.zig" + "os/linux/index.zig" + "os/linux/errno.zig" + "os/linux/i386.zig" + "os/linux/x86_64.zig" "os/path.zig" "os/windows/error.zig" "os/windows/index.zig" diff --git a/src/ir.cpp b/src/ir.cpp index 9d5f59d187..1dc717286a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6193,6 +6193,15 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc if (other_type->id == TypeTableEntryIdFloat) { return true; } else if (other_type->id == TypeTableEntryIdInt && const_val_is_int) { + if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + ir_add_error(ira, instruction, + buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", + buf_ptr(val_buf), + buf_ptr(&other_type->name))); + return false; + } if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count, other_type->data.integral.is_signed)) { @@ -6205,6 +6214,15 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc if (const_val_fits_in_num_lit(const_val, child_type)) { return true; } else if (child_type->id == TypeTableEntryIdInt && const_val_is_int) { + if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + ir_add_error(ira, instruction, + buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", + buf_ptr(val_buf), + buf_ptr(&child_type->name))); + return false; + } if (bigint_fits_in_bits(&const_val->data.x_bigint, child_type->data.integral.bit_count, child_type->data.integral.is_signed)) diff --git a/std/c/linux.zig b/std/c/linux.zig index 6378955795..b2ac05eba5 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,4 +1,4 @@ -pub use @import("../os/linux_errno.zig"); +pub use @import("../os/linux/errno.zig"); pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() &c_int; diff --git a/std/io.zig b/std/io.zig index 41f32622f8..2fe57e4dfe 100644 --- a/std/io.zig +++ b/std/io.zig @@ -2,7 +2,7 @@ const std = @import("index.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const system = switch(builtin.os) { - Os.linux => @import("os/linux.zig"), + Os.linux => @import("os/linux/index.zig"), Os.macosx, Os.ios => @import("os/darwin.zig"), Os.windows => @import("os/windows/index.zig"), else => @compileError("Unsupported OS"), diff --git a/std/os/index.zig b/std/os/index.zig index 00a0d7b94c..ed1e262f82 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -6,7 +6,7 @@ const os = this; pub const windows = @import("windows/index.zig"); pub const darwin = @import("darwin.zig"); -pub const linux = @import("linux.zig"); +pub const linux = @import("linux/index.zig"); pub const zen = @import("zen.zig"); pub const posix = switch(builtin.os) { Os.linux => linux, @@ -78,13 +78,28 @@ error WouldBlock; pub fn getRandomBytes(buf: []u8) %void { switch (builtin.os) { Os.linux => while (true) { - const err = posix.getErrno(posix.getRandomBytes(buf)); - if (err > 0) return unexpectedErrorPosix(err); + // TODO check libc version and potentially call c.getrandom. + // See #397 + const err = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); + if (err > 0) { + switch (err) { + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EINTR => continue, + posix.ENOSYS => { + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); + defer close(fd); + + try posixRead(fd, buf); + return; + }, + else => return unexpectedErrorPosix(err), + } + } return; }, Os.macosx, Os.ios => { - const fd = try posixOpen("/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, - 0, null); + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); defer close(fd); try posixRead(fd, buf); @@ -253,8 +268,12 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al mem.copy(u8, path0, file_path); path0[file_path.len] = 0; + return posixOpenC(path0.ptr, flags, perm); +} + +pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) %i32 { while (true) { - const result = posix.open(path0.ptr, flags, perm); + const result = posix.open(file_path, flags, perm); const err = posix.getErrno(result); if (err > 0) { return switch (err) { @@ -1486,10 +1505,10 @@ test "std.os" { _ = @import("darwin_errno.zig"); _ = @import("darwin.zig"); _ = @import("get_user_id.zig"); - _ = @import("linux_errno.zig"); + _ = @import("linux/errno.zig"); //_ = @import("linux_i386.zig"); - _ = @import("linux_x86_64.zig"); - _ = @import("linux.zig"); + _ = @import("linux/x86_64.zig"); + _ = @import("linux/index.zig"); _ = @import("path.zig"); _ = @import("windows/index.zig"); } diff --git a/std/os/linux_errno.zig b/std/os/linux/errno.zig similarity index 100% rename from std/os/linux_errno.zig rename to std/os/linux/errno.zig diff --git a/std/os/linux_i386.zig b/std/os/linux/i386.zig similarity index 99% rename from std/os/linux_i386.zig rename to std/os/linux/i386.zig index 353461562b..691377e24e 100644 --- a/std/os/linux_i386.zig +++ b/std/os/linux/i386.zig @@ -1,4 +1,5 @@ -const linux = @import("linux.zig"); +const std = @import("../../index.zig"); +const linux = std.os.linux; const socklen_t = linux.socklen_t; const iovec = linux.iovec; diff --git a/std/os/linux.zig b/std/os/linux/index.zig similarity index 98% rename from std/os/linux.zig rename to std/os/linux/index.zig index 79ae623a1c..43d175b74d 100644 --- a/std/os/linux.zig +++ b/std/os/linux/index.zig @@ -1,13 +1,12 @@ -const std = @import("../index.zig"); +const std = @import("../../index.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); const arch = switch (builtin.arch) { - builtin.Arch.x86_64 => @import("linux_x86_64.zig"), - builtin.Arch.i386 => @import("linux_i386.zig"), + builtin.Arch.x86_64 => @import("x86_64.zig"), + builtin.Arch.i386 => @import("i386.zig"), else => @compileError("unsupported arch"), }; -pub use @import("linux_errno.zig"); -pub use @import("linux_random.zig"); +pub use @import("errno.zig"); pub const PATH_MAX = 4096; @@ -788,10 +787,10 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va return arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); } -test "import linux_test" { +test "import linux test" { // TODO lazy analysis should prevent this test from being compiled on windows, but // it is still compiled on windows if (builtin.os == builtin.Os.linux) { - _ = @import("linux_test.zig"); + _ = @import("test.zig"); } } diff --git a/std/os/linux_test.zig b/std/os/linux/test.zig similarity index 96% rename from std/os/linux_test.zig rename to std/os/linux/test.zig index 265d0a17f9..c7dbeab67f 100644 --- a/std/os/linux_test.zig +++ b/std/os/linux/test.zig @@ -1,4 +1,4 @@ -const std = @import("std"); +const std = @import("../../index.zig"); const linux = std.os.linux; const assert = std.debug.assert; diff --git a/std/os/linux_x86_64.zig b/std/os/linux/x86_64.zig similarity index 99% rename from std/os/linux_x86_64.zig rename to std/os/linux/x86_64.zig index 3706633745..3a76ca4f87 100644 --- a/std/os/linux_x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -1,4 +1,5 @@ -const linux = @import("linux.zig"); +const std = @import("../../index.zig"); +const linux = std.os.linux; const socklen_t = linux.socklen_t; const iovec = linux.iovec; diff --git a/std/os/linux_random.zig b/std/os/linux_random.zig deleted file mode 100644 index 0fb10d1ee9..0000000000 --- a/std/os/linux_random.zig +++ /dev/null @@ -1,246 +0,0 @@ -const std = @import("../index.zig"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const linux = std.os.linux; -const math = std.math; -const mem = std.mem; -const os = std.os; - -use @import("linux_errno.zig"); - -const arch = switch (builtin.arch) { - builtin.Arch.x86_64 => @import("linux_x86_64.zig"), - builtin.Arch.i386 => @import("linux_i386.zig"), - else => @compileError("unsupported arch"), -}; - -const Method = enum { - Syscall, - Sysctl, - Urandom, -}; - -const Callback = fn(&i32, []u8) usize; - -const Context = struct { - syscall: Callback, - sysctl: Callback, - urandom: Callback, -}; - -pub fn getRandomBytes(buf: []u8) usize { - const ctx = Context { - .syscall = syscall, - .sysctl = sysctl, - .urandom = urandom, - }; - return withContext(ctx, buf); -} - -fn withContext(comptime ctx: Context, buf: []u8) usize { - if (buf.len == 0) return 0; - - var fd: i32 = -1; - defer if (fd != -1) { - const _ = linux.close(fd); // Ignore errors, can't do anything sensible. - }; - - // TODO(bnoordhuis) Remember the method across invocations so we don't make - // unnecessary system calls that are going to fail with ENOSYS anyway. - var method = Method.Syscall; - var i: usize = 0; - while (i < buf.len) { - const rc = switch (method) { - Method.Syscall => ctx.syscall(&fd, buf[i..]), - Method.Sysctl => ctx.sysctl(&fd, buf[i..]), - Method.Urandom => ctx.urandom(&fd, buf[i..]), - }; - if (rc == 0) return usize(-EIO); // Can't really happen. - if (!isErr(rc)) { - i += rc; - continue; - } - if (rc == usize(-EINTR)) continue; - if (rc == usize(-ENOSYS) and method == Method.Syscall) { - method = Method.Urandom; - continue; - } - if (method == Method.Urandom) { - method = Method.Sysctl; - continue; - } - return rc; // Unexpected error. - } - - return i; -} - -fn syscall(_: &i32, buf: []u8) usize { - return arch.syscall3(arch.SYS_getrandom, @ptrToInt(&buf[0]), buf.len, 0); -} - -// Note: reads only 14 bytes at a time. -fn sysctl(_: &i32, buf: []u8) usize { - const __sysctl_args = extern struct { - name: &c_int, - nlen: c_int, - oldval: &u8, - oldlenp: &usize, - newval: ?&u8, - newlen: usize, - unused: [4]usize, - }; - - var name = [3]c_int { 1, 40, 6 }; // { CTL_KERN, KERN_RANDOM, RANDOM_UUID } - var uuid: [16]u8 = undefined; - - const expected: usize = @sizeOf(@typeOf(uuid)); - var len = expected; - - var args = __sysctl_args { - .name = &name[0], - .nlen = c_int(name.len), - .oldval = &uuid[0], - .oldlenp = &len, - .newval = null, - .newlen = 0, - .unused = []usize {0} ** 4, - }; - - const rc = arch.syscall1(arch.SYS__sysctl, @ptrToInt(&args)); - if (rc != 0) return rc; - if (len != expected) return 0; // Can't happen. - - // uuid[] is now a type 4 UUID; bytes 6 and 8 (counting from zero) - // contain 4 and 5 bits of entropy, respectively. For ease of use, - // we skip those and only use 14 of the 16 bytes. - uuid[6] = uuid[14]; - uuid[8] = uuid[15]; - - const n = math.min(buf.len, usize(14)); - @memcpy(&buf[0], &uuid[0], n); - return n; -} - -fn urandom(fd: &i32, buf: []u8) usize { - if (*fd == -1) { - const flags = linux.O_CLOEXEC|linux.O_RDONLY; - const rc = linux.open(c"/dev/urandom", flags, 0); - if (isErr(rc)) return rc; - *fd = i32(rc); - } - // read() doesn't like reads > INT_MAX. - const n = math.min(buf.len, usize(0x7FFFFFFF)); - return linux.read(*fd, &buf[0], n); -} - -fn isErr(rc: usize) bool { - return rc > usize(-4096); -} - -test "os.linux.getRandomBytes" { - try check(42, getRandomBytesTrampoline); -} - -test "os.linux.getRandomBytes syscall" { - try check(42, syscall); -} - -test "os.linux.getRandomBytes sysctl" { - try check(14, sysctl); -} - -test "os.linux.getRandomBytes /dev/urandom" { - try check(42, urandom); -} - -test "os.linux.getRandomBytes state machine" { - const ctx = Context { - .syscall = fortytwo, - .urandom = fail, - .sysctl = fail, - }; - var buf = []u8 {0}; - assert(1 == withContext(ctx, buf[0..])); - assert(42 == buf[0]); -} - -test "os.linux.getRandomBytes no-syscall state machine" { - const ctx = Context { - .syscall = enosys, - .urandom = fortytwo, - .sysctl = fail, - }; - var buf = []u8 {0}; - assert(1 == withContext(ctx, buf[0..])); - assert(42 == buf[0]); -} - -test "os.linux.getRandomBytes no-urandom state machine" { - const ctx = Context { - .syscall = enosys, - .urandom = einval, - .sysctl = fortytwo, - }; - var buf = []u8 {0}; - assert(1 == withContext(ctx, buf[0..])); - assert(42 == buf[0]); -} - -test "os.linux.getRandomBytes no-sysctl state machine" { - const ctx = Context { - .syscall = enosys, - .urandom = einval, - .sysctl = einval, - }; - var buf = []u8 {0}; - assert(usize(-EINVAL) == withContext(ctx, buf[0..])); - assert(0 == buf[0]); -} - -fn einval(_: &i32, buf: []u8) usize { - return usize(-EINVAL); -} - -fn enosys(_: &i32, buf: []u8) usize { - return usize(-ENOSYS); -} - -fn fail(_: &i32, buf: []u8) usize { - os.abort(); -} - -fn fortytwo(_: &i32, buf: []u8) usize { - assert(buf.len == 1); - buf[0] = 42; - return 1; -} - -fn check(comptime N: usize, cb: Callback) %void { - if (builtin.os == builtin.Os.linux) { - var fd: i32 = -1; - defer if (fd != -1) { - const _ = linux.close(fd); // Ignore errors, can't do anything sensible. - }; - - var bufs = [3][N]u8 { - []u8 {0} ** N, - []u8 {0} ** N, - []u8 {0} ** N, - }; - - for (bufs) |*buf| { - const err = cb(&fd, (*buf)[0..]); - assert(err == N); - } - - for (bufs) |*a| - for (bufs) |*b| - if (a != b) - assert(!mem.eql(u8, *a, *b)); - } -} - -fn getRandomBytesTrampoline(_: &i32, buf: []u8) usize { - return getRandomBytes(buf); -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3267ea7435..92066d7e0e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,12 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("cast negative integer literal to usize", + \\export fn entry() void { + \\ const x = usize(-10); + \\} + , ".tmp_source.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'"); + cases.add("use invalid number literal as array index", \\var v = 25; \\export fn entry() void {