Skip to content

Linux system calls may return unreachable or unexpected error codes #10776

@Mathnerd314

Description

@Mathnerd314

Zig Version

0.9.0

Steps to Reproduce

For this to work you must have a Linux kernel with CONFIG_BPF_KPROBE_OVERRIDE=y. Apparently this is the default on Arch Linux, but for NixOS I had to add:

  boot.kernelPatches = [ {
    name = "kprobe-bpf-override-config";
    patch = null;
    extraConfig = ''
      FUNCTION_ERROR_INJECTION y
      BPF_KPROBE_OVERRIDE y
      '';
    } ];

and build a kernel.
Once that is done you can run:

git clone --recursive https://github.com/trailofbits/ebpfault
cd ebpfault/libraries/ebpf-common/src
git fetch https://github.com/trailofbits/ebpf-common.git
git cherry-pick b20da60eec9c5bd051c5570950e72a34b3244c30 f75a900af068a2751c85c1332dade3167114400f
cd ../../..
nix-shell -E 'with import <nixpkgs> { }; llvmPackages_8.stdenv.mkDerivation { name = "ebpfault"; buildInputs = [ cmake llvmPackages_8.llvm zig ]; cmakeFlags = ["-DEBPFAULT_ENABLE_INSTALL:BOOL=true" "-DEBPF_COMMON_ENABLE_TESTS:BOOL=true" "-DEBPF_COMMON_ENABLE_SANITIZERS:BOOL=false" "-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo"]; }'

Then in the shell:

cmakeConfigurePhase
buildPhase
cat > efault.json <<EOF
{"fault_injectors": [
    {
      "syscall_name": "chdir",
      "error_list": [
        {
          "exit_code": "-EFAULT",
          "probability": 100
        }
      ]
    }
  ]
}
EOF
cat > einval.json <<EOF
{"fault_injectors": [
    {
      "syscall_name": "chdir",
      "error_list": [
        {
          "exit_code": "-EINVAL",
          "probability": 100
        }
      ]
    }
  ]
}
EOF
cat > chdir.zig <<EOF
const std = @import("std");

pub fn main() !void {
    try std.os.chdir(".");
}
EOF
zig build-exe chdir.zig
sudo ./ebpfault --config efault.json --exec ./chdir
sudo ./ebpfault --config einval.json --exec ./chdir

This is only the easiest way to get rare kernel errors to show up. As Matthew Wilcox writes, there are 70+ filesystems and also hooks such as Linux Security Modules. These can all return their own error codes under the correct conditions. Simply do git grep -ho '\-E[A-Z]*' fs or git grep -ho '\-E[A-Z]*' security in a kernel git repo to see the wide variety of error codes - nearly all error codes are in use. Furthermore a new release of the kernel may return new error codes. Figuring out the actual set of error codes each system call may return is a "Sisyphean" task that is best left unattempted.

Expected Behavior

I expect Zig to allow writing robust programs, so crashing with a panic is definitely not what I expected. I should be able to see the exact error code such as EINVAL or EFAULT when it comes up in practice, in the stack trace - the message for Unexpected doesn't count because it only shows up in debug mode. I should be able to handle the error code in a way appropriate to the application, without modifying Zig's standard library.

Actual Behavior

On running sudo ./ebpfault --config efault.json --exec ./chdir:

timestamp: 4844456200127 syscall: chdir process_id: 11026 thread_id: 11026 injected_error: -EFAULT
       r15 0000000000000000        r14 0000000000000000        r13 0000000000000000 
       r12 ffffa821812e7f58        rbp ffffa821812e7f48        rbx 0000000000000000 
       r11 0000000000000000        r10 0000000000000000         r9 0000000000000000 
        r8 0000000000000000        rax ffffffffb62d3040        rcx 0000000000000000 
       rdx ffffffffffffffff        rsi 0000000000000050        rdi ffffa821812e7f58 
  orig_rax 0000000000000000        rip ffffffffb62d3041         cs 0000000000000010 
    eflags 0000000000000206        rsp ffffa821812e7f38         ss 0000000000000018 
thread 11026 panic: reached unreachable code

/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2625:19: 0x2349ee in std.os.chdirZ (chdir)
        .FAULT => unreachable,
                  ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2610:22: 0x234916 in std.os.chdir (chdir)
        return chdirZ(&dir_path_c);
                     ^
ebpfault/build/chdir.zig:4:21: 0x22d19a in main (chdir)
    try std.os.chdir(".");
                    ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:553:37: 0x225e4a in std.start.callMain (chdir)
            const result = root.main() catch |err| {
                                    ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:495:12: 0x2071fe in std.start.callMainWithArgs (chdir)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:409:17: 0x206296 in std.start.posixCallMainAndExit (chdir)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:322:5: 0x2060a2 in std.start._start (chdir)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^

On running sudo ./ebpfault --config einval.json --exec ./chdir:

timestamp: 4918261533081 syscall: chdir process_id: 11058 thread_id: 11058 injected_error: -EINVAL
       r15 0000000000000000        r14 0000000000000000        r13 0000000000000000 
       r12 ffffa8218127ff58        rbp ffffa8218127ff48        rbx 0000000000000000 
       r11 0000000000000000        r10 0000000000000000         r9 0000000000000000 
        r8 0000000000000000        rax ffffffffb62d3040        rcx 0000000000000000 
       rdx ffffffffffffffff        rsi 0000000000000050        rdi ffffa8218127ff58 
  orig_rax 0000000000000000        rip ffffffffb62d3041         cs 0000000000000010 
    eflags 0000000000000206        rsp ffffa8218127ff38         ss 0000000000000018 

unexpected errno: 22
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/debug.zig:458:19: 0x208048 in std.debug.writeCurrentStackTrace (chdir)
    while (it.next()) |return_address| {
                  ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/debug.zig:115:31: 0x206aca in std.debug.dumpCurrentStackTrace (chdir)
        writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {
                              ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:4985:40: 0x209682 in std.os.unexpectedErrno (chdir)
        std.debug.dumpCurrentStackTrace(null);
                                       ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2632:45: 0x23499b in std.os.chdirZ (chdir)
        else => |err| return unexpectedErrno(err),
                                            ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2610:22: 0x234916 in std.os.chdir (chdir)
        return chdirZ(&dir_path_c);
                     ^
ebpfault/build/chdir.zig:4:21: 0x22d19a in main (chdir)
    try std.os.chdir(".");
                    ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:553:37: 0x225e4a in std.start.callMain (chdir)
            const result = root.main() catch |err| {
                                    ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:495:12: 0x2071fe in std.start.callMainWithArgs (chdir)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:409:17: 0x206296 in std.start.posixCallMainAndExit (chdir)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/start.zig:322:5: 0x2060a2 in std.start._start (chdir)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
error: Unexpected
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:4987:5: 0x209691 in std.os.unexpectedErrno (chdir)
    return error.Unexpected;
    ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2632:23: 0x2349a8 in std.os.chdirZ (chdir)
        else => |err| return unexpectedErrno(err),
                      ^
/nix/store/xnvnk2dpcig6xcx55xd8bvwdznsd0h9h-zig-0.9.0/lib/zig/std/os.zig:2610:9: 0x23492c in std.os.chdir (chdir)
        return chdirZ(&dir_path_c);
        ^
ebpfault/build/chdir.zig:4:5: 0x22d1b9 in main (chdir)
    try std.os.chdir(".");
    ^

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingImplementing this issue could cause existing code to no longer compile or have different behavior.os-linuxLinuxproposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.standard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions