Panic: integer does not fit in destination type

Hi all, I am working on a Zig program to create a container using clone() and waitpid() systems calls. However as soon as I pass the clone flag: linux.CLONE.NEWUTS into the clone() system call the program crashes with the integer overflow error. I have pasted the program and the error below. Any hint on what’s causing the error is greatly appreciated.

const std = @import("std");
const linux = std.os.linux;

const Arg = struct {
    n: u8,
    buf: []const u8,

    fn init(n: u8, buf: []const u8) Arg {
        return Arg{ .n = n, .buf = buf };
    }
};

fn child(arg: usize) callconv(.c) u8 {
    const input: *Arg = @ptrFromInt(arg);
    const tid = linux.gettid();
    const pid = linux.getpid();
    const ppid = linux.getppid();

    std.debug.print("child:  tid: {}, pid: {}, ppid: {}, n = {d}, args = {s}\n", .{ tid, pid, ppid, input.n, input.buf });

    return 0;
}

pub fn main() !void {
    var fd: [2]i32 = undefined;
    const pres = std.os.linux.pipe(&fd);

    if (pres == -1) {
        std.debug.print("error: pipe\n", .{});
        return error.SyscallError;
    }

    std.debug.print("pipe {d}, reader_fd: {d}, writer_fd:{d}\n", .{ pres, fd[0], fd[1] });

    const stack_size: usize = 8 * 1024;
    const stack_memory = try std.heap.page_allocator.alloc(u8, stack_size);
    defer std.heap.page_allocator.free(stack_memory);

    const stack_ptr = @intFromPtr(stack_memory.ptr + stack_size);
    const clone_flags = linux.CLONE.VM | linux.SIG.CHLD | linux.CLONE.NEWUTS; // error from here with linux.CLONE.NEWUTS flag.

    const arg = Arg.init(4, "go build -o main");
    const pid = linux.clone(
        child,
        stack_ptr,
        clone_flags,
        @intFromPtr(&arg),
        null,
        0,
        null,
    );
    std.debug.print("parent: pid: {}\n", .{pid});
    var status: u32 = undefined;
    const wait_flags = 0;

    const pipe = fd[1];
    const wres = linux.write(pipe, "ok", 2);
    if (wres == -1) {
        std.debug.print("error: write\n", .{});
        return error.SyscallError;
    }

    const cres = linux.close(pipe);
    if (cres == -1) {
        std.debug.print("error: close\n", .{});
        return error.SyscallError;
    }

    _ = linux.waitpid(@intCast(pid), &status, wait_flags);
    std.debug.print("parent: tid: {}, pid: {}, ppid: {}\n", .{ linux.gettid(), linux.getpid(), linux.getppid() });
}

Error:

pipe 0, reader_fd: 3, writer_fd:4
parent: pid: 18446744073709551615
thread 2203563 panic: integer does not fit in destination type
/home/ec2-user/zig-examples/namespace.zig:69:23: 0x113d52c in main (namespace.zig)
    _ = linux.waitpid(@intCast(pid), &status, wait_flags);
                      ^
/home/ec2-user/zig-x86_64-linux-0.15.2/lib/std/start.zig:627:37: 0x113ded9 in posixCallMainAndExit (std.zig)
            const result = root.main() catch |err| {
                                    ^
/home/ec2-user/zig-x86_64-linux-0.15.2/lib/std/start.zig:232:5: 0x113d291 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x0 in ??? (???)
Aborted (core dumped)

I’m not sure exactly why, but std.linux.clone returns a usize, not a pid_t or i32. IntCast will try to convert the value. you are probably wanting @truncate, or @bitcast.

clone returns a thread id and not a process id. fork returns a process id.

1 Like

Yes, I am not sure why it returns max of u64 (18446744073709551615). Seems to me there’s a bug in the standard library. Thanks!

it’s not a bug, digging into the man page some more, the raw system call returns a long. Zig doesn’t go through libc unless you are linking to it, but makes the sys calls directly, so it has to return a usize.

Thanks for your reply. I am not linking libc, using the system call directly. It returns a usize correctly with correct thread id, when not using the flag linux.CLONE.NEWUTS. The issue occurs when I use the flag linux.CLONE.NEWUTS. I wrote a c program with clone/waitpid with those options: linux.CLONE.VM | linux.SIG.CHLD | linux.CLONE.NEWUTS, and it works without any issue.

Use std.os.linux.errno (std.os.linux.E.init in older zig versions) to check the return value for errors. Linux syscalls return errors as negative values, but zig uses usize for convenience as most valid values are actually positive.

2 Likes

Thanks for the suggestion. It revealed the problem: there was a permission error: PERM ( Zig Documentation ). The man page for clone(2) mentions that the CLONE_NEWUTS flag will need elevated permission ( clone(2) - Linux manual page ). Running the program with elevated permission correctly produced the expected outcome. Thanks!

2 Likes