diff --git a/lib/std/c.zig b/lib/std/c.zig index c9d4265c7455..818d92a4914e 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10856,6 +10856,18 @@ pub const pthread_threadid_np = switch (native_os) { else => {}, }; +pub const caddr_t = ?[*]u8; + +pub const ptrace = switch (native_os) { + .linux, .serenity => private.ptrace, + .macos, .ios, .tvos, .watchos, .visionos => darwin.ptrace, + .dragonfly => dragonfly.ptrace, + .freebsd => freebsd.ptrace, + .netbsd => netbsd.ptrace, + .openbsd => openbsd.ptrace, + else => {}, +}; + pub extern "c" fn sem_init(sem: *sem_t, pshared: c_int, value: c_uint) c_int; pub extern "c" fn sem_destroy(sem: *sem_t) c_int; pub extern "c" fn sem_open(name: [*:0]const u8, flag: c_int, mode: mode_t, value: c_uint) *sem_t; @@ -11305,7 +11317,6 @@ pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np; pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np; pub const pthread_get_qos_class_np = darwin.pthread_get_qos_class_np; pub const pthread_set_qos_class_self_np = darwin.pthread_set_qos_class_self_np; -pub const ptrace = darwin.ptrace; pub const qos_class_t = darwin.qos_class_t; pub const task_flavor_t = darwin.task_flavor_t; pub const task_for_pid = darwin.task_for_pid; @@ -11341,7 +11352,6 @@ pub const vm_region_submap_info_64 = darwin.vm_region_submap_info_64; pub const vm_region_submap_short_info_64 = darwin.vm_region_submap_short_info_64; pub const vm_region_top_info = darwin.vm_region_top_info; -pub const caddr_t = darwin.caddr_t; pub const exception_behavior_array_t = darwin.exception_behavior_array_t; pub const exception_behavior_t = darwin.exception_behavior_t; pub const exception_data_t = darwin.exception_data_t; @@ -11466,6 +11476,9 @@ const private = struct { extern "c" fn mlockall(flags: MCL) c_int; extern "c" fn munlockall() c_int; + // linux and https://github.com/SerenityOS/serenity/blob/502caef9a40bccc7459f9835f2174a601106299a/Userland/Libraries/LibC/sys/ptrace.cpp + extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: ?*anyopaque) c_long; + /// macos modernized symbols. /// x86_64 links to $INODE64 suffix for 64-bit support. /// Note these are not necessary on aarch64. diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 255bf03280f7..050d518f3d38 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -4,6 +4,7 @@ const native_arch = builtin.target.cpu.arch; const assert = std.debug.assert; const AF = std.c.AF; const PROT = std.c.PROT; +const caddr_t = std.c.caddr_t; const fd_t = std.c.fd_t; const iovec_const = std.posix.iovec_const; const mode_t = std.c.mode_t; @@ -1318,8 +1319,6 @@ pub const PT = enum(c_int) { _, }; -pub const caddr_t = ?[*]u8; - pub extern "c" fn ptrace(request: PT, pid: pid_t, addr: caddr_t, data: c_int) c_int; pub const POSIX_SPAWN = packed struct(c_short) { diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index c06d7ab8f532..e9caf5ff1df2 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -1,6 +1,7 @@ const std = @import("../std.zig"); const SIG = std.c.SIG; +const caddr_t = std.c.caddr_t; const gid_t = std.c.gid_t; const iovec = std.c.iovec; const pid_t = std.c.pid_t; @@ -8,6 +9,7 @@ const socklen_t = std.c.socklen_t; const uid_t = std.c.uid_t; pub extern "c" fn lwp_gettid() c_int; +pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int; pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 6fd33c80b716..391c3e26152b 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -5,6 +5,7 @@ const assert = std.debug.assert; const PATH_MAX = std.c.PATH_MAX; const blkcnt_t = std.c.blkcnt_t; const blksize_t = std.c.blksize_t; +const caddr_t = std.c.caddr_t; const dev_t = std.c.dev_t; const fd_t = std.c.fd_t; const gid_t = std.c.gid_t; @@ -25,6 +26,8 @@ comptime { assert(builtin.os.tag == .freebsd); // Prevent access of std.c symbols on wrong OS. } +pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; + pub extern "c" fn kinfo_getfile(pid: pid_t, cntp: *c_int) ?[*]kinfo_file; pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*off_t, fd_out: fd_t, off_out: ?*off_t, len: usize, flags: u32) usize; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index a4b3218ad93b..fad2322d7b06 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -5,6 +5,8 @@ const pthread_t = std.c.pthread_t; const sigval_t = std.c.sigval_t; const uid_t = std.c.uid_t; +pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: c_int) c_int; + pub const lwpid_t = i32; pub extern "c" fn _lwp_self() lwpid_t; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 242d988de3d3..b5a6bd0ed4fe 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -2,6 +2,7 @@ const std = @import("../std.zig"); const assert = std.debug.assert; const maxInt = std.math.maxInt; const builtin = @import("builtin"); +const caddr_t = std.c.caddr_t; const iovec = std.posix.iovec; const iovec_const = std.posix.iovec_const; const passwd = std.c.passwd; @@ -13,6 +14,8 @@ comptime { assert(builtin.os.tag == .openbsd); // Prevent access of std.c symbols on wrong OS. } +pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; + pub const pthread_spinlock_t = extern struct { inner: ?*anyopaque = null, }; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index f5ff7767a8a8..e28e4bf139e7 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -9648,6 +9648,33 @@ pub const PTRACE = struct { pub const SECCOMP_GET_FILTER = 0x420c; pub const SECCOMP_GET_METADATA = 0x420d; pub const GET_SYSCALL_INFO = 0x420e; + + pub const EVENT = struct { + pub const FORK = 1; + pub const VFORK = 2; + pub const CLONE = 3; + pub const EXEC = 4; + pub const VFORK_DONE = 5; + pub const EXIT = 6; + pub const SECCOMP = 7; + pub const STOP = 128; + }; + + pub const O = struct { + pub const TRACESYSGOOD = 1; + pub const TRACEFORK = 1 << PTRACE.EVENT.FORK; + pub const TRACEVFORK = 1 << PTRACE.EVENT.VFORK; + pub const TRACECLONE = 1 << PTRACE.EVENT.CLONE; + pub const TRACEEXEC = 1 << PTRACE.EVENT.EXEC; + pub const TRACEVFORKDONE = 1 << PTRACE.EVENT.VFORK_DONE; + pub const TRACEEXIT = 1 << PTRACE.EVENT.EXIT; + pub const TRACESECCOMP = 1 << PTRACE.EVENT.SECCOMP; + + pub const EXITKILL = 1 << 20; + pub const SUSPEND_SECCOMP = 1 << 21; + + pub const MASK = 0x000000ff | PTRACE.O.EXITKILL | PTRACE.O.SUSPEND_SECCOMP; + }; }; /// For futex2_waitv and futex2_requeue. Arrays of `futex2_waitone` allow diff --git a/lib/std/posix.zig b/lib/std/posix.zig index e0fd68db2446..e1d2c4378ac9 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -7377,18 +7377,33 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!system.itimerspec { } pub const PtraceError = error{ + DeadLock, DeviceBusy, InputOutput, + NameTooLong, + OperationNotSupported, + OutOfMemory, ProcessNotFound, PermissionDenied, } || UnexpectedError; -pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void { - if (native_os == .windows or native_os == .wasi) - @compileError("Unsupported OS"); - +pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!void { return switch (native_os) { - .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) { + .windows, + .wasi, + .emscripten, + .haiku, + .solaris, + .illumos, + .plan9, + => @compileError("ptrace unsupported by target OS"), + + .linux => switch (errno(if (builtin.link_libc) std.c.ptrace( + @intCast(request), + pid, + @ptrFromInt(addr), + @ptrFromInt(data), + ) else linux.ptrace(request, pid, addr, data, 0))) { .SUCCESS => {}, .SRCH => error.ProcessNotFound, .FAULT => unreachable, @@ -7400,27 +7415,80 @@ pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError! }, .macos, .ios, .tvos, .watchos, .visionos => switch (errno(std.c.ptrace( + @enumFromInt(request), + pid, + @ptrFromInt(addr), + @intCast(data), + ))) { + .SUCCESS => {}, + .SRCH => error.ProcessNotFound, + .INVAL => unreachable, + .PERM => error.PermissionDenied, + .BUSY => error.DeviceBusy, + else => |err| return unexpectedErrno(err), + }, + + .dragonfly => switch (errno(std.c.ptrace( + @intCast(request), + pid, + @ptrFromInt(addr), + @intCast(data), + ))) { + .SUCCESS => {}, + .SRCH => error.ProcessNotFound, + .INVAL => unreachable, + .PERM => error.PermissionDenied, + .BUSY => error.DeviceBusy, + else => |err| return unexpectedErrno(err), + }, + + .freebsd => switch (errno(std.c.ptrace( + @intCast(request), + pid, + @ptrFromInt(addr), + @intCast(data), + ))) { + .SUCCESS => {}, + .SRCH => error.ProcessNotFound, + .INVAL => unreachable, + .PERM => error.PermissionDenied, + .BUSY => error.DeviceBusy, + .NOENT, .NOMEM => error.OutOfMemory, + .NAMETOOLONG => error.NameTooLong, + else => |err| return unexpectedErrno(err), + }, + + .netbsd => switch (errno(std.c.ptrace( @intCast(request), pid, @ptrFromInt(addr), - @intCast(signal), + @intCast(data), ))) { .SUCCESS => {}, .SRCH => error.ProcessNotFound, .INVAL => unreachable, .PERM => error.PermissionDenied, .BUSY => error.DeviceBusy, + .DEADLK => error.DeadLock, else => |err| return unexpectedErrno(err), }, - else => switch (errno(system.ptrace(request, pid, addr, signal))) { + .openbsd => switch (errno(std.c.ptrace( + @intCast(request), + pid, + @ptrFromInt(addr), + @intCast(data), + ))) { .SUCCESS => {}, .SRCH => error.ProcessNotFound, .INVAL => unreachable, .PERM => error.PermissionDenied, .BUSY => error.DeviceBusy, + .NOTSUP => error.OperationNotSupported, else => |err| return unexpectedErrno(err), }, + + else => @compileError("std.posix.ptrace unimplemented for target OS"), }; }