diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index a0ca0ddee75f..cceb0d0d05c8 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -1,1320 +1,1354 @@ .\" $FreeBSD$ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd January 22, 2022 +.Dd January 27, 2022 .Dt PTRACE 2 .Os .Sh NAME .Nm ptrace .Nd process tracing and debugging .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/types.h .In sys/ptrace.h .Ft int .Fn ptrace "int request" "pid_t pid" "caddr_t addr" "int data" .Sh DESCRIPTION The .Fn ptrace system call provides tracing and debugging facilities. It allows one process (the .Em tracing process) to control another (the .Em traced process). The tracing process must first attach to the traced process, and then issue a series of .Fn ptrace system calls to control the execution of the process, as well as access process memory and register state. For the duration of the tracing session, the traced process will be .Dq re-parented , with its parent process ID (and resulting behavior) changed to the tracing process. It is permissible for a tracing process to attach to more than one other process at a time. When the tracing process has completed its work, it must detach the traced process; if a tracing process exits without first detaching all processes it has attached, those processes will be killed. .Pp Most of the time, the traced process runs normally, but when it receives a signal (see .Xr sigaction 2 ) , it stops. The tracing process is expected to notice this via .Xr wait 2 or the delivery of a .Dv SIGCHLD signal, examine the state of the stopped process, and cause it to terminate or continue as appropriate. The signal may be a normal process signal, generated as a result of traced process behavior, or use of the .Xr kill 2 system call; alternatively, it may be generated by the tracing facility as a result of attaching, stepping by the tracing process, or an event in the traced process. The tracing process may choose to intercept the signal, using it to observe process behavior (such as .Dv SIGTRAP ) , or forward the signal to the process if appropriate. The .Fn ptrace system call is the mechanism by which all this happens. .Pp A traced process may report additional signal stops corresponding to events in the traced process. These additional signal stops are reported as .Dv SIGTRAP or .Dv SIGSTOP signals. The tracing process can use the .Dv PT_LWPINFO request to determine which events are associated with a .Dv SIGTRAP or .Dv SIGSTOP signal. Note that multiple events may be associated with a single signal. For example, events indicated by the .Dv PL_FLAG_BORN , .Dv PL_FLAG_FORKED , and .Dv PL_FLAG_EXEC flags are also reported as a system call exit event .Pq Dv PL_FLAG_SCX . The signal stop for a new child process enabled via .Dv PTRACE_FORK will report a .Dv SIGSTOP signal. All other additional signal stops use .Dv SIGTRAP . .Sh DETACH AND TERMINATION .Pp Normally, exiting tracing process should wait for all pending debugging events and then detach from all alive traced processes before exiting using .Dv PT_DETACH request. If tracing process exits without detaching, for instance due to abnormal termination, the destiny of the traced children processes is determined by the .Dv kern.kill_on_debugger_exit sysctl control. .Pp If the control is set to the default value 1, such traced processes are terminated. If set to zero, kernel implicitly detaches traced processes. Traced processes are un-stopped if needed, and then continue the execution without tracing. Kernel drops any .Dv SIGTRAP signals queued to the traced children, which could be either generated by not yet consumed debug events, or sent by other means, the later should not be done anyway. .Sh DISABLING PTRACE The .Nm subsystem provides rich facilities to manipulate other processes state. Sometimes it may be desirable to disallow it either completely, or limit its scope. The following controls are provided for this: .Bl -tag -width security.bsd.unprivileged_proc_debug .It Dv security.bsd.allow_ptrace Setting this sysctl to zero value makes .Xr ptrace 2 return .Er ENOSYS always as if the syscall is not implemented by the kernel. .It Dv security.bsd.unprivileged_proc_debug Setting this sysctl to zero disallows use of .Fn ptrace by unprivileged processes. .It Dv security.bsd.see_other_uids Setting this sysctl to zero value disallows .Fn ptrace requests from targeting processes with the real user identifier different from the real user identifier of the caller. The requests return .Er ESRCH if policy is not met. .It Dv security.bsd.see_other_gids Setting this sysctl to zero value disallows .Fn ptrace requests from process belonging to a group that is not also one of the group of the target process. The requests return .Er ESRCH if policy is not met. .It Dv securelevel and init The .Xr init 1 process can only be traced with .Nm if securelevel is zero. .It Dv procctl(2) PROC_TRACE_CTL Process can deny attempts to trace itself with .Xr procctl 2 .Dv PROC_TRACE_CTL request. In this case requests return .Xr EPERM error. .El .Sh TRACING EVENTS .Pp Each traced process has a tracing event mask. An event in the traced process only reports a signal stop if the corresponding flag is set in the tracing event mask. The current set of tracing event flags include: .Bl -tag -width "Dv PTRACE_SYSCALL" .It Dv PTRACE_EXEC Report a stop for a successful invocation of .Xr execve 2 . This event is indicated by the .Dv PL_FLAG_EXEC flag in the .Va pl_flags member of .Vt "struct ptrace_lwpinfo" . .It Dv PTRACE_SCE Report a stop on each system call entry. This event is indicated by the .Dv PL_FLAG_SCE flag in the .Va pl_flags member of .Vt "struct ptrace_lwpinfo" . .It Dv PTRACE_SCX Report a stop on each system call exit. This event is indicated by the .Dv PL_FLAG_SCX flag in the .Va pl_flags member of .Vt "struct ptrace_lwpinfo" . .It Dv PTRACE_SYSCALL Report stops for both system call entry and exit. .It Dv PTRACE_FORK This event flag controls tracing for new child processes of a traced process. .Pp When this event flag is enabled, new child processes will enable tracing and stop before executing their first instruction. The new child process will include the .Dv PL_FLAG_CHILD flag in the .Va pl_flags member of .Vt "struct ptrace_lwpinfo" . The traced process will report a stop that includes the .Dv PL_FLAG_FORKED flag. The process ID of the new child process will also be present in the .Va pl_child_pid member of .Vt "struct ptrace_lwpinfo" . If the new child process was created via .Xr vfork 2 , the traced process's stop will also include the .Dv PL_FLAG_VFORKED flag. Note that new child processes will be attached with the default tracing event mask; they do not inherit the event mask of the traced process. .Pp When this event flag is not enabled, new child processes will execute without tracing enabled. .It Dv PTRACE_LWP This event flag controls tracing of LWP .Pq kernel thread creation and destruction. When this event is enabled, new LWPs will stop and report an event with .Dv PL_FLAG_BORN set before executing their first instruction, and exiting LWPs will stop and report an event with .Dv PL_FLAG_EXITED set before completing their termination. .Pp Note that new processes do not report an event for the creation of their initial thread, and exiting processes do not report an event for the termination of the last thread. .It Dv PTRACE_VFORK Report a stop event when a parent process resumes after a .Xr vfork 2 . .Pp When a thread in the traced process creates a new child process via .Xr vfork 2 , the stop that reports .Dv PL_FLAG_FORKED and .Dv PL_FLAG_SCX occurs just after the child process is created, but before the thread waits for the child process to stop sharing process memory. If a debugger is not tracing the new child process, it must ensure that no breakpoints are enabled in the shared process memory before detaching from the new child process. This means that no breakpoints are enabled in the parent process either. .Pp The .Dv PTRACE_VFORK flag enables a new stop that indicates when the new child process stops sharing the process memory of the parent process. A debugger can reinsert breakpoints in the parent process and resume it in response to this event. This event is indicated by setting the .Dv PL_FLAG_VFORK_DONE flag. .El .Pp The default tracing event mask when attaching to a process via .Dv PT_ATTACH , .Dv PT_TRACE_ME , or .Dv PTRACE_FORK includes only .Dv PTRACE_EXEC events. All other event flags are disabled. .Sh PTRACE REQUESTS .Pp The .Fa request argument specifies what operation is being performed; the meaning of the rest of the arguments depends on the operation, but except for one special case noted below, all .Fn ptrace calls are made by the tracing process, and the .Fa pid argument specifies the process ID of the traced process or a corresponding thread ID. The .Fa request argument can be: .Bl -tag -width "Dv PT_GET_EVENT_MASK" .It Dv PT_TRACE_ME This request is the only one used by the traced process; it declares that the process expects to be traced by its parent. All the other arguments are ignored. (If the parent process does not expect to trace the child, it will probably be rather confused by the results; once the traced process stops, it cannot be made to continue except via .Fn ptrace . ) When a process has used this request and calls .Xr execve 2 or any of the routines built on it (such as .Xr execv 3 ) , it will stop before executing the first instruction of the new image. Also, any setuid or setgid bits on the executable being executed will be ignored. If the child was created by .Xr vfork 2 system call or .Xr rfork 2 call with the .Dv RFMEM flag specified, the debugging events are reported to the parent only after the .Xr execve 2 is executed. .It Dv PT_READ_I , Dv PT_READ_D These requests read a single .Vt int of data from the traced process's address space. Traditionally, .Fn ptrace has allowed for machines with distinct address spaces for instruction and data, which is why there are two requests: conceptually, .Dv PT_READ_I reads from the instruction space and .Dv PT_READ_D reads from the data space. In the current .Fx implementation, these two requests are completely identical. The .Fa addr argument specifies the address (in the traced process's virtual address space) at which the read is to be done. This address does not have to meet any alignment constraints. The value read is returned as the return value from .Fn ptrace . .It Dv PT_WRITE_I , Dv PT_WRITE_D These requests parallel .Dv PT_READ_I and .Dv PT_READ_D , except that they write rather than read. The .Fa data argument supplies the value to be written. .It Dv PT_IO This request allows reading and writing arbitrary amounts of data in the traced process's address space. The .Fa addr argument specifies a pointer to a .Vt "struct ptrace_io_desc" , which is defined as follows: .Bd -literal struct ptrace_io_desc { int piod_op; /* I/O operation */ void *piod_offs; /* child offset */ void *piod_addr; /* parent offset */ size_t piod_len; /* request length */ }; /* * Operations in piod_op. */ #define PIOD_READ_D 1 /* Read from D space */ #define PIOD_WRITE_D 2 /* Write to D space */ #define PIOD_READ_I 3 /* Read from I space */ #define PIOD_WRITE_I 4 /* Write to I space */ .Ed .Pp The .Fa data argument is ignored. The actual number of bytes read or written is stored in .Va piod_len upon return. .It Dv PT_CONTINUE The traced process continues execution. The .Fa addr argument is an address specifying the place where execution is to be resumed (a new value for the program counter), or .Po Vt caddr_t Pc Ns 1 to indicate that execution is to pick up where it left off. The .Fa data argument provides a signal number to be delivered to the traced process as it resumes execution, or 0 if no signal is to be sent. .It Dv PT_STEP The traced process is single stepped one instruction. The .Fa addr argument should be passed .Po Vt caddr_t Pc Ns 1 . The .Fa data argument provides a signal number to be delivered to the traced process as it resumes execution, or 0 if no signal is to be sent. .It Dv PT_KILL The traced process terminates, as if .Dv PT_CONTINUE had been used with .Dv SIGKILL given as the signal to be delivered. .It Dv PT_ATTACH This request allows a process to gain control of an otherwise unrelated process and begin tracing it. It does not need any cooperation from the to-be-traced process. In this case, .Fa pid specifies the process ID of the to-be-traced process, and the other two arguments are ignored. This request requires that the target process must have the same real UID as the tracing process, and that it must not be executing a setuid or setgid executable. (If the tracing process is running as root, these restrictions do not apply.) The tracing process will see the newly-traced process stop and may then control it as if it had been traced all along. .It Dv PT_DETACH This request is like PT_CONTINUE, except that it does not allow specifying an alternate place to continue execution, and after it succeeds, the traced process is no longer traced and continues execution normally. .It Dv PT_GETREGS This request reads the traced process's machine registers into the .Do .Vt "struct reg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . .It Dv PT_SETREGS This request is the converse of .Dv PT_GETREGS ; it loads the traced process's machine registers from the .Do .Vt "struct reg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . .It Dv PT_GETFPREGS This request reads the traced process's floating-point registers into the .Do .Vt "struct fpreg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . .It Dv PT_SETFPREGS This request is the converse of .Dv PT_GETFPREGS ; it loads the traced process's floating-point registers from the .Do .Vt "struct fpreg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . .It Dv PT_GETDBREGS This request reads the traced process's debug registers into the .Do .Vt "struct dbreg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . .It Dv PT_SETDBREGS This request is the converse of .Dv PT_GETDBREGS ; it loads the traced process's debug registers from the .Do .Vt "struct dbreg" .Dc (defined in .In machine/reg.h ) pointed to by .Fa addr . +.It Dv PT_GETREGSET +This request reads the registers from the traced process. +The +.Fa data +argument specifies the register set to read, with the +.Fa addr +argument pointing at a +.Vt "struct iovec" +where the +.Va iov_base +field points to a register set specific structure to hold the registers, +and the +.Va iov_len +field holds the length of the structure. +.It Dv PT_SETREGSET +This request writes to the registers of the traced process. +The +.Fa data +argument specifies the register set to write to, with the +.Fa addr +argument pointing at a +.Vt "struct iovec" +where the +.Va iov_base +field points to a register set specific structure to hold the registers, +and the +.Va iov_len +field holds the length of the structure. +If +.Va iov_base +is NULL the kernel will return the expected length of the register set +specific structure in the +.Va iov_len +field and not change the target register set. .It Dv PT_LWPINFO This request can be used to obtain information about the kernel thread, also known as light-weight process, that caused the traced process to stop. The .Fa addr argument specifies a pointer to a .Vt "struct ptrace_lwpinfo" , which is defined as follows: .Bd -literal struct ptrace_lwpinfo { lwpid_t pl_lwpid; int pl_event; int pl_flags; sigset_t pl_sigmask; sigset_t pl_siglist; siginfo_t pl_siginfo; char pl_tdname[MAXCOMLEN + 1]; pid_t pl_child_pid; u_int pl_syscall_code; u_int pl_syscall_narg; }; .Ed .Pp The .Fa data argument is to be set to the size of the structure known to the caller. This allows the structure to grow without affecting older programs. .Pp The fields in the .Vt "struct ptrace_lwpinfo" have the following meaning: .Bl -tag -width indent -compact .It Va pl_lwpid LWP id of the thread .It Va pl_event Event that caused the stop. Currently defined events are: .Bl -tag -width "Dv PL_EVENT_SIGNAL" -compact .It Dv PL_EVENT_NONE No reason given .It Dv PL_EVENT_SIGNAL Thread stopped due to the pending signal .El .It Va pl_flags Flags that specify additional details about observed stop. Currently defined flags are: .Bl -tag -width indent -compact .It Dv PL_FLAG_SCE The thread stopped due to system call entry, right after the kernel is entered. The debugger may examine syscall arguments that are stored in memory and registers according to the ABI of the current process, and modify them, if needed. .It Dv PL_FLAG_SCX The thread is stopped immediately before syscall is returning to the usermode. The debugger may examine system call return values in the ABI-defined registers and/or memory. .It Dv PL_FLAG_EXEC When .Dv PL_FLAG_SCX is set, this flag may be additionally specified to inform that the program being executed by debuggee process has been changed by successful execution of a system call from the .Fn execve 2 family. .It Dv PL_FLAG_SI Indicates that .Va pl_siginfo member of .Vt "struct ptrace_lwpinfo" contains valid information. .It Dv PL_FLAG_FORKED Indicates that the process is returning from a call to .Fn fork 2 that created a new child process. The process identifier of the new process is available in the .Va pl_child_pid member of .Vt "struct ptrace_lwpinfo" . .It Dv PL_FLAG_CHILD The flag is set for first event reported from a new child which is automatically attached when .Dv PTRACE_FORK is enabled. .It Dv PL_FLAG_BORN This flag is set for the first event reported from a new LWP when .Dv PTRACE_LWP is enabled. It is reported along with .Dv PL_FLAG_SCX . .It Dv PL_FLAG_EXITED This flag is set for the last event reported by an exiting LWP when .Dv PTRACE_LWP is enabled. Note that this event is not reported when the last LWP in a process exits. The termination of the last thread is reported via a normal process exit event. .It Dv PL_FLAG_VFORKED Indicates that the thread is returning from a call to .Xr vfork 2 that created a new child process. This flag is set in addition to .Dv PL_FLAG_FORKED . .It Dv PL_FLAG_VFORK_DONE Indicates that the thread has resumed after a child process created via .Xr vfork 2 has stopped sharing its address space with the traced process. .El .It Va pl_sigmask The current signal mask of the LWP .It Va pl_siglist The current pending set of signals for the LWP. Note that signals that are delivered to the process would not appear on an LWP siglist until the thread is selected for delivery. .It Va pl_siginfo The siginfo that accompanies the signal pending. Only valid for .Dv PL_EVENT_SIGNAL stop when .Dv PL_FLAG_SI is set in .Va pl_flags . .It Va pl_tdname The name of the thread. .It Va pl_child_pid The process identifier of the new child process. Only valid for a .Dv PL_EVENT_SIGNAL stop when .Dv PL_FLAG_FORKED is set in .Va pl_flags . .It Va pl_syscall_code The ABI-specific identifier of the current system call. Note that for indirect system calls this field reports the indirected system call. Only valid when .Dv PL_FLAG_SCE or .Dv PL_FLAG_SCX is set in .Va pl_flags . .It Va pl_syscall_narg The number of arguments passed to the current system call not counting the system call identifier. Note that for indirect system calls this field reports the arguments passed to the indirected system call. Only valid when .Dv PL_FLAG_SCE or .Dv PL_FLAG_SCX is set in .Va pl_flags . .El .It Dv PT_GETNUMLWPS This request returns the number of kernel threads associated with the traced process. .It Dv PT_GETLWPLIST This request can be used to get the current thread list. A pointer to an array of type .Vt lwpid_t should be passed in .Fa addr , with the array size specified by .Fa data . The return value from .Fn ptrace is the count of array entries filled in. .It Dv PT_SETSTEP This request will turn on single stepping of the specified process. Stepping is automatically disabled when a single step trap is caught. .It Dv PT_CLEARSTEP This request will turn off single stepping of the specified process. .It Dv PT_SUSPEND This request will suspend the specified thread. .It Dv PT_RESUME This request will resume the specified thread. .It Dv PT_TO_SCE This request will set the .Dv PTRACE_SCE event flag to trace all future system call entries and continue the process. The .Fa addr and .Fa data arguments are used the same as for .Dv PT_CONTINUE . .It Dv PT_TO_SCX This request will set the .Dv PTRACE_SCX event flag to trace all future system call exits and continue the process. The .Fa addr and .Fa data arguments are used the same as for .Dv PT_CONTINUE . .It Dv PT_SYSCALL This request will set the .Dv PTRACE_SYSCALL event flag to trace all future system call entries and exits and continue the process. The .Fa addr and .Fa data arguments are used the same as for .Dv PT_CONTINUE . .It Dv PT_GET_SC_ARGS For the thread which is stopped in either .Dv PL_FLAG_SCE or .Dv PL_FLAG_SCX state, that is, on entry or exit to a syscall, this request fetches the syscall arguments. .Pp The arguments are copied out into the buffer pointed to by the .Fa addr pointer, sequentially. Each syscall argument is stored as the machine word. Kernel copies out as many arguments as the syscall accepts, see the .Va pl_syscall_narg member of the .Vt struct ptrace_lwpinfo , but not more than the .Fa data bytes in total are copied. .It Dv PT_GET_SC_RET Fetch the system call return values on exit from a syscall. This request is only valid for threads stopped in a syscall exit (the .Dv PL_FLAG_SCX state). The .Fa addr argument specifies a pointer to a .Vt "struct ptrace_sc_ret" , which is defined as follows: .Bd -literal struct ptrace_sc_ret { register_t sr_retval[2]; int sr_error; }; .Ed .Pp The .Fa data argument is set to the size of the structure. .Pp If the system call completed successfully, .Va sr_error is set to zero and the return values of the system call are saved in .Va sr_retval . If the system call failed to execute, .Va sr_error field is set to a positive .Xr errno 2 value. If the system call completed in an unusual fashion, .Va sr_error is set to a negative value: .Bl -tag -width Dv EJUSTRETURN -compact .It Dv ERESTART System call will be restarted. .It Dv EJUSTRETURN System call completed sucessfully but did not set a return value .Po for example, .Xr setcontext 2 and .Xr sigreturn 2 .Pc . .El .It Dv PT_FOLLOW_FORK This request controls tracing for new child processes of a traced process. If .Fa data is non-zero, .Dv PTRACE_FORK is set in the traced process's event tracing mask. If .Fa data is zero, .Dv PTRACE_FORK is cleared from the traced process's event tracing mask. .It Dv PT_LWP_EVENTS This request controls tracing of LWP creation and destruction. If .Fa data is non-zero, .Dv PTRACE_LWP is set in the traced process's event tracing mask. If .Fa data is zero, .Dv PTRACE_LWP is cleared from the traced process's event tracing mask. .It Dv PT_GET_EVENT_MASK This request reads the traced process's event tracing mask into the integer pointed to by .Fa addr . The size of the integer must be passed in .Fa data . .It Dv PT_SET_EVENT_MASK This request sets the traced process's event tracing mask from the integer pointed to by .Fa addr . The size of the integer must be passed in .Fa data . .It Dv PT_VM_TIMESTAMP This request returns the generation number or timestamp of the memory map of the traced process as the return value from .Fn ptrace . This provides a low-cost way for the tracing process to determine if the VM map changed since the last time this request was made. .It Dv PT_VM_ENTRY This request is used to iterate over the entries of the VM map of the traced process. The .Fa addr argument specifies a pointer to a .Vt "struct ptrace_vm_entry" , which is defined as follows: .Bd -literal struct ptrace_vm_entry { int pve_entry; int pve_timestamp; u_long pve_start; u_long pve_end; u_long pve_offset; u_int pve_prot; u_int pve_pathlen; long pve_fileid; uint32_t pve_fsid; char *pve_path; }; .Ed .Pp The first entry is returned by setting .Va pve_entry to zero. Subsequent entries are returned by leaving .Va pve_entry unmodified from the value returned by previous requests. The .Va pve_timestamp field can be used to detect changes to the VM map while iterating over the entries. The tracing process can then take appropriate action, such as restarting. By setting .Va pve_pathlen to a non-zero value on entry, the pathname of the backing object is returned in the buffer pointed to by .Va pve_path , provided the entry is backed by a vnode. The .Va pve_pathlen field is updated with the actual length of the pathname (including the terminating null character). The .Va pve_offset field is the offset within the backing object at which the range starts. The range is located in the VM space at .Va pve_start and extends up to .Va pve_end (inclusive). .Pp The .Fa data argument is ignored. .It Dv PT_COREDUMP This request creates a coredump for the stopped program. The .Fa addr argument specifies a pointer to a .Vt "struct ptrace_coredump" , which is defined as follows: .Bd -literal struct ptrace_coredump { int pc_fd; uint32_t pc_flags; off_t pc_limit; }; .Ed The fields of the structure are: .Bl -tag -width pc_flags .It Dv pc_fd File descriptor to write the dump to. It must refer to a regular file, opened for writing. .It Dv pc_flags Flags. The following flags are defined: .Bl -tag -width PC_COMPRESS .It Dv PC_COMPRESS Request compression of the dump. .It Dv PC_ALL Include non-dumpable entries into the dump. The dumper ignores .Dv MAP_NOCORE flag of the process map entry, but device mappings are not dumped even with .Dv PC_ALL set. .El .It Dv pc_limit Maximum size of the coredump. Specify zero for no limit. .El .Pp The size of .Vt "struct ptrace_coredump" must be passed in .Fa data . .Pp The process must be stopped before dumping core. A single thread in the target process is temporarily unsuspended in kernel to write the dump. If the .Nm call fails before a thread is unsuspended, there is no event to .Xr waitpid 2 for. If a thread was unsuspended, it will stop again before the .Nm call returns, and the process must be waited upon using .Xr waitpid 2 to consume the new stop event. Since it is hard to deduce whether a thread was unsuspended before an error occurred, it is recommended to unconditionally perform .Xr waitpid 2 with .Dv WNOHANG flag after .Dv PT_COREDUMP , and silently accept zero result from it. .El .Sh ARM MACHINE-SPECIFIC REQUESTS .Bl -tag -width "Dv PT_SETVFPREGS" .It Dv PT_GETVFPREGS Return the thread's .Dv VFP machine state in the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .It Dv PT_SETVFPREGS Set the thread's .Dv VFP machine state from the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .El .Sh x86 MACHINE-SPECIFIC REQUESTS .Bl -tag -width "Dv PT_GETXSTATE_INFO" .It Dv PT_GETXMMREGS Copy the XMM FPU state into the buffer pointed to by the argument .Fa addr . The buffer has the same layout as the 32-bit save buffer for the machine instruction .Dv FXSAVE . .Pp This request is only valid for i386 programs, both on native 32-bit systems and on amd64 kernels. For 64-bit amd64 programs, the XMM state is reported as part of the FPU state returned by the .Dv PT_GETFPREGS request. .Pp The .Fa data argument is ignored. .It Dv PT_SETXMMREGS Load the XMM FPU state for the thread from the buffer pointed to by the argument .Fa addr . The buffer has the same layout as the 32-bit load buffer for the machine instruction .Dv FXRSTOR . .Pp As with .Dv PT_GETXMMREGS , this request is only valid for i386 programs. .Pp The .Fa data argument is ignored. .It Dv PT_GETXSTATE_INFO Report which XSAVE FPU extensions are supported by the CPU and allowed in userspace programs. The .Fa addr argument must point to a variable of type .Vt struct ptrace_xstate_info , which contains the information on the request return. .Vt struct ptrace_xstate_info is defined as follows: .Bd -literal struct ptrace_xstate_info { uint64_t xsave_mask; uint32_t xsave_len; }; .Ed The .Dv xsave_mask field is a bitmask of the currently enabled extensions. The meaning of the bits is defined in the Intel and AMD processor documentation. The .Dv xsave_len field reports the length of the XSAVE area for storing the hardware state for currently enabled extensions in the format defined by the x86 .Dv XSAVE machine instruction. .Pp The .Fa data argument value must be equal to the size of the .Vt struct ptrace_xstate_info . .It Dv PT_GETXSTATE Return the content of the XSAVE area for the thread. The .Fa addr argument points to the buffer where the content is copied, and the .Fa data argument specifies the size of the buffer. The kernel copies out as much content as allowed by the buffer size. The buffer layout is specified by the layout of the save area for the .Dv XSAVE machine instruction. .It Dv PT_SETXSTATE Load the XSAVE state for the thread from the buffer specified by the .Fa addr pointer. The buffer size is passed in the .Fa data argument. The buffer must be at least as large as the .Vt struct savefpu (defined in .Pa x86/fpu.h ) to allow the complete x87 FPU and XMM state load. It must not be larger than the XSAVE state length, as reported by the .Dv xsave_len field from the .Vt struct ptrace_xstate_info of the .Dv PT_GETXSTATE_INFO request. Layout of the buffer is identical to the layout of the load area for the .Dv XRSTOR machine instruction. .It Dv PT_GETFSBASE Return the value of the base used when doing segmented memory addressing using the %fs segment register. The .Fa addr argument points to an .Vt unsigned long variable where the base value is stored. .Pp The .Fa data argument is ignored. .It Dv PT_GETGSBASE Like the .Dv PT_GETFSBASE request, but returns the base for the %gs segment register. .It Dv PT_SETFSBASE Set the base for the %fs segment register to the value pointed to by the .Fa addr argument. .Fa addr must point to the .Vt unsigned long variable containing the new base. .Pp The .Fa data argument is ignored. .It Dv PT_SETGSBASE Like the .Dv PT_SETFSBASE request, but sets the base for the %gs segment register. .El .Sh PowerPC MACHINE-SPECIFIC REQUESTS .Bl -tag -width "Dv PT_SETVRREGS" .It Dv PT_GETVRREGS Return the thread's .Dv ALTIVEC machine state in the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .It Dv PT_SETVRREGS Set the thread's .Dv ALTIVEC machine state from the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .It Dv PT_GETVSRREGS Return doubleword 1 of the thread's .Dv VSX registers VSR0-VSR31 in the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .It Dv PT_SETVSRREGS Set doubleword 1 of the thread's .Dv VSX registers VSR0-VSR31 from the buffer pointed to by .Fa addr . .Pp The .Fa data argument is ignored. .El .Pp Additionally, other machine-specific requests can exist. .Sh RETURN VALUES Most requests return 0 on success and \-1 on error. Some requests can cause .Fn ptrace to return \-1 as a non-error value, among them are .Dv PT_READ_I and .Dv PT_READ_D , which return the value read from the process memory on success. To disambiguate, .Va errno can be set to 0 before the call and checked afterwards. .Pp The current .Fn ptrace implementation always sets .Va errno to 0 before calling into the kernel, both for historic reasons and for consistency with other operating systems. It is recommended to assign zero to .Va errno explicitly for forward compatibility. .Sh ERRORS The .Fn ptrace system call may fail if: .Bl -tag -width Er .It Bq Er ESRCH .Bl -bullet -compact .It No process having the specified process ID exists. .El .It Bq Er EINVAL .Bl -bullet -compact .It A process attempted to use .Dv PT_ATTACH on itself. .It The .Fa request argument was not one of the legal requests. .It The signal number (in .Fa data ) to .Dv PT_CONTINUE was neither 0 nor a legal signal number. .It .Dv PT_GETREGS , .Dv PT_SETREGS , .Dv PT_GETFPREGS , .Dv PT_SETFPREGS , .Dv PT_GETDBREGS , or .Dv PT_SETDBREGS was attempted on a process with no valid register set. (This is normally true only of system processes.) .It .Dv PT_VM_ENTRY was given an invalid value for .Fa pve_entry . This can also be caused by changes to the VM map of the process. .It The size (in .Fa data ) provided to .Dv PT_LWPINFO was less than or equal to zero, or larger than the .Vt ptrace_lwpinfo structure known to the kernel. .It The size (in .Fa data ) provided to the x86-specific .Dv PT_GETXSTATE_INFO request was not equal to the size of the .Vt struct ptrace_xstate_info . .It The size (in .Fa data ) provided to the x86-specific .Dv PT_SETXSTATE request was less than the size of the x87 plus the XMM save area. .It The size (in .Fa data ) provided to the x86-specific .Dv PT_SETXSTATE request was larger than returned in the .Dv xsave_len member of the .Vt struct ptrace_xstate_info from the .Dv PT_GETXSTATE_INFO request. .It The base value, provided to the amd64-specific requests .Dv PT_SETFSBASE or .Dv PT_SETGSBASE , pointed outside of the valid user address space. This error will not occur in 32-bit programs. .El .It Bq Er EBUSY .Bl -bullet -compact .It .Dv PT_ATTACH was attempted on a process that was already being traced. .It A request attempted to manipulate a process that was being traced by some process other than the one making the request. .It A request (other than .Dv PT_ATTACH ) specified a process that was not stopped. .El .It Bq Er EPERM .Bl -bullet -compact .It A request (other than .Dv PT_ATTACH ) attempted to manipulate a process that was not being traced at all. .It An attempt was made to use .Dv PT_ATTACH on a process in violation of the requirements listed under .Dv PT_ATTACH above. .El .It Bq Er ENOENT .Bl -bullet -compact .It .Dv PT_VM_ENTRY previously returned the last entry of the memory map. No more entries exist. .El .It Bq Er ENOMEM .Bl -bullet -compact .It A .Dv PT_READ_I, .Dv PT_READ_D, .Dv PT_WRITE_I, or .Dv PT_WRITE_D request attempted to access an invalid address, or a memory allocation failure occurred when accessing process memory. .El .It Bq Er ENAMETOOLONG .Bl -bullet -compact .It .Dv PT_VM_ENTRY cannot return the pathname of the backing object because the buffer is not big enough. .Fa pve_pathlen holds the minimum buffer size required on return. .El .El .Sh SEE ALSO .Xr execve 2 , .Xr sigaction 2 , .Xr wait 2 , .Xr execv 3 , .Xr i386_clr_watch 3 , .Xr i386_set_watch 3 .Sh HISTORY The .Fn ptrace function appeared in .At v6 . diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c index 192a73e0a7df..1237eadcd239 100644 --- a/sys/amd64/amd64/elf_machdep.c +++ b/sys/amd64/amd64/elf_machdep.c @@ -1,446 +1,451 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include "vdso_offsets.h" extern const char _binary_elf_vdso_so_1_start[]; extern const char _binary_elf_vdso_so_1_end[]; extern char _binary_elf_vdso_so_1_size; struct sysentvec elf64_freebsd_sysvec_la48 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = _binary_elf_vdso_so_1_start, .sv_szsigcode = (int *)&_binary_elf_vdso_so_1_size, .sv_sigcodeoff = VDSO_SIGCODE_OFFSET, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS_LA48, .sv_usrstack = USRSTACK_LA48, .sv_psstrings = PS_STRINGS_LA48, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ASLR | SV_LP64 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER | SV_DSO_SIG, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE_LA48, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, .sv_set_fork_retval = x86_set_fork_retval, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; struct sysentvec elf64_freebsd_sysvec_la57 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = _binary_elf_vdso_so_1_start, .sv_szsigcode = (int *)&_binary_elf_vdso_so_1_size, .sv_sigcodeoff = VDSO_SIGCODE_OFFSET, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS_LA57, .sv_usrstack = USRSTACK_LA57, .sv_psstrings = PS_STRINGS_LA57, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ASLR | SV_LP64 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER | SV_DSO_SIG, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE_LA57, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, .sv_set_fork_retval= x86_set_fork_retval, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; static void amd64_init_sysvecs(void *arg) { amd64_lower_shared_page(&elf64_freebsd_sysvec_la48); if (la57) { exec_sysvec_init(&elf64_freebsd_sysvec_la57); exec_sysvec_init_secondary(&elf64_freebsd_sysvec_la57, &elf64_freebsd_sysvec_la48); } else { exec_sysvec_init(&elf64_freebsd_sysvec_la48); } } SYSINIT(elf64_sysvec, SI_SUB_EXEC, SI_ORDER_ANY, amd64_init_sysvecs, NULL); void amd64_lower_shared_page(struct sysentvec *sv) { if (hw_lower_amd64_sharedpage != 0) { sv->sv_maxuser -= PAGE_SIZE; sv->sv_shared_page_base -= PAGE_SIZE; sv->sv_usrstack -= PAGE_SIZE; sv->sv_psstrings -= PAGE_SIZE; } } static boolean_t freebsd_brand_info_la57_img_compat(struct image_params *imgp, int32_t *osrel __unused, uint32_t *fctl0) { if ((imgp->proc->p_md.md_flags & P_MD_LA57) != 0) return (TRUE); if (fctl0 == NULL || (*fctl0 & NT_FREEBSD_FCTL_LA48) != 0) return (FALSE); if ((imgp->proc->p_md.md_flags & P_MD_LA48) != 0) return (FALSE); return (TRUE); } static Elf64_Brandinfo freebsd_brand_info_la48 = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_la48, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, }; static Elf64_Brandinfo freebsd_brand_info_la57 = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_la57, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = freebsd_brand_info_la57_img_compat, }; static void sysinit_register_elf64_brand_entries(void *arg __unused) { /* * _57 must go first so it can either claim the image or hand * it to _48. */ if (la57) elf64_insert_brand_entry(&freebsd_brand_info_la57); elf64_insert_brand_entry(&freebsd_brand_info_la48); } SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, sysinit_register_elf64_brand_entries, NULL); static Elf64_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_la48, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_oinfo); static Elf64_Brandinfo kfreebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld-kfreebsd-x86-64.so.1", .sysvec = &elf64_freebsd_sysvec_la48, .interp_newpath = NULL, .brand_note = &elf64_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t)elf64_insert_brand_entry, &kfreebsd_brand_info); void elf64_dump_thread(struct thread *td, void *dst, size_t *off) { void *buf; size_t len; len = 0; if (use_xsave) { if (dst != NULL) { fpugetregs(td); len += elf64_populate_note(NT_X86_XSTATE, get_pcb_user_save_td(td), dst, cpu_max_ext_state_size, &buf); *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = xsave_mask; } else len += elf64_populate_note(NT_X86_XSTATE, NULL, NULL, cpu_max_ext_state_size, NULL); } *off = len; } bool elf_is_ifunc_reloc(Elf_Size r_info) { return (ELF_R_TYPE(r_info) == R_X86_64_IRELATIVE); } /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, bool late_ifunc, elf_lookup_fn lookup) { Elf64_Addr *where, val; Elf32_Addr *where32, val32; Elf_Addr addr; Elf_Addr addend; Elf_Size rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); /* Addend is 32 bit on 32 bit relocs */ switch (rtype) { case R_X86_64_PC32: case R_X86_64_32S: case R_X86_64_PLT32: addend = *(Elf32_Addr *)where; break; default: addend = *where; break; } break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if (late_ifunc) { KASSERT(type == ELF_RELOC_RELA, ("Only RELA ifunc relocations are supported")); if (rtype != R_X86_64_IRELATIVE) return (0); } switch (rtype) { case R_X86_64_NONE: /* none */ break; case R_X86_64_64: /* S + A */ error = lookup(lf, symidx, 1, &addr); val = addr + addend; if (error != 0) return (-1); if (*where != val) *where = val; break; case R_X86_64_PC32: /* S + A - P */ case R_X86_64_PLT32: /* L + A - P, L is PLT location for the symbol, which we treat as S */ error = lookup(lf, symidx, 1, &addr); where32 = (Elf32_Addr *)where; val32 = (Elf32_Addr)(addr + addend - (Elf_Addr)where); if (error != 0) return (-1); if (*where32 != val32) *where32 = val32; break; case R_X86_64_32S: /* S + A sign extend */ error = lookup(lf, symidx, 1, &addr); val32 = (Elf32_Addr)(addr + addend); where32 = (Elf32_Addr *)where; if (error != 0) return (-1); if (*where32 != val32) *where32 = val32; break; case R_X86_64_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation, " "symbol index %ld\n", symidx); return (-1); case R_X86_64_GLOB_DAT: /* S */ case R_X86_64_JMP_SLOT: /* XXX need addend + offset */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); if (*where != addr) *where = addr; break; case R_X86_64_RELATIVE: /* B + A */ addr = elf_relocaddr(lf, relocbase + addend); val = addr; if (*where != val) *where = val; break; case R_X86_64_IRELATIVE: addr = relocbase + addend; val = ((Elf64_Addr (*)(void))addr)(); if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %ld, " "symbol index %ld\n", rtype, symidx); return (-1); } return (0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, false, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, false, lookup)); } int elf_reloc_late(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, true, lookup)); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } diff --git a/sys/arm/arm/elf_machdep.c b/sys/arm/arm/elf_machdep.c index a20268f42f67..d993ba57c7d0 100644 --- a/sys/arm/arm/elf_machdep.c +++ b/sys/arm/arm/elf_machdep.c @@ -1,342 +1,345 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef VFP #include #endif #include "opt_ddb.h" /* for OPT_DDB */ #include "opt_global.h" /* for OPT_KDTRACE_HOOKS */ #include "opt_stack.h" /* for OPT_STACK */ static boolean_t elf32_arm_abi_supported(struct image_params *, int32_t *, uint32_t *); u_long elf_hwcap; u_long elf_hwcap2; struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ASLR | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER | SV_ABI_FREEBSD | SV_ILP32, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &elf_hwcap, .sv_hwcap2 = &elf_hwcap2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_ARM, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported= elf32_arm_abi_supported, }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_info); static boolean_t elf32_arm_abi_supported(struct image_params *imgp, int32_t *osrel __unused, uint32_t *fctl0 __unused) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; /* * When configured for EABI, FreeBSD supports EABI vesions 4 and 5. */ if (EF_ARM_EABI_VERSION(hdr->e_flags) < EF_ARM_EABI_FREEBSD_MIN) { if (bootverbose) uprintf("Attempting to execute non EABI binary (rev %d) image %s", EF_ARM_EABI_VERSION(hdr->e_flags), imgp->args->fname); return (FALSE); } return (TRUE); } void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { #ifdef VFP mcontext_vfp_t vfp; if (dst != NULL) { get_vfpcontext(td, &vfp); *off = elf32_populate_note(NT_ARM_VFP, &vfp, dst, sizeof(vfp), NULL); } else *off = elf32_populate_note(NT_ARM_VFP, NULL, NULL, sizeof(vfp), NULL); #endif } bool elf_is_ifunc_reloc(Elf_Size r_info __unused) { return (false); } /* * It is possible for the compiler to emit relocations for unaligned data. * We handle this situation with these inlines. */ #define RELOC_ALIGNED_P(x) \ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) static __inline Elf_Addr load_ptr(Elf_Addr *where) { Elf_Addr res; if (RELOC_ALIGNED_P(where)) return *where; memcpy(&res, where, sizeof(res)); return (res); } static __inline void store_ptr(Elf_Addr *where, Elf_Addr val) { if (RELOC_ALIGNED_P(where)) *where = val; else memcpy(where, &val, sizeof(val)); } #undef RELOC_ALIGNED_P /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = load_ptr(where); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if (local) { if (rtype == R_ARM_RELATIVE) { /* A + B */ addr = elf_relocaddr(lf, relocbase + addend); if (load_ptr(where) != addr) store_ptr(where, addr); } return (0); } switch (rtype) { case R_ARM_NONE: /* none */ break; case R_ARM_ABS32: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); store_ptr(where, addr + load_ptr(where)); break; case R_ARM_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation, " "symbol index %d\n", symidx); return (-1); break; case R_ARM_JUMP_SLOT: error = lookup(lf, symidx, 1, &addr); if (error == 0) { store_ptr(where, addr); return (0); } return (-1); case R_ARM_RELATIVE: break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", rtype, symidx); return (-1); } return(0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf) { /* * The pmap code does not do an icache sync upon establishing executable * mappings in the kernel pmap. It's an optimization based on the fact * that kernel memory allocations always have EXECUTABLE protection even * when the memory isn't going to hold executable code. The only time * kernel memory holding instructions does need a sync is after loading * a kernel module, and that's when this function gets called. * * This syncs data and instruction caches after loading a module. We * don't worry about the kernel itself (lf->id is 1) as locore.S did * that on entry. Even if data cache maintenance was done by IO code, * the relocation fixup process creates dirty cache entries that we must * write back before doing icache sync. The instruction cache sync also * invalidates the branch predictor cache on platforms that have one. */ if (lf->id == 1) return (0); dcache_wb_pou((vm_offset_t)lf->address, (vm_size_t)lf->size); icache_inv_all(); #if defined(DDB) || defined(KDTRACE_HOOKS) || defined(STACK) /* * Inform the stack(9) code of the new module, so it can acquire its * per-module unwind data. */ unwind_module_loaded(lf); #endif return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf) { #if defined(DDB) || defined(KDTRACE_HOOKS) || defined(STACK) /* Inform the stack(9) code that this module is gone. */ unwind_module_unloaded(lf); #endif return (0); } diff --git a/sys/arm64/arm64/elf32_machdep.c b/sys/arm64/arm64/elf32_machdep.c index e5ce1556558e..919ef20425a3 100644 --- a/sys/arm64/arm64/elf32_machdep.c +++ b/sys/arm64/arm64/elf32_machdep.c @@ -1,289 +1,292 @@ /*- * Copyright (c) 2014, 2015 The FreeBSD Foundation. * Copyright (c) 2014, 2017 Andrew Turner. * Copyright (c) 2018 Olivier Houchard * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define __ELF_WORD_SIZE 32 #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #ifdef VFP #include #endif #include #define FREEBSD32_MINUSER 0x00001000 #define FREEBSD32_MAXUSER ((1ul << 32) - PAGE_SIZE) #define FREEBSD32_SHAREDPAGE (FREEBSD32_MAXUSER - PAGE_SIZE) #define FREEBSD32_USRSTACK FREEBSD32_SHAREDPAGE extern const char *freebsd32_syscallnames[]; extern char aarch32_sigcode[]; extern int sz_aarch32_sigcode; static int freebsd32_fetch_syscall_args(struct thread *td); static void freebsd32_setregs(struct thread *td, struct image_params *imgp, u_long stack); static void freebsd32_set_syscall_retval(struct thread *, int); static boolean_t elf32_arm_abi_supported(struct image_params *, int32_t *, uint32_t *); extern void freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask); u_long __read_frequently elf32_hwcap; u_long __read_frequently elf32_hwcap2; static struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = freebsd32_sysent, .sv_transtrap = NULL, .sv_fixup = elf32_freebsd_fixup, .sv_sendsig = freebsd32_sendsig, .sv_sigcode = aarch32_sigcode, .sv_szsigcode = &sz_aarch32_sigcode, .sv_name = "FreeBSD ELF32", .sv_coredump = elf32_coredump, .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = elf32_prepare_notes, .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = FREEBSD32_MINUSER, .sv_maxuser = FREEBSD32_MAXUSER, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_psstringssz = sizeof(struct freebsd32_ps_strings), .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_auxargs = elf32_freebsd_copyout_auxargs, .sv_copyout_strings = freebsd32_copyout_strings, .sv_setregs = freebsd32_setregs, .sv_fixlimit = NULL, // XXX .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = freebsd32_set_syscall_retval, .sv_fetch_syscall_args = freebsd32_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &elf32_hwcap, .sv_hwcap2 = &elf32_hwcap2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd32_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_ARM, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = "/libexec/ld-elf32.so.1", .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported= elf32_arm_abi_supported, }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)elf32_insert_brand_entry, &freebsd32_brand_info); static boolean_t elf32_arm_abi_supported(struct image_params *imgp, int32_t *osrel __unused, uint32_t *fctl0 __unused) { const Elf32_Ehdr *hdr; /* Check if we support AArch32 */ if (ID_AA64PFR0_EL0_VAL(READ_SPECIALREG(id_aa64pfr0_el1)) != ID_AA64PFR0_EL0_64_32) return (FALSE); #define EF_ARM_EABI_VERSION(x) (((x) & EF_ARM_EABIMASK) >> 24) #define EF_ARM_EABI_FREEBSD_MIN 4 hdr = (const Elf32_Ehdr *)imgp->image_header; if (EF_ARM_EABI_VERSION(hdr->e_flags) < EF_ARM_EABI_FREEBSD_MIN) { if (bootverbose) uprintf("Attempting to execute non EABI binary " "(rev %d) image %s", EF_ARM_EABI_VERSION(hdr->e_flags), imgp->args->fname); return (FALSE); } return (TRUE); } static int freebsd32_fetch_syscall_args(struct thread *td) { struct proc *p; register_t *ap; struct syscall_args *sa; int error, i, nap, narg; unsigned int args[4]; nap = 4; p = td->td_proc; ap = td->td_frame->tf_x; sa = &td->td_sa; /* r7 is the syscall id */ sa->code = td->td_frame->tf_x[7]; sa->original_code = sa->code; if (sa->code == SYS_syscall) { sa->code = *ap++; nap--; } else if (sa->code == SYS___syscall) { sa->code = ap[1]; nap -= 2; ap += 2; } if (sa->code >= p->p_sysent->sv_size) sa->callp = &p->p_sysent->sv_table[0]; else sa->callp = &p->p_sysent->sv_table[sa->code]; narg = sa->callp->sy_narg; for (i = 0; i < nap; i++) sa->args[i] = ap[i]; if (narg > nap) { if (narg - nap > nitems(args)) panic("Too many system call arguiments"); error = copyin((void *)td->td_frame->tf_x[13], args, (narg - nap) * sizeof(int)); if (error != 0) return (error); for (i = 0; i < (narg - nap); i++) sa->args[i + nap] = args[i]; } td->td_retval[0] = 0; td->td_retval[1] = 0; return (0); } static void freebsd32_set_syscall_retval(struct thread *td, int error) { struct trapframe *frame; frame = td->td_frame; switch (error) { case 0: frame->tf_x[0] = td->td_retval[0]; frame->tf_x[1] = td->td_retval[1]; frame->tf_spsr &= ~PSR_C; break; case ERESTART: /* * Reconstruct the pc to point at the swi. */ if ((frame->tf_spsr & PSR_T) != 0) frame->tf_elr -= 2; //THUMB_INSN_SIZE; else frame->tf_elr -= 4; //INSN_SIZE; break; case EJUSTRETURN: /* nothing to do */ break; default: frame->tf_x[0] = error; frame->tf_spsr |= PSR_C; break; } } static void freebsd32_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) { struct trapframe *tf = td->td_frame; struct pcb *pcb = td->td_pcb; memset(tf, 0, sizeof(struct trapframe)); /* * We need to set x0 for init as it doesn't call * cpu_set_syscall_retval to copy the value. We also * need to set td_retval for the cases where we do. */ tf->tf_x[0] = stack; /* SP_usr is mapped to x13 */ tf->tf_x[13] = stack; /* LR_usr is mapped to x14 */ tf->tf_x[14] = imgp->entry_addr; tf->tf_elr = imgp->entry_addr; tf->tf_spsr = PSR_M_32; if ((uint32_t)imgp->entry_addr & 1) tf->tf_spsr |= PSR_T; #ifdef VFP vfp_reset_state(td, pcb); #endif /* * Clear debug register state. It is not applicable to the new process. */ bzero(&pcb->pcb_dbg_regs, sizeof(pcb->pcb_dbg_regs)); } void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { /* XXX: VFP */ } diff --git a/sys/arm64/arm64/elf_machdep.c b/sys/arm64/arm64/elf_machdep.c index 3de0f82839e6..d98512b71e6c 100644 --- a/sys/arm64/arm64/elf_machdep.c +++ b/sys/arm64/arm64/elf_machdep.c @@ -1,291 +1,294 @@ /*- * Copyright (c) 2014, 2015 The FreeBSD Foundation. * Copyright (c) 2014 Andrew Turner. * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include "linker_if.h" u_long __read_frequently elf_hwcap; u_long __read_frequently elf_hwcap2; static struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_SHP | SV_TIMEKEEP | SV_ABI_FREEBSD | SV_LP64 | SV_ASLR | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &elf_hwcap, .sv_hwcap2 = &elf_hwcap2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_AARCH64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info); void elf64_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) { } bool elf_is_ifunc_reloc(Elf_Size r_info __unused) { return (ELF_R_TYPE(r_info) == R_AARCH64_IRELATIVE); } static int reloc_instr_imm(Elf32_Addr *where, Elf_Addr val, u_int msb, u_int lsb) { /* Check bounds: upper bits must be all ones or all zeros. */ if ((uint64_t)((int64_t)val >> (msb + 1)) + 1 > 1) return (-1); val >>= lsb; val &= (1 << (msb - lsb + 1)) - 1; *where |= (Elf32_Addr)val; return (0); } /* * Process a relocation. Support for some static relocations is required * in order for the -zifunc-noplt optimization to work. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int flags, elf_lookup_fn lookup) { #define ARM64_ELF_RELOC_LOCAL (1 << 0) #define ARM64_ELF_RELOC_LATE_IFUNC (1 << 1) Elf_Addr *where, addr, addend, val; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = *where; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if ((flags & ARM64_ELF_RELOC_LATE_IFUNC) != 0) { KASSERT(type == ELF_RELOC_RELA, ("Only RELA ifunc relocations are supported")); if (rtype != R_AARCH64_IRELATIVE) return (0); } if ((flags & ARM64_ELF_RELOC_LOCAL) != 0) { if (rtype == R_AARCH64_RELATIVE) *where = elf_relocaddr(lf, relocbase + addend); return (0); } error = 0; switch (rtype) { case R_AARCH64_NONE: case R_AARCH64_RELATIVE: break; case R_AARCH64_TSTBR14: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 15, 2); break; case R_AARCH64_CONDBR19: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 20, 2); break; case R_AARCH64_JUMP26: case R_AARCH64_CALL26: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 27, 2); break; case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: case R_AARCH64_JUMP_SLOT: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); *where = addr + addend; break; case R_AARCH64_IRELATIVE: addr = relocbase + addend; val = ((Elf64_Addr (*)(void))addr)(); if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", rtype, symidx); return (-1); } return (error); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, ARM64_ELF_RELOC_LOCAL, lookup)); } /* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_late(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, ARM64_ELF_RELOC_LATE_IFUNC, lookup)); } int elf_cpu_load_file(linker_file_t lf) { if (lf->id != 1) cpu_icache_sync_range((vm_offset_t)lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 28262f011830..790f2597b75f 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -1,3976 +1,3998 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ffclock.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ktrace.h" #define __ELF_WORD_SIZE 32 #ifdef COMPAT_FREEBSD11 #define _WANT_FREEBSD11_KEVENT #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Must come after sys/malloc.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Must come after sys/selinfo.h */ #include /* Must come after sys/selinfo.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #ifdef INET #include #endif #include #include #include #include #include #include #include #include #ifdef __amd64__ #include #endif #include #include #include #include #include #include #include FEATURE(compat_freebsd_32bit, "Compatible with 32-bit FreeBSD"); struct ptrace_io_desc32 { int piod_op; uint32_t piod_offs; uint32_t piod_addr; uint32_t piod_len; }; struct ptrace_sc_ret32 { uint32_t sr_retval[2]; int sr_error; }; struct ptrace_vm_entry32 { int pve_entry; int pve_timestamp; uint32_t pve_start; uint32_t pve_end; uint32_t pve_offset; u_int pve_prot; u_int pve_pathlen; int32_t pve_fileid; u_int pve_fsid; uint32_t pve_path; }; #ifdef __amd64__ CTASSERT(sizeof(struct timeval32) == 8); CTASSERT(sizeof(struct timespec32) == 8); CTASSERT(sizeof(struct itimerval32) == 16); CTASSERT(sizeof(struct bintime32) == 12); #endif CTASSERT(sizeof(struct ostatfs32) == 256); #ifdef __amd64__ CTASSERT(sizeof(struct rusage32) == 72); #endif CTASSERT(sizeof(struct sigaltstack32) == 12); #ifdef __amd64__ CTASSERT(sizeof(struct kevent32) == 56); #else CTASSERT(sizeof(struct kevent32) == 64); #endif CTASSERT(sizeof(struct iovec32) == 8); CTASSERT(sizeof(struct msghdr32) == 28); #ifdef __amd64__ CTASSERT(sizeof(struct stat32) == 208); CTASSERT(sizeof(struct freebsd11_stat32) == 96); #endif CTASSERT(sizeof(struct sigaction32) == 24); static int freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count); static int freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count); static int freebsd32_user_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, const struct timespec32 *ua_rqtp, struct timespec32 *ua_rmtp); void freebsd32_rusage_out(const struct rusage *s, struct rusage32 *s32) { TV_CP(*s, *s32, ru_utime); TV_CP(*s, *s32, ru_stime); CP(*s, *s32, ru_maxrss); CP(*s, *s32, ru_ixrss); CP(*s, *s32, ru_idrss); CP(*s, *s32, ru_isrss); CP(*s, *s32, ru_minflt); CP(*s, *s32, ru_majflt); CP(*s, *s32, ru_nswap); CP(*s, *s32, ru_inblock); CP(*s, *s32, ru_oublock); CP(*s, *s32, ru_msgsnd); CP(*s, *s32, ru_msgrcv); CP(*s, *s32, ru_nsignals); CP(*s, *s32, ru_nvcsw); CP(*s, *s32, ru_nivcsw); } int freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) { int error, status; struct rusage32 ru32; struct rusage ru, *rup; if (uap->rusage != NULL) rup = &ru; else rup = NULL; error = kern_wait(td, uap->pid, &status, uap->options, rup); if (error) return (error); if (uap->status != NULL) error = copyout(&status, uap->status, sizeof(status)); if (uap->rusage != NULL && error == 0) { freebsd32_rusage_out(&ru, &ru32); error = copyout(&ru32, uap->rusage, sizeof(ru32)); } return (error); } int freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap) { struct __wrusage32 wru32; struct __wrusage wru, *wrup; struct siginfo32 si32; struct __siginfo si, *sip; int error, status; if (uap->wrusage != NULL) wrup = &wru; else wrup = NULL; if (uap->info != NULL) { sip = &si; bzero(sip, sizeof(*sip)); } else sip = NULL; error = kern_wait6(td, uap->idtype, PAIR32TO64(id_t, uap->id), &status, uap->options, wrup, sip); if (error != 0) return (error); if (uap->status != NULL) error = copyout(&status, uap->status, sizeof(status)); if (uap->wrusage != NULL && error == 0) { freebsd32_rusage_out(&wru.wru_self, &wru32.wru_self); freebsd32_rusage_out(&wru.wru_children, &wru32.wru_children); error = copyout(&wru32, uap->wrusage, sizeof(wru32)); } if (uap->info != NULL && error == 0) { siginfo_to_siginfo32 (&si, &si32); error = copyout(&si32, uap->info, sizeof(si32)); } return (error); } #ifdef COMPAT_FREEBSD4 static void copy_statfs(struct statfs *in, struct ostatfs32 *out) { statfs_scale_blocks(in, INT32_MAX); bzero(out, sizeof(*out)); CP(*in, *out, f_bsize); out->f_iosize = MIN(in->f_iosize, INT32_MAX); CP(*in, *out, f_blocks); CP(*in, *out, f_bfree); CP(*in, *out, f_bavail); out->f_files = MIN(in->f_files, INT32_MAX); out->f_ffree = MIN(in->f_ffree, INT32_MAX); CP(*in, *out, f_fsid); CP(*in, *out, f_owner); CP(*in, *out, f_type); CP(*in, *out, f_flags); out->f_syncwrites = MIN(in->f_syncwrites, INT32_MAX); out->f_asyncwrites = MIN(in->f_asyncwrites, INT32_MAX); strlcpy(out->f_fstypename, in->f_fstypename, MFSNAMELEN); strlcpy(out->f_mntonname, in->f_mntonname, min(MNAMELEN, FREEBSD4_OMNAMELEN)); out->f_syncreads = MIN(in->f_syncreads, INT32_MAX); out->f_asyncreads = MIN(in->f_asyncreads, INT32_MAX); strlcpy(out->f_mntfromname, in->f_mntfromname, min(MNAMELEN, FREEBSD4_OMNAMELEN)); } #endif int freebsd32_getfsstat(struct thread *td, struct freebsd32_getfsstat_args *uap) { size_t count; int error; if (uap->bufsize < 0 || uap->bufsize > SIZE_MAX) return (EINVAL); error = kern_getfsstat(td, &uap->buf, uap->bufsize, &count, UIO_USERSPACE, uap->mode); if (error == 0) td->td_retval[0] = count; return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_getfsstat(struct thread *td, struct freebsd4_freebsd32_getfsstat_args *uap) { struct statfs *buf, *sp; struct ostatfs32 stat32; size_t count, size, copycount; int error; count = uap->bufsize / sizeof(struct ostatfs32); size = count * sizeof(struct statfs); error = kern_getfsstat(td, &buf, size, &count, UIO_SYSSPACE, uap->mode); if (size > 0) { sp = buf; copycount = count; while (copycount > 0 && error == 0) { copy_statfs(sp, &stat32); error = copyout(&stat32, uap->buf, sizeof(stat32)); sp++; uap->buf++; copycount--; } free(buf, M_STATFS); } if (error == 0) td->td_retval[0] = count; return (error); } #endif #ifdef COMPAT_FREEBSD11 int freebsd11_freebsd32_getfsstat(struct thread *td, struct freebsd11_freebsd32_getfsstat_args *uap) { return(kern_freebsd11_getfsstat(td, uap->buf, uap->bufsize, uap->mode)); } #endif int freebsd32_sigaltstack(struct thread *td, struct freebsd32_sigaltstack_args *uap) { struct sigaltstack32 s32; struct sigaltstack ss, oss, *ssp; int error; if (uap->ss != NULL) { error = copyin(uap->ss, &s32, sizeof(s32)); if (error) return (error); PTRIN_CP(s32, ss, ss_sp); CP(s32, ss, ss_size); CP(s32, ss, ss_flags); ssp = &ss; } else ssp = NULL; error = kern_sigaltstack(td, ssp, &oss); if (error == 0 && uap->oss != NULL) { PTROUT_CP(oss, s32, ss_sp); CP(oss, s32, ss_size); CP(oss, s32, ss_flags); error = copyout(&s32, uap->oss, sizeof(s32)); } return (error); } /* * Custom version of exec_copyin_args() so that we can translate * the pointers. */ int freebsd32_exec_copyin_args(struct image_args *args, const char *fname, enum uio_seg segflg, uint32_t *argv, uint32_t *envv) { char *argp, *envp; uint32_t *p32, arg; int error; bzero(args, sizeof(*args)); if (argv == NULL) return (EFAULT); /* * Allocate demand-paged memory for the file name, argument, and * environment strings. */ error = exec_alloc_args(args); if (error != 0) return (error); /* * Copy the file name. */ error = exec_args_add_fname(args, fname, segflg); if (error != 0) goto err_exit; /* * extract arguments first */ p32 = argv; for (;;) { error = copyin(p32++, &arg, sizeof(arg)); if (error) goto err_exit; if (arg == 0) break; argp = PTRIN(arg); error = exec_args_add_arg(args, argp, UIO_USERSPACE); if (error != 0) goto err_exit; } /* * extract environment strings */ if (envv) { p32 = envv; for (;;) { error = copyin(p32++, &arg, sizeof(arg)); if (error) goto err_exit; if (arg == 0) break; envp = PTRIN(arg); error = exec_args_add_env(args, envp, UIO_USERSPACE); if (error != 0) goto err_exit; } } return (0); err_exit: exec_free_args(args); return (error); } int freebsd32_execve(struct thread *td, struct freebsd32_execve_args *uap) { struct image_args eargs; struct vmspace *oldvmspace; int error; error = pre_execve(td, &oldvmspace); if (error != 0) return (error); error = freebsd32_exec_copyin_args(&eargs, uap->fname, UIO_USERSPACE, uap->argv, uap->envv); if (error == 0) error = kern_execve(td, &eargs, NULL, oldvmspace); post_execve(td, error, oldvmspace); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } int freebsd32_fexecve(struct thread *td, struct freebsd32_fexecve_args *uap) { struct image_args eargs; struct vmspace *oldvmspace; int error; error = pre_execve(td, &oldvmspace); if (error != 0) return (error); error = freebsd32_exec_copyin_args(&eargs, NULL, UIO_SYSSPACE, uap->argv, uap->envv); if (error == 0) { eargs.fd = uap->fd; error = kern_execve(td, &eargs, NULL, oldvmspace); } post_execve(td, error, oldvmspace); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } int freebsd32_mknodat(struct thread *td, struct freebsd32_mknodat_args *uap) { return (kern_mknodat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode, PAIR32TO64(dev_t, uap->dev))); } int freebsd32_mprotect(struct thread *td, struct freebsd32_mprotect_args *uap) { int prot; prot = uap->prot; #if defined(__amd64__) if (i386_read_exec && (prot & PROT_READ) != 0) prot |= PROT_EXEC; #endif return (kern_mprotect(td, (uintptr_t)PTRIN(uap->addr), uap->len, prot)); } int freebsd32_mmap(struct thread *td, struct freebsd32_mmap_args *uap) { int prot; prot = uap->prot; #if defined(__amd64__) if (i386_read_exec && (prot & PROT_READ)) prot |= PROT_EXEC; #endif return (kern_mmap(td, &(struct mmap_req){ .mr_hint = (uintptr_t)uap->addr, .mr_len = uap->len, .mr_prot = prot, .mr_flags = uap->flags, .mr_fd = uap->fd, .mr_pos = PAIR32TO64(off_t, uap->pos), })); } #ifdef COMPAT_FREEBSD6 int freebsd6_freebsd32_mmap(struct thread *td, struct freebsd6_freebsd32_mmap_args *uap) { int prot; prot = uap->prot; #if defined(__amd64__) if (i386_read_exec && (prot & PROT_READ)) prot |= PROT_EXEC; #endif return (kern_mmap(td, &(struct mmap_req){ .mr_hint = (uintptr_t)uap->addr, .mr_len = uap->len, .mr_prot = prot, .mr_flags = uap->flags, .mr_fd = uap->fd, .mr_pos = PAIR32TO64(off_t, uap->pos), })); } #endif #ifdef COMPAT_43 int ofreebsd32_mmap(struct thread *td, struct ofreebsd32_mmap_args *uap) { return (kern_ommap(td, (uintptr_t)uap->addr, uap->len, uap->prot, uap->flags, uap->fd, uap->pos)); } #endif int freebsd32_setitimer(struct thread *td, struct freebsd32_setitimer_args *uap) { struct itimerval itv, oitv, *itvp; struct itimerval32 i32; int error; if (uap->itv != NULL) { error = copyin(uap->itv, &i32, sizeof(i32)); if (error) return (error); TV_CP(i32, itv, it_interval); TV_CP(i32, itv, it_value); itvp = &itv; } else itvp = NULL; error = kern_setitimer(td, uap->which, itvp, &oitv); if (error || uap->oitv == NULL) return (error); TV_CP(oitv, i32, it_interval); TV_CP(oitv, i32, it_value); return (copyout(&i32, uap->oitv, sizeof(i32))); } int freebsd32_getitimer(struct thread *td, struct freebsd32_getitimer_args *uap) { struct itimerval itv; struct itimerval32 i32; int error; error = kern_getitimer(td, uap->which, &itv); if (error || uap->itv == NULL) return (error); TV_CP(itv, i32, it_interval); TV_CP(itv, i32, it_value); return (copyout(&i32, uap->itv, sizeof(i32))); } int freebsd32_select(struct thread *td, struct freebsd32_select_args *uap) { struct timeval32 tv32; struct timeval tv, *tvp; int error; if (uap->tv != NULL) { error = copyin(uap->tv, &tv32, sizeof(tv32)); if (error) return (error); CP(tv32, tv, tv_sec); CP(tv32, tv, tv_usec); tvp = &tv; } else tvp = NULL; /* * XXX Do pointers need PTRIN()? */ return (kern_select(td, uap->nd, uap->in, uap->ou, uap->ex, tvp, sizeof(int32_t) * 8)); } int freebsd32_pselect(struct thread *td, struct freebsd32_pselect_args *uap) { struct timespec32 ts32; struct timespec ts; struct timeval tv, *tvp; sigset_t set, *uset; int error; if (uap->ts != NULL) { error = copyin(uap->ts, &ts32, sizeof(ts32)); if (error != 0) return (error); CP(ts32, ts, tv_sec); CP(ts32, ts, tv_nsec); TIMESPEC_TO_TIMEVAL(&tv, &ts); tvp = &tv; } else tvp = NULL; if (uap->sm != NULL) { error = copyin(uap->sm, &set, sizeof(set)); if (error != 0) return (error); uset = &set; } else uset = NULL; /* * XXX Do pointers need PTRIN()? */ error = kern_pselect(td, uap->nd, uap->in, uap->ou, uap->ex, tvp, uset, sizeof(int32_t) * 8); return (error); } /* * Copy 'count' items into the destination list pointed to by uap->eventlist. */ static int freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count) { struct freebsd32_kevent_args *uap; struct kevent32 ks32[KQ_NEVENTS]; uint64_t e; int i, j, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); uap = (struct freebsd32_kevent_args *)arg; for (i = 0; i < count; i++) { CP(kevp[i], ks32[i], ident); CP(kevp[i], ks32[i], filter); CP(kevp[i], ks32[i], flags); CP(kevp[i], ks32[i], fflags); #if BYTE_ORDER == LITTLE_ENDIAN ks32[i].data1 = kevp[i].data; ks32[i].data2 = kevp[i].data >> 32; #else ks32[i].data1 = kevp[i].data >> 32; ks32[i].data2 = kevp[i].data; #endif PTROUT_CP(kevp[i], ks32[i], udata); for (j = 0; j < nitems(kevp->ext); j++) { e = kevp[i].ext[j]; #if BYTE_ORDER == LITTLE_ENDIAN ks32[i].ext64[2 * j] = e; ks32[i].ext64[2 * j + 1] = e >> 32; #else ks32[i].ext64[2 * j] = e >> 32; ks32[i].ext64[2 * j + 1] = e; #endif } } error = copyout(ks32, uap->eventlist, count * sizeof *ks32); if (error == 0) uap->eventlist += count; return (error); } /* * Copy 'count' items from the list pointed to by uap->changelist. */ static int freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count) { struct freebsd32_kevent_args *uap; struct kevent32 ks32[KQ_NEVENTS]; uint64_t e; int i, j, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); uap = (struct freebsd32_kevent_args *)arg; error = copyin(uap->changelist, ks32, count * sizeof *ks32); if (error) goto done; uap->changelist += count; for (i = 0; i < count; i++) { CP(ks32[i], kevp[i], ident); CP(ks32[i], kevp[i], filter); CP(ks32[i], kevp[i], flags); CP(ks32[i], kevp[i], fflags); kevp[i].data = PAIR32TO64(uint64_t, ks32[i].data); PTRIN_CP(ks32[i], kevp[i], udata); for (j = 0; j < nitems(kevp->ext); j++) { #if BYTE_ORDER == LITTLE_ENDIAN e = ks32[i].ext64[2 * j + 1]; e <<= 32; e += ks32[i].ext64[2 * j]; #else e = ks32[i].ext64[2 * j]; e <<= 32; e += ks32[i].ext64[2 * j + 1]; #endif kevp[i].ext[j] = e; } } done: return (error); } int freebsd32_kevent(struct thread *td, struct freebsd32_kevent_args *uap) { struct timespec32 ts32; struct timespec ts, *tsp; struct kevent_copyops k_ops = { .arg = uap, .k_copyout = freebsd32_kevent_copyout, .k_copyin = freebsd32_kevent_copyin, }; #ifdef KTRACE struct kevent32 *eventlist = uap->eventlist; #endif int error; if (uap->timeout) { error = copyin(uap->timeout, &ts32, sizeof(ts32)); if (error) return (error); CP(ts32, ts, tv_sec); CP(ts32, ts, tv_nsec); tsp = &ts; } else tsp = NULL; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT_ARRAY)) ktrstructarray("kevent32", UIO_USERSPACE, uap->changelist, uap->nchanges, sizeof(struct kevent32)); #endif error = kern_kevent(td, uap->fd, uap->nchanges, uap->nevents, &k_ops, tsp); #ifdef KTRACE if (error == 0 && KTRPOINT(td, KTR_STRUCT_ARRAY)) ktrstructarray("kevent32", UIO_USERSPACE, eventlist, td->td_retval[0], sizeof(struct kevent32)); #endif return (error); } #ifdef COMPAT_FREEBSD11 static int freebsd32_kevent11_copyout(void *arg, struct kevent *kevp, int count) { struct freebsd11_freebsd32_kevent_args *uap; struct freebsd11_kevent32 ks32[KQ_NEVENTS]; int i, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); uap = (struct freebsd11_freebsd32_kevent_args *)arg; for (i = 0; i < count; i++) { CP(kevp[i], ks32[i], ident); CP(kevp[i], ks32[i], filter); CP(kevp[i], ks32[i], flags); CP(kevp[i], ks32[i], fflags); CP(kevp[i], ks32[i], data); PTROUT_CP(kevp[i], ks32[i], udata); } error = copyout(ks32, uap->eventlist, count * sizeof *ks32); if (error == 0) uap->eventlist += count; return (error); } /* * Copy 'count' items from the list pointed to by uap->changelist. */ static int freebsd32_kevent11_copyin(void *arg, struct kevent *kevp, int count) { struct freebsd11_freebsd32_kevent_args *uap; struct freebsd11_kevent32 ks32[KQ_NEVENTS]; int i, j, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); uap = (struct freebsd11_freebsd32_kevent_args *)arg; error = copyin(uap->changelist, ks32, count * sizeof *ks32); if (error) goto done; uap->changelist += count; for (i = 0; i < count; i++) { CP(ks32[i], kevp[i], ident); CP(ks32[i], kevp[i], filter); CP(ks32[i], kevp[i], flags); CP(ks32[i], kevp[i], fflags); CP(ks32[i], kevp[i], data); PTRIN_CP(ks32[i], kevp[i], udata); for (j = 0; j < nitems(kevp->ext); j++) kevp[i].ext[j] = 0; } done: return (error); } int freebsd11_freebsd32_kevent(struct thread *td, struct freebsd11_freebsd32_kevent_args *uap) { struct timespec32 ts32; struct timespec ts, *tsp; struct kevent_copyops k_ops = { .arg = uap, .k_copyout = freebsd32_kevent11_copyout, .k_copyin = freebsd32_kevent11_copyin, }; #ifdef KTRACE struct freebsd11_kevent32 *eventlist = uap->eventlist; #endif int error; if (uap->timeout) { error = copyin(uap->timeout, &ts32, sizeof(ts32)); if (error) return (error); CP(ts32, ts, tv_sec); CP(ts32, ts, tv_nsec); tsp = &ts; } else tsp = NULL; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT_ARRAY)) ktrstructarray("freebsd11_kevent32", UIO_USERSPACE, uap->changelist, uap->nchanges, sizeof(struct freebsd11_kevent32)); #endif error = kern_kevent(td, uap->fd, uap->nchanges, uap->nevents, &k_ops, tsp); #ifdef KTRACE if (error == 0 && KTRPOINT(td, KTR_STRUCT_ARRAY)) ktrstructarray("freebsd11_kevent32", UIO_USERSPACE, eventlist, td->td_retval[0], sizeof(struct freebsd11_kevent32)); #endif return (error); } #endif int freebsd32_gettimeofday(struct thread *td, struct freebsd32_gettimeofday_args *uap) { struct timeval atv; struct timeval32 atv32; struct timezone rtz; int error = 0; if (uap->tp) { microtime(&atv); CP(atv, atv32, tv_sec); CP(atv, atv32, tv_usec); error = copyout(&atv32, uap->tp, sizeof (atv32)); } if (error == 0 && uap->tzp != NULL) { rtz.tz_minuteswest = 0; rtz.tz_dsttime = 0; error = copyout(&rtz, uap->tzp, sizeof (rtz)); } return (error); } int freebsd32_getrusage(struct thread *td, struct freebsd32_getrusage_args *uap) { struct rusage32 s32; struct rusage s; int error; error = kern_getrusage(td, uap->who, &s); if (error == 0) { freebsd32_rusage_out(&s, &s32); error = copyout(&s32, uap->rusage, sizeof(s32)); } return (error); } static void ptrace_lwpinfo_to32(const struct ptrace_lwpinfo *pl, struct ptrace_lwpinfo32 *pl32) { bzero(pl32, sizeof(*pl32)); pl32->pl_lwpid = pl->pl_lwpid; pl32->pl_event = pl->pl_event; pl32->pl_flags = pl->pl_flags; pl32->pl_sigmask = pl->pl_sigmask; pl32->pl_siglist = pl->pl_siglist; siginfo_to_siginfo32(&pl->pl_siginfo, &pl32->pl_siginfo); strcpy(pl32->pl_tdname, pl->pl_tdname); pl32->pl_child_pid = pl->pl_child_pid; pl32->pl_syscall_code = pl->pl_syscall_code; pl32->pl_syscall_narg = pl->pl_syscall_narg; } static void ptrace_sc_ret_to32(const struct ptrace_sc_ret *psr, struct ptrace_sc_ret32 *psr32) { bzero(psr32, sizeof(*psr32)); psr32->sr_retval[0] = psr->sr_retval[0]; psr32->sr_retval[1] = psr->sr_retval[1]; psr32->sr_error = psr->sr_error; } int freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap) { union { struct ptrace_io_desc piod; struct ptrace_lwpinfo pl; struct ptrace_vm_entry pve; struct ptrace_coredump pc; struct dbreg32 dbreg; struct fpreg32 fpreg; struct reg32 reg; + struct iovec vec; register_t args[nitems(td->td_sa.args)]; struct ptrace_sc_ret psr; int ptevents; } r; union { struct ptrace_io_desc32 piod; struct ptrace_lwpinfo32 pl; struct ptrace_vm_entry32 pve; struct ptrace_coredump32 pc; uint32_t args[nitems(td->td_sa.args)]; struct ptrace_sc_ret32 psr; + struct iovec32 vec; } r32; void *addr; int data, error, i; if (!allow_ptrace) return (ENOSYS); error = 0; AUDIT_ARG_PID(uap->pid); AUDIT_ARG_CMD(uap->req); AUDIT_ARG_VALUE(uap->data); addr = &r; data = uap->data; switch (uap->req) { case PT_GET_EVENT_MASK: case PT_GET_SC_ARGS: case PT_GET_SC_RET: break; case PT_LWPINFO: if (uap->data > sizeof(r32.pl)) return (EINVAL); /* * Pass size of native structure in 'data'. Truncate * if necessary to avoid siginfo. */ data = sizeof(r.pl); if (uap->data < offsetof(struct ptrace_lwpinfo32, pl_siginfo) + sizeof(struct siginfo32)) data = offsetof(struct ptrace_lwpinfo, pl_siginfo); break; case PT_GETREGS: bzero(&r.reg, sizeof(r.reg)); break; case PT_GETFPREGS: bzero(&r.fpreg, sizeof(r.fpreg)); break; case PT_GETDBREGS: bzero(&r.dbreg, sizeof(r.dbreg)); break; case PT_SETREGS: error = copyin(uap->addr, &r.reg, sizeof(r.reg)); break; case PT_SETFPREGS: error = copyin(uap->addr, &r.fpreg, sizeof(r.fpreg)); break; case PT_SETDBREGS: error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg)); break; + case PT_SETREGSET: + error = copyin(uap->addr, &r32.vec, sizeof(r32.vec)); + if (error != 0) + break; + + r.vec.iov_len = r32.vec.iov_len; + r.vec.iov_base = PTRIN(r32.vec.iov_base); + break; + case PT_GETREGSET: + error = copyin(uap->addr, &r32.vec, sizeof(r32.vec)); + if (error != 0) + break; + + r.vec.iov_len = r32.vec.iov_len; + r.vec.iov_base = PTRIN(r32.vec.iov_base); + break; case PT_SET_EVENT_MASK: if (uap->data != sizeof(r.ptevents)) error = EINVAL; else error = copyin(uap->addr, &r.ptevents, uap->data); break; case PT_IO: error = copyin(uap->addr, &r32.piod, sizeof(r32.piod)); if (error) break; CP(r32.piod, r.piod, piod_op); PTRIN_CP(r32.piod, r.piod, piod_offs); PTRIN_CP(r32.piod, r.piod, piod_addr); CP(r32.piod, r.piod, piod_len); break; case PT_VM_ENTRY: error = copyin(uap->addr, &r32.pve, sizeof(r32.pve)); if (error) break; CP(r32.pve, r.pve, pve_entry); CP(r32.pve, r.pve, pve_timestamp); CP(r32.pve, r.pve, pve_start); CP(r32.pve, r.pve, pve_end); CP(r32.pve, r.pve, pve_offset); CP(r32.pve, r.pve, pve_prot); CP(r32.pve, r.pve, pve_pathlen); CP(r32.pve, r.pve, pve_fileid); CP(r32.pve, r.pve, pve_fsid); PTRIN_CP(r32.pve, r.pve, pve_path); break; case PT_COREDUMP: if (uap->data != sizeof(r32.pc)) error = EINVAL; else error = copyin(uap->addr, &r32.pc, uap->data); CP(r32.pc, r.pc, pc_fd); CP(r32.pc, r.pc, pc_flags); r.pc.pc_limit = PAIR32TO64(off_t, r32.pc.pc_limit); data = sizeof(r.pc); break; default: addr = uap->addr; break; } if (error) return (error); error = kern_ptrace(td, uap->req, uap->pid, addr, data); if (error) return (error); switch (uap->req) { case PT_VM_ENTRY: CP(r.pve, r32.pve, pve_entry); CP(r.pve, r32.pve, pve_timestamp); CP(r.pve, r32.pve, pve_start); CP(r.pve, r32.pve, pve_end); CP(r.pve, r32.pve, pve_offset); CP(r.pve, r32.pve, pve_prot); CP(r.pve, r32.pve, pve_pathlen); CP(r.pve, r32.pve, pve_fileid); CP(r.pve, r32.pve, pve_fsid); error = copyout(&r32.pve, uap->addr, sizeof(r32.pve)); break; case PT_IO: CP(r.piod, r32.piod, piod_len); error = copyout(&r32.piod, uap->addr, sizeof(r32.piod)); break; case PT_GETREGS: error = copyout(&r.reg, uap->addr, sizeof(r.reg)); break; case PT_GETFPREGS: error = copyout(&r.fpreg, uap->addr, sizeof(r.fpreg)); break; case PT_GETDBREGS: error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg)); break; + case PT_GETREGSET: + r32.vec.iov_len = r.vec.iov_len; + error = copyout(&r32.vec, uap->addr, sizeof(r32.vec)); + break; case PT_GET_EVENT_MASK: /* NB: The size in uap->data is validated in kern_ptrace(). */ error = copyout(&r.ptevents, uap->addr, uap->data); break; case PT_LWPINFO: ptrace_lwpinfo_to32(&r.pl, &r32.pl); error = copyout(&r32.pl, uap->addr, uap->data); break; case PT_GET_SC_ARGS: for (i = 0; i < nitems(r.args); i++) r32.args[i] = (uint32_t)r.args[i]; error = copyout(r32.args, uap->addr, MIN(uap->data, sizeof(r32.args))); break; case PT_GET_SC_RET: ptrace_sc_ret_to32(&r.psr, &r32.psr); error = copyout(&r32.psr, uap->addr, MIN(uap->data, sizeof(r32.psr))); break; } return (error); } int freebsd32_copyinuio(struct iovec32 *iovp, u_int iovcnt, struct uio **uiop) { struct iovec32 iov32; struct iovec *iov; struct uio *uio; u_int iovlen; int error, i; *uiop = NULL; if (iovcnt > UIO_MAXIOV) return (EINVAL); iovlen = iovcnt * sizeof(struct iovec); uio = malloc(iovlen + sizeof *uio, M_IOV, M_WAITOK); iov = (struct iovec *)(uio + 1); for (i = 0; i < iovcnt; i++) { error = copyin(&iovp[i], &iov32, sizeof(struct iovec32)); if (error) { free(uio, M_IOV); return (error); } iov[i].iov_base = PTRIN(iov32.iov_base); iov[i].iov_len = iov32.iov_len; } uio->uio_iov = iov; uio->uio_iovcnt = iovcnt; uio->uio_segflg = UIO_USERSPACE; uio->uio_offset = -1; uio->uio_resid = 0; for (i = 0; i < iovcnt; i++) { if (iov->iov_len > INT_MAX - uio->uio_resid) { free(uio, M_IOV); return (EINVAL); } uio->uio_resid += iov->iov_len; iov++; } *uiop = uio; return (0); } int freebsd32_readv(struct thread *td, struct freebsd32_readv_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_readv(td, uap->fd, auio); free(auio, M_IOV); return (error); } int freebsd32_writev(struct thread *td, struct freebsd32_writev_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_writev(td, uap->fd, auio); free(auio, M_IOV); return (error); } int freebsd32_preadv(struct thread *td, struct freebsd32_preadv_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_preadv(td, uap->fd, auio, PAIR32TO64(off_t,uap->offset)); free(auio, M_IOV); return (error); } int freebsd32_pwritev(struct thread *td, struct freebsd32_pwritev_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_pwritev(td, uap->fd, auio, PAIR32TO64(off_t,uap->offset)); free(auio, M_IOV); return (error); } int freebsd32_copyiniov(struct iovec32 *iovp32, u_int iovcnt, struct iovec **iovp, int error) { struct iovec32 iov32; struct iovec *iov; u_int iovlen; int i; *iovp = NULL; if (iovcnt > UIO_MAXIOV) return (error); iovlen = iovcnt * sizeof(struct iovec); iov = malloc(iovlen, M_IOV, M_WAITOK); for (i = 0; i < iovcnt; i++) { error = copyin(&iovp32[i], &iov32, sizeof(struct iovec32)); if (error) { free(iov, M_IOV); return (error); } iov[i].iov_base = PTRIN(iov32.iov_base); iov[i].iov_len = iov32.iov_len; } *iovp = iov; return (0); } static int freebsd32_copyinmsghdr(const struct msghdr32 *msg32, struct msghdr *msg) { struct msghdr32 m32; int error; error = copyin(msg32, &m32, sizeof(m32)); if (error) return (error); msg->msg_name = PTRIN(m32.msg_name); msg->msg_namelen = m32.msg_namelen; msg->msg_iov = PTRIN(m32.msg_iov); msg->msg_iovlen = m32.msg_iovlen; msg->msg_control = PTRIN(m32.msg_control); msg->msg_controllen = m32.msg_controllen; msg->msg_flags = m32.msg_flags; return (0); } static int freebsd32_copyoutmsghdr(struct msghdr *msg, struct msghdr32 *msg32) { struct msghdr32 m32; int error; m32.msg_name = PTROUT(msg->msg_name); m32.msg_namelen = msg->msg_namelen; m32.msg_iov = PTROUT(msg->msg_iov); m32.msg_iovlen = msg->msg_iovlen; m32.msg_control = PTROUT(msg->msg_control); m32.msg_controllen = msg->msg_controllen; m32.msg_flags = msg->msg_flags; error = copyout(&m32, msg32, sizeof(m32)); return (error); } #ifndef __mips__ #define FREEBSD32_ALIGNBYTES (sizeof(int) - 1) #else #define FREEBSD32_ALIGNBYTES (sizeof(long) - 1) #endif #define FREEBSD32_ALIGN(p) \ (((u_long)(p) + FREEBSD32_ALIGNBYTES) & ~FREEBSD32_ALIGNBYTES) #define FREEBSD32_CMSG_SPACE(l) \ (FREEBSD32_ALIGN(sizeof(struct cmsghdr)) + FREEBSD32_ALIGN(l)) #define FREEBSD32_CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + \ FREEBSD32_ALIGN(sizeof(struct cmsghdr))) static size_t freebsd32_cmsg_convert(const struct cmsghdr *cm, void *data, socklen_t datalen) { size_t copylen; union { struct timespec32 ts; struct timeval32 tv; struct bintime32 bt; } tmp32; union { struct timespec ts; struct timeval tv; struct bintime bt; } *in; in = data; copylen = 0; switch (cm->cmsg_level) { case SOL_SOCKET: switch (cm->cmsg_type) { case SCM_TIMESTAMP: TV_CP(*in, tmp32, tv); copylen = sizeof(tmp32.tv); break; case SCM_BINTIME: BT_CP(*in, tmp32, bt); copylen = sizeof(tmp32.bt); break; case SCM_REALTIME: case SCM_MONOTONIC: TS_CP(*in, tmp32, ts); copylen = sizeof(tmp32.ts); break; default: break; } default: break; } if (copylen == 0) return (datalen); KASSERT((datalen >= copylen), ("corrupted cmsghdr")); bcopy(&tmp32, data, copylen); return (copylen); } static int freebsd32_copy_msg_out(struct msghdr *msg, struct mbuf *control) { struct cmsghdr *cm; void *data; socklen_t clen, datalen, datalen_out, oldclen; int error; caddr_t ctlbuf; int len, copylen; struct mbuf *m; error = 0; len = msg->msg_controllen; msg->msg_controllen = 0; ctlbuf = msg->msg_control; for (m = control; m != NULL && len > 0; m = m->m_next) { cm = mtod(m, struct cmsghdr *); clen = m->m_len; while (cm != NULL) { if (sizeof(struct cmsghdr) > clen || cm->cmsg_len > clen) { error = EINVAL; break; } data = CMSG_DATA(cm); datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data; datalen_out = freebsd32_cmsg_convert(cm, data, datalen); /* * Copy out the message header. Preserve the native * message size in case we need to inspect the message * contents later. */ copylen = sizeof(struct cmsghdr); if (len < copylen) { msg->msg_flags |= MSG_CTRUNC; m_dispose_extcontrolm(m); goto exit; } oldclen = cm->cmsg_len; cm->cmsg_len = FREEBSD32_ALIGN(sizeof(struct cmsghdr)) + datalen_out; error = copyout(cm, ctlbuf, copylen); cm->cmsg_len = oldclen; if (error != 0) goto exit; ctlbuf += FREEBSD32_ALIGN(copylen); len -= FREEBSD32_ALIGN(copylen); copylen = datalen_out; if (len < copylen) { msg->msg_flags |= MSG_CTRUNC; m_dispose_extcontrolm(m); break; } /* Copy out the message data. */ error = copyout(data, ctlbuf, copylen); if (error) goto exit; ctlbuf += FREEBSD32_ALIGN(copylen); len -= FREEBSD32_ALIGN(copylen); if (CMSG_SPACE(datalen) < clen) { clen -= CMSG_SPACE(datalen); cm = (struct cmsghdr *) ((caddr_t)cm + CMSG_SPACE(datalen)); } else { clen = 0; cm = NULL; } msg->msg_controllen += FREEBSD32_CMSG_SPACE(datalen_out); } } if (len == 0 && m != NULL) { msg->msg_flags |= MSG_CTRUNC; m_dispose_extcontrolm(m); } exit: return (error); } int freebsd32_recvmsg(struct thread *td, struct freebsd32_recvmsg_args *uap) { struct msghdr msg; struct iovec *uiov, *iov; struct mbuf *control = NULL; struct mbuf **controlp; int error; error = freebsd32_copyinmsghdr(uap->msg, &msg); if (error) return (error); error = freebsd32_copyiniov((void *)msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_flags = uap->flags; uiov = msg.msg_iov; msg.msg_iov = iov; controlp = (msg.msg_control != NULL) ? &control : NULL; error = kern_recvit(td, uap->s, &msg, UIO_USERSPACE, controlp); if (error == 0) { msg.msg_iov = uiov; if (control != NULL) error = freebsd32_copy_msg_out(&msg, control); else msg.msg_controllen = 0; if (error == 0) error = freebsd32_copyoutmsghdr(&msg, uap->msg); } free(iov, M_IOV); if (control != NULL) { if (error != 0) m_dispose_extcontrolm(control); m_freem(control); } return (error); } #ifdef COMPAT_43 int ofreebsd32_recvmsg(struct thread *td, struct ofreebsd32_recvmsg_args *uap) { return (ENOSYS); } #endif /* * Copy-in the array of control messages constructed using alignment * and padding suitable for a 32-bit environment and construct an * mbuf using alignment and padding suitable for a 64-bit kernel. * The alignment and padding are defined indirectly by CMSG_DATA(), * CMSG_SPACE() and CMSG_LEN(). */ static int freebsd32_copyin_control(struct mbuf **mp, caddr_t buf, u_int buflen) { struct cmsghdr *cm; struct mbuf *m; void *in, *in1, *md; u_int msglen, outlen; int error; if (buflen > MCLBYTES) return (EINVAL); in = malloc(buflen, M_TEMP, M_WAITOK); error = copyin(buf, in, buflen); if (error != 0) goto out; /* * Make a pass over the input buffer to determine the amount of space * required for 64 bit-aligned copies of the control messages. */ in1 = in; outlen = 0; while (buflen > 0) { if (buflen < sizeof(*cm)) { error = EINVAL; break; } cm = (struct cmsghdr *)in1; if (cm->cmsg_len < FREEBSD32_ALIGN(sizeof(*cm))) { error = EINVAL; break; } msglen = FREEBSD32_ALIGN(cm->cmsg_len); if (msglen > buflen || msglen < cm->cmsg_len) { error = EINVAL; break; } buflen -= msglen; in1 = (char *)in1 + msglen; outlen += CMSG_ALIGN(sizeof(*cm)) + CMSG_ALIGN(msglen - FREEBSD32_ALIGN(sizeof(*cm))); } if (error == 0 && outlen > MCLBYTES) { /* * XXXMJ This implies that the upper limit on 32-bit aligned * control messages is less than MCLBYTES, and so we are not * perfectly compatible. However, there is no platform * guarantee that mbuf clusters larger than MCLBYTES can be * allocated. */ error = EINVAL; } if (error != 0) goto out; m = m_get2(outlen, M_WAITOK, MT_CONTROL, 0); m->m_len = outlen; md = mtod(m, void *); /* * Make a second pass over input messages, copying them into the output * buffer. */ in1 = in; while (outlen > 0) { /* Copy the message header and align the length field. */ cm = md; memcpy(cm, in1, sizeof(*cm)); msglen = cm->cmsg_len - FREEBSD32_ALIGN(sizeof(*cm)); cm->cmsg_len = CMSG_ALIGN(sizeof(*cm)) + msglen; /* Copy the message body. */ in1 = (char *)in1 + FREEBSD32_ALIGN(sizeof(*cm)); md = (char *)md + CMSG_ALIGN(sizeof(*cm)); memcpy(md, in1, msglen); in1 = (char *)in1 + FREEBSD32_ALIGN(msglen); md = (char *)md + CMSG_ALIGN(msglen); KASSERT(outlen >= CMSG_ALIGN(sizeof(*cm)) + CMSG_ALIGN(msglen), ("outlen %u underflow, msglen %u", outlen, msglen)); outlen -= CMSG_ALIGN(sizeof(*cm)) + CMSG_ALIGN(msglen); } *mp = m; out: free(in, M_TEMP); return (error); } int freebsd32_sendmsg(struct thread *td, struct freebsd32_sendmsg_args *uap) { struct msghdr msg; struct iovec *iov; struct mbuf *control = NULL; struct sockaddr *to = NULL; int error; error = freebsd32_copyinmsghdr(uap->msg, &msg); if (error) return (error); error = freebsd32_copyiniov((void *)msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_iov = iov; if (msg.msg_name != NULL) { error = getsockaddr(&to, msg.msg_name, msg.msg_namelen); if (error) { to = NULL; goto out; } msg.msg_name = to; } if (msg.msg_control) { if (msg.msg_controllen < sizeof(struct cmsghdr)) { error = EINVAL; goto out; } error = freebsd32_copyin_control(&control, msg.msg_control, msg.msg_controllen); if (error) goto out; msg.msg_control = NULL; msg.msg_controllen = 0; } error = kern_sendit(td, uap->s, &msg, uap->flags, control, UIO_USERSPACE); out: free(iov, M_IOV); if (to) free(to, M_SONAME); return (error); } #ifdef COMPAT_43 int ofreebsd32_sendmsg(struct thread *td, struct ofreebsd32_sendmsg_args *uap) { return (ENOSYS); } #endif int freebsd32_settimeofday(struct thread *td, struct freebsd32_settimeofday_args *uap) { struct timeval32 tv32; struct timeval tv, *tvp; struct timezone tz, *tzp; int error; if (uap->tv) { error = copyin(uap->tv, &tv32, sizeof(tv32)); if (error) return (error); CP(tv32, tv, tv_sec); CP(tv32, tv, tv_usec); tvp = &tv; } else tvp = NULL; if (uap->tzp) { error = copyin(uap->tzp, &tz, sizeof(tz)); if (error) return (error); tzp = &tz; } else tzp = NULL; return (kern_settimeofday(td, tvp, tzp)); } int freebsd32_utimes(struct thread *td, struct freebsd32_utimes_args *uap) { struct timeval32 s32[2]; struct timeval s[2], *sp; int error; if (uap->tptr != NULL) { error = copyin(uap->tptr, s32, sizeof(s32)); if (error) return (error); CP(s32[0], s[0], tv_sec); CP(s32[0], s[0], tv_usec); CP(s32[1], s[1], tv_sec); CP(s32[1], s[1], tv_usec); sp = s; } else sp = NULL; return (kern_utimesat(td, AT_FDCWD, uap->path, UIO_USERSPACE, sp, UIO_SYSSPACE)); } int freebsd32_lutimes(struct thread *td, struct freebsd32_lutimes_args *uap) { struct timeval32 s32[2]; struct timeval s[2], *sp; int error; if (uap->tptr != NULL) { error = copyin(uap->tptr, s32, sizeof(s32)); if (error) return (error); CP(s32[0], s[0], tv_sec); CP(s32[0], s[0], tv_usec); CP(s32[1], s[1], tv_sec); CP(s32[1], s[1], tv_usec); sp = s; } else sp = NULL; return (kern_lutimes(td, uap->path, UIO_USERSPACE, sp, UIO_SYSSPACE)); } int freebsd32_futimes(struct thread *td, struct freebsd32_futimes_args *uap) { struct timeval32 s32[2]; struct timeval s[2], *sp; int error; if (uap->tptr != NULL) { error = copyin(uap->tptr, s32, sizeof(s32)); if (error) return (error); CP(s32[0], s[0], tv_sec); CP(s32[0], s[0], tv_usec); CP(s32[1], s[1], tv_sec); CP(s32[1], s[1], tv_usec); sp = s; } else sp = NULL; return (kern_futimes(td, uap->fd, sp, UIO_SYSSPACE)); } int freebsd32_futimesat(struct thread *td, struct freebsd32_futimesat_args *uap) { struct timeval32 s32[2]; struct timeval s[2], *sp; int error; if (uap->times != NULL) { error = copyin(uap->times, s32, sizeof(s32)); if (error) return (error); CP(s32[0], s[0], tv_sec); CP(s32[0], s[0], tv_usec); CP(s32[1], s[1], tv_sec); CP(s32[1], s[1], tv_usec); sp = s; } else sp = NULL; return (kern_utimesat(td, uap->fd, uap->path, UIO_USERSPACE, sp, UIO_SYSSPACE)); } int freebsd32_futimens(struct thread *td, struct freebsd32_futimens_args *uap) { struct timespec32 ts32[2]; struct timespec ts[2], *tsp; int error; if (uap->times != NULL) { error = copyin(uap->times, ts32, sizeof(ts32)); if (error) return (error); CP(ts32[0], ts[0], tv_sec); CP(ts32[0], ts[0], tv_nsec); CP(ts32[1], ts[1], tv_sec); CP(ts32[1], ts[1], tv_nsec); tsp = ts; } else tsp = NULL; return (kern_futimens(td, uap->fd, tsp, UIO_SYSSPACE)); } int freebsd32_utimensat(struct thread *td, struct freebsd32_utimensat_args *uap) { struct timespec32 ts32[2]; struct timespec ts[2], *tsp; int error; if (uap->times != NULL) { error = copyin(uap->times, ts32, sizeof(ts32)); if (error) return (error); CP(ts32[0], ts[0], tv_sec); CP(ts32[0], ts[0], tv_nsec); CP(ts32[1], ts[1], tv_sec); CP(ts32[1], ts[1], tv_nsec); tsp = ts; } else tsp = NULL; return (kern_utimensat(td, uap->fd, uap->path, UIO_USERSPACE, tsp, UIO_SYSSPACE, uap->flag)); } int freebsd32_adjtime(struct thread *td, struct freebsd32_adjtime_args *uap) { struct timeval32 tv32; struct timeval delta, olddelta, *deltap; int error; if (uap->delta) { error = copyin(uap->delta, &tv32, sizeof(tv32)); if (error) return (error); CP(tv32, delta, tv_sec); CP(tv32, delta, tv_usec); deltap = δ } else deltap = NULL; error = kern_adjtime(td, deltap, &olddelta); if (uap->olddelta && error == 0) { CP(olddelta, tv32, tv_sec); CP(olddelta, tv32, tv_usec); error = copyout(&tv32, uap->olddelta, sizeof(tv32)); } return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_statfs(struct thread *td, struct freebsd4_freebsd32_statfs_args *uap) { struct ostatfs32 s32; struct statfs *sp; int error; sp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_statfs(td, uap->path, UIO_USERSPACE, sp); if (error == 0) { copy_statfs(sp, &s32); error = copyout(&s32, uap->buf, sizeof(s32)); } free(sp, M_STATFS); return (error); } #endif #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_fstatfs(struct thread *td, struct freebsd4_freebsd32_fstatfs_args *uap) { struct ostatfs32 s32; struct statfs *sp; int error; sp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fstatfs(td, uap->fd, sp); if (error == 0) { copy_statfs(sp, &s32); error = copyout(&s32, uap->buf, sizeof(s32)); } free(sp, M_STATFS); return (error); } #endif #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_fhstatfs(struct thread *td, struct freebsd4_freebsd32_fhstatfs_args *uap) { struct ostatfs32 s32; struct statfs *sp; fhandle_t fh; int error; if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0) return (error); sp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fhstatfs(td, fh, sp); if (error == 0) { copy_statfs(sp, &s32); error = copyout(&s32, uap->buf, sizeof(s32)); } free(sp, M_STATFS); return (error); } #endif int freebsd32_pread(struct thread *td, struct freebsd32_pread_args *uap) { return (kern_pread(td, uap->fd, uap->buf, uap->nbyte, PAIR32TO64(off_t, uap->offset))); } int freebsd32_pwrite(struct thread *td, struct freebsd32_pwrite_args *uap) { return (kern_pwrite(td, uap->fd, uap->buf, uap->nbyte, PAIR32TO64(off_t, uap->offset))); } #ifdef COMPAT_43 int ofreebsd32_lseek(struct thread *td, struct ofreebsd32_lseek_args *uap) { return (kern_lseek(td, uap->fd, uap->offset, uap->whence)); } #endif int freebsd32_lseek(struct thread *td, struct freebsd32_lseek_args *uap) { int error; off_t pos; error = kern_lseek(td, uap->fd, PAIR32TO64(off_t, uap->offset), uap->whence); /* Expand the quad return into two parts for eax and edx */ pos = td->td_uretoff.tdu_off; td->td_retval[RETVAL_LO] = pos & 0xffffffff; /* %eax */ td->td_retval[RETVAL_HI] = pos >> 32; /* %edx */ return error; } int freebsd32_truncate(struct thread *td, struct freebsd32_truncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, PAIR32TO64(off_t, uap->length))); } #ifdef COMPAT_43 int ofreebsd32_truncate(struct thread *td, struct ofreebsd32_truncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } #endif int freebsd32_ftruncate(struct thread *td, struct freebsd32_ftruncate_args *uap) { return (kern_ftruncate(td, uap->fd, PAIR32TO64(off_t, uap->length))); } #ifdef COMPAT_43 int ofreebsd32_ftruncate(struct thread *td, struct ofreebsd32_ftruncate_args *uap) { return (kern_ftruncate(td, uap->fd, uap->length)); } int ofreebsd32_getdirentries(struct thread *td, struct ofreebsd32_getdirentries_args *uap) { struct ogetdirentries_args ap; int error; long loff; int32_t loff_cut; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; error = kern_ogetdirentries(td, &ap, &loff); if (error == 0) { loff_cut = loff; error = copyout(&loff_cut, uap->basep, sizeof(int32_t)); } return (error); } #endif #if defined(COMPAT_FREEBSD11) int freebsd11_freebsd32_getdirentries(struct thread *td, struct freebsd11_freebsd32_getdirentries_args *uap) { long base; int32_t base32; int error; error = freebsd11_kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, NULL); if (error) return (error); if (uap->basep != NULL) { base32 = base; error = copyout(&base32, uap->basep, sizeof(int32_t)); } return (error); } #endif /* COMPAT_FREEBSD11 */ #ifdef COMPAT_FREEBSD6 /* versions with the 'int pad' argument */ int freebsd6_freebsd32_pread(struct thread *td, struct freebsd6_freebsd32_pread_args *uap) { return (kern_pread(td, uap->fd, uap->buf, uap->nbyte, PAIR32TO64(off_t, uap->offset))); } int freebsd6_freebsd32_pwrite(struct thread *td, struct freebsd6_freebsd32_pwrite_args *uap) { return (kern_pwrite(td, uap->fd, uap->buf, uap->nbyte, PAIR32TO64(off_t, uap->offset))); } int freebsd6_freebsd32_lseek(struct thread *td, struct freebsd6_freebsd32_lseek_args *uap) { int error; off_t pos; error = kern_lseek(td, uap->fd, PAIR32TO64(off_t, uap->offset), uap->whence); /* Expand the quad return into two parts for eax and edx */ pos = *(off_t *)(td->td_retval); td->td_retval[RETVAL_LO] = pos & 0xffffffff; /* %eax */ td->td_retval[RETVAL_HI] = pos >> 32; /* %edx */ return error; } int freebsd6_freebsd32_truncate(struct thread *td, struct freebsd6_freebsd32_truncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, PAIR32TO64(off_t, uap->length))); } int freebsd6_freebsd32_ftruncate(struct thread *td, struct freebsd6_freebsd32_ftruncate_args *uap) { return (kern_ftruncate(td, uap->fd, PAIR32TO64(off_t, uap->length))); } #endif /* COMPAT_FREEBSD6 */ struct sf_hdtr32 { uint32_t headers; int hdr_cnt; uint32_t trailers; int trl_cnt; }; static int freebsd32_do_sendfile(struct thread *td, struct freebsd32_sendfile_args *uap, int compat) { struct sf_hdtr32 hdtr32; struct sf_hdtr hdtr; struct uio *hdr_uio, *trl_uio; struct file *fp; cap_rights_t rights; struct iovec32 *iov32; off_t offset, sbytes; int error; offset = PAIR32TO64(off_t, uap->offset); if (offset < 0) return (EINVAL); hdr_uio = trl_uio = NULL; if (uap->hdtr != NULL) { error = copyin(uap->hdtr, &hdtr32, sizeof(hdtr32)); if (error) goto out; PTRIN_CP(hdtr32, hdtr, headers); CP(hdtr32, hdtr, hdr_cnt); PTRIN_CP(hdtr32, hdtr, trailers); CP(hdtr32, hdtr, trl_cnt); if (hdtr.headers != NULL) { iov32 = PTRIN(hdtr32.headers); error = freebsd32_copyinuio(iov32, hdtr32.hdr_cnt, &hdr_uio); if (error) goto out; #ifdef COMPAT_FREEBSD4 /* * In FreeBSD < 5.0 the nbytes to send also included * the header. If compat is specified subtract the * header size from nbytes. */ if (compat) { if (uap->nbytes > hdr_uio->uio_resid) uap->nbytes -= hdr_uio->uio_resid; else uap->nbytes = 0; } #endif } if (hdtr.trailers != NULL) { iov32 = PTRIN(hdtr32.trailers); error = freebsd32_copyinuio(iov32, hdtr32.trl_cnt, &trl_uio); if (error) goto out; } } AUDIT_ARG_FD(uap->fd); if ((error = fget_read(td, uap->fd, cap_rights_init_one(&rights, CAP_PREAD), &fp)) != 0) goto out; error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, offset, uap->nbytes, &sbytes, uap->flags, td); fdrop(fp, td); if (uap->sbytes != NULL) copyout(&sbytes, uap->sbytes, sizeof(off_t)); out: if (hdr_uio) free(hdr_uio, M_IOV); if (trl_uio) free(trl_uio, M_IOV); return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_sendfile(struct thread *td, struct freebsd4_freebsd32_sendfile_args *uap) { return (freebsd32_do_sendfile(td, (struct freebsd32_sendfile_args *)uap, 1)); } #endif int freebsd32_sendfile(struct thread *td, struct freebsd32_sendfile_args *uap) { return (freebsd32_do_sendfile(td, uap, 0)); } static void copy_stat(struct stat *in, struct stat32 *out) { CP(*in, *out, st_dev); CP(*in, *out, st_ino); CP(*in, *out, st_mode); CP(*in, *out, st_nlink); CP(*in, *out, st_uid); CP(*in, *out, st_gid); CP(*in, *out, st_rdev); TS_CP(*in, *out, st_atim); TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); CP(*in, *out, st_size); CP(*in, *out, st_blocks); CP(*in, *out, st_blksize); CP(*in, *out, st_flags); CP(*in, *out, st_gen); TS_CP(*in, *out, st_birthtim); out->st_padding0 = 0; out->st_padding1 = 0; #ifdef __STAT32_TIME_T_EXT out->st_atim_ext = 0; out->st_mtim_ext = 0; out->st_ctim_ext = 0; out->st_btim_ext = 0; #endif bzero(out->st_spare, sizeof(out->st_spare)); } #ifdef COMPAT_43 static void copy_ostat(struct stat *in, struct ostat32 *out) { bzero(out, sizeof(*out)); CP(*in, *out, st_dev); CP(*in, *out, st_ino); CP(*in, *out, st_mode); CP(*in, *out, st_nlink); CP(*in, *out, st_uid); CP(*in, *out, st_gid); CP(*in, *out, st_rdev); out->st_size = MIN(in->st_size, INT32_MAX); TS_CP(*in, *out, st_atim); TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); CP(*in, *out, st_blksize); CP(*in, *out, st_blocks); CP(*in, *out, st_flags); CP(*in, *out, st_gen); } #endif #ifdef COMPAT_43 int ofreebsd32_stat(struct thread *td, struct ofreebsd32_stat_args *uap) { struct stat sb; struct ostat32 sb32; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error) return (error); copy_ostat(&sb, &sb32); error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } #endif int freebsd32_fstat(struct thread *td, struct freebsd32_fstat_args *uap) { struct stat ub; struct stat32 ub32; int error; error = kern_fstat(td, uap->fd, &ub); if (error) return (error); copy_stat(&ub, &ub32); error = copyout(&ub32, uap->sb, sizeof(ub32)); return (error); } #ifdef COMPAT_43 int ofreebsd32_fstat(struct thread *td, struct ofreebsd32_fstat_args *uap) { struct stat ub; struct ostat32 ub32; int error; error = kern_fstat(td, uap->fd, &ub); if (error) return (error); copy_ostat(&ub, &ub32); error = copyout(&ub32, uap->sb, sizeof(ub32)); return (error); } #endif int freebsd32_fstatat(struct thread *td, struct freebsd32_fstatat_args *uap) { struct stat ub; struct stat32 ub32; int error; error = kern_statat(td, uap->flag, uap->fd, uap->path, UIO_USERSPACE, &ub, NULL); if (error) return (error); copy_stat(&ub, &ub32); error = copyout(&ub32, uap->buf, sizeof(ub32)); return (error); } #ifdef COMPAT_43 int ofreebsd32_lstat(struct thread *td, struct ofreebsd32_lstat_args *uap) { struct stat sb; struct ostat32 sb32; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error) return (error); copy_ostat(&sb, &sb32); error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } #endif int freebsd32_fhstat(struct thread *td, struct freebsd32_fhstat_args *uap) { struct stat sb; struct stat32 sb32; struct fhandle fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error != 0) return (error); error = kern_fhstat(td, fh, &sb); if (error != 0) return (error); copy_stat(&sb, &sb32); error = copyout(&sb32, uap->sb, sizeof (sb32)); return (error); } #if defined(COMPAT_FREEBSD11) extern int ino64_trunc_error; static int freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out) { CP(*in, *out, st_ino); if (in->st_ino != out->st_ino) { switch (ino64_trunc_error) { default: case 0: break; case 1: return (EOVERFLOW); case 2: out->st_ino = UINT32_MAX; break; } } CP(*in, *out, st_nlink); if (in->st_nlink != out->st_nlink) { switch (ino64_trunc_error) { default: case 0: break; case 1: return (EOVERFLOW); case 2: out->st_nlink = UINT16_MAX; break; } } out->st_dev = in->st_dev; if (out->st_dev != in->st_dev) { switch (ino64_trunc_error) { default: break; case 1: return (EOVERFLOW); } } CP(*in, *out, st_mode); CP(*in, *out, st_uid); CP(*in, *out, st_gid); out->st_rdev = in->st_rdev; if (out->st_rdev != in->st_rdev) { switch (ino64_trunc_error) { default: break; case 1: return (EOVERFLOW); } } TS_CP(*in, *out, st_atim); TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); CP(*in, *out, st_size); CP(*in, *out, st_blocks); CP(*in, *out, st_blksize); CP(*in, *out, st_flags); CP(*in, *out, st_gen); TS_CP(*in, *out, st_birthtim); out->st_lspare = 0; bzero((char *)&out->st_birthtim + sizeof(out->st_birthtim), sizeof(*out) - offsetof(struct freebsd11_stat32, st_birthtim) - sizeof(out->st_birthtim)); return (0); } int freebsd11_freebsd32_stat(struct thread *td, struct freebsd11_freebsd32_stat_args *uap) { struct stat sb; struct freebsd11_stat32 sb32; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat32(&sb, &sb32); if (error == 0) error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } int freebsd11_freebsd32_fstat(struct thread *td, struct freebsd11_freebsd32_fstat_args *uap) { struct stat sb; struct freebsd11_stat32 sb32; int error; error = kern_fstat(td, uap->fd, &sb); if (error != 0) return (error); error = freebsd11_cvtstat32(&sb, &sb32); if (error == 0) error = copyout(&sb32, uap->sb, sizeof (sb32)); return (error); } int freebsd11_freebsd32_fstatat(struct thread *td, struct freebsd11_freebsd32_fstatat_args *uap) { struct stat sb; struct freebsd11_stat32 sb32; int error; error = kern_statat(td, uap->flag, uap->fd, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat32(&sb, &sb32); if (error == 0) error = copyout(&sb32, uap->buf, sizeof (sb32)); return (error); } int freebsd11_freebsd32_lstat(struct thread *td, struct freebsd11_freebsd32_lstat_args *uap) { struct stat sb; struct freebsd11_stat32 sb32; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat32(&sb, &sb32); if (error == 0) error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } int freebsd11_freebsd32_fhstat(struct thread *td, struct freebsd11_freebsd32_fhstat_args *uap) { struct stat sb; struct freebsd11_stat32 sb32; struct fhandle fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error != 0) return (error); error = kern_fhstat(td, fh, &sb); if (error != 0) return (error); error = freebsd11_cvtstat32(&sb, &sb32); if (error == 0) error = copyout(&sb32, uap->sb, sizeof (sb32)); return (error); } static int freebsd11_cvtnstat32(struct stat *sb, struct nstat32 *nsb32) { struct nstat nsb; int error; error = freebsd11_cvtnstat(sb, &nsb); if (error != 0) return (error); bzero(nsb32, sizeof(*nsb32)); CP(nsb, *nsb32, st_dev); CP(nsb, *nsb32, st_ino); CP(nsb, *nsb32, st_mode); CP(nsb, *nsb32, st_nlink); CP(nsb, *nsb32, st_uid); CP(nsb, *nsb32, st_gid); CP(nsb, *nsb32, st_rdev); CP(nsb, *nsb32, st_atim.tv_sec); CP(nsb, *nsb32, st_atim.tv_nsec); CP(nsb, *nsb32, st_mtim.tv_sec); CP(nsb, *nsb32, st_mtim.tv_nsec); CP(nsb, *nsb32, st_ctim.tv_sec); CP(nsb, *nsb32, st_ctim.tv_nsec); CP(nsb, *nsb32, st_size); CP(nsb, *nsb32, st_blocks); CP(nsb, *nsb32, st_blksize); CP(nsb, *nsb32, st_flags); CP(nsb, *nsb32, st_gen); CP(nsb, *nsb32, st_birthtim.tv_sec); CP(nsb, *nsb32, st_birthtim.tv_nsec); return (0); } int freebsd11_freebsd32_nstat(struct thread *td, struct freebsd11_freebsd32_nstat_args *uap) { struct stat sb; struct nstat32 nsb; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtnstat32(&sb, &nsb); if (error != 0) error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } int freebsd11_freebsd32_nlstat(struct thread *td, struct freebsd11_freebsd32_nlstat_args *uap) { struct stat sb; struct nstat32 nsb; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtnstat32(&sb, &nsb); if (error == 0) error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } int freebsd11_freebsd32_nfstat(struct thread *td, struct freebsd11_freebsd32_nfstat_args *uap) { struct nstat32 nub; struct stat ub; int error; error = kern_fstat(td, uap->fd, &ub); if (error != 0) return (error); error = freebsd11_cvtnstat32(&ub, &nub); if (error == 0) error = copyout(&nub, uap->sb, sizeof(nub)); return (error); } #endif int freebsd32___sysctl(struct thread *td, struct freebsd32___sysctl_args *uap) { int error, name[CTL_MAXNAME]; size_t j, oldlen; uint32_t tmp; if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); error = copyin(uap->name, name, uap->namelen * sizeof(int)); if (error) return (error); if (uap->oldlenp) { error = fueword32(uap->oldlenp, &tmp); oldlen = tmp; } else { oldlen = 0; } if (error != 0) return (EFAULT); error = userland_sysctl(td, name, uap->namelen, uap->old, &oldlen, 1, uap->new, uap->newlen, &j, SCTL_MASK32); if (error) return (error); if (uap->oldlenp) suword32(uap->oldlenp, j); return (0); } int freebsd32___sysctlbyname(struct thread *td, struct freebsd32___sysctlbyname_args *uap) { size_t oldlen, rv; int error; uint32_t tmp; if (uap->oldlenp != NULL) { error = fueword32(uap->oldlenp, &tmp); oldlen = tmp; } else { error = oldlen = 0; } if (error != 0) return (EFAULT); error = kern___sysctlbyname(td, uap->name, uap->namelen, uap->old, &oldlen, uap->new, uap->newlen, &rv, SCTL_MASK32, 1); if (error != 0) return (error); if (uap->oldlenp != NULL) error = suword32(uap->oldlenp, rv); return (error); } int freebsd32_jail(struct thread *td, struct freebsd32_jail_args *uap) { uint32_t version; int error; struct jail j; error = copyin(uap->jail, &version, sizeof(uint32_t)); if (error) return (error); switch (version) { case 0: { /* FreeBSD single IPv4 jails. */ struct jail32_v0 j32_v0; bzero(&j, sizeof(struct jail)); error = copyin(uap->jail, &j32_v0, sizeof(struct jail32_v0)); if (error) return (error); CP(j32_v0, j, version); PTRIN_CP(j32_v0, j, path); PTRIN_CP(j32_v0, j, hostname); j.ip4s = htonl(j32_v0.ip_number); /* jail_v0 is host order */ break; } case 1: /* * Version 1 was used by multi-IPv4 jail implementations * that never made it into the official kernel. */ return (EINVAL); case 2: /* JAIL_API_VERSION */ { /* FreeBSD multi-IPv4/IPv6,noIP jails. */ struct jail32 j32; error = copyin(uap->jail, &j32, sizeof(struct jail32)); if (error) return (error); CP(j32, j, version); PTRIN_CP(j32, j, path); PTRIN_CP(j32, j, hostname); PTRIN_CP(j32, j, jailname); CP(j32, j, ip4s); CP(j32, j, ip6s); PTRIN_CP(j32, j, ip4); PTRIN_CP(j32, j, ip6); break; } default: /* Sci-Fi jails are not supported, sorry. */ return (EINVAL); } return (kern_jail(td, &j)); } int freebsd32_jail_set(struct thread *td, struct freebsd32_jail_set_args *uap) { struct uio *auio; int error; /* Check that we have an even number of iovecs. */ if (uap->iovcnt & 1) return (EINVAL); error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_jail_set(td, auio, uap->flags); free(auio, M_IOV); return (error); } int freebsd32_jail_get(struct thread *td, struct freebsd32_jail_get_args *uap) { struct iovec32 iov32; struct uio *auio; int error, i; /* Check that we have an even number of iovecs. */ if (uap->iovcnt & 1) return (EINVAL); error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_jail_get(td, auio, uap->flags); if (error == 0) for (i = 0; i < uap->iovcnt; i++) { PTROUT_CP(auio->uio_iov[i], iov32, iov_base); CP(auio->uio_iov[i], iov32, iov_len); error = copyout(&iov32, uap->iovp + i, sizeof(iov32)); if (error != 0) break; } free(auio, M_IOV); return (error); } int freebsd32_sigaction(struct thread *td, struct freebsd32_sigaction_args *uap) { struct sigaction32 s32; struct sigaction sa, osa, *sap; int error; if (uap->act) { error = copyin(uap->act, &s32, sizeof(s32)); if (error) return (error); sa.sa_handler = PTRIN(s32.sa_u); CP(s32, sa, sa_flags); CP(s32, sa, sa_mask); sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->sig, sap, &osa, 0); if (error == 0 && uap->oact != NULL) { s32.sa_u = PTROUT(osa.sa_handler); CP(osa, s32, sa_flags); CP(osa, s32, sa_mask); error = copyout(&s32, uap->oact, sizeof(s32)); } return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_sigaction(struct thread *td, struct freebsd4_freebsd32_sigaction_args *uap) { struct sigaction32 s32; struct sigaction sa, osa, *sap; int error; if (uap->act) { error = copyin(uap->act, &s32, sizeof(s32)); if (error) return (error); sa.sa_handler = PTRIN(s32.sa_u); CP(s32, sa, sa_flags); CP(s32, sa, sa_mask); sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->sig, sap, &osa, KSA_FREEBSD4); if (error == 0 && uap->oact != NULL) { s32.sa_u = PTROUT(osa.sa_handler); CP(osa, s32, sa_flags); CP(osa, s32, sa_mask); error = copyout(&s32, uap->oact, sizeof(s32)); } return (error); } #endif #ifdef COMPAT_43 struct osigaction32 { uint32_t sa_u; osigset_t sa_mask; int sa_flags; }; #define ONSIG 32 int ofreebsd32_sigaction(struct thread *td, struct ofreebsd32_sigaction_args *uap) { struct osigaction32 s32; struct sigaction sa, osa, *sap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); if (uap->nsa) { error = copyin(uap->nsa, &s32, sizeof(s32)); if (error) return (error); sa.sa_handler = PTRIN(s32.sa_u); CP(s32, sa, sa_flags); OSIG2SIG(s32.sa_mask, sa.sa_mask); sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->signum, sap, &osa, KSA_OSIGSET); if (error == 0 && uap->osa != NULL) { s32.sa_u = PTROUT(osa.sa_handler); CP(osa, s32, sa_flags); SIG2OSIG(osa.sa_mask, s32.sa_mask); error = copyout(&s32, uap->osa, sizeof(s32)); } return (error); } struct sigvec32 { uint32_t sv_handler; int sv_mask; int sv_flags; }; int ofreebsd32_sigvec(struct thread *td, struct ofreebsd32_sigvec_args *uap) { struct sigvec32 vec; struct sigaction sa, osa, *sap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); if (uap->nsv) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); sa.sa_handler = PTRIN(vec.sv_handler); OSIG2SIG(vec.sv_mask, sa.sa_mask); sa.sa_flags = vec.sv_flags; sa.sa_flags ^= SA_RESTART; sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->signum, sap, &osa, KSA_OSIGSET); if (error == 0 && uap->osv != NULL) { vec.sv_handler = PTROUT(osa.sa_handler); SIG2OSIG(osa.sa_mask, vec.sv_mask); vec.sv_flags = osa.sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } struct sigstack32 { uint32_t ss_sp; int ss_onstack; }; int ofreebsd32_sigstack(struct thread *td, struct ofreebsd32_sigstack_args *uap) { struct sigstack32 s32; struct sigstack nss, oss; int error = 0, unss; if (uap->nss != NULL) { error = copyin(uap->nss, &s32, sizeof(s32)); if (error) return (error); nss.ss_sp = PTRIN(s32.ss_sp); CP(s32, nss, ss_onstack); unss = 1; } else { unss = 0; } oss.ss_sp = td->td_sigstk.ss_sp; oss.ss_onstack = sigonstack(cpu_getstack(td)); if (unss) { td->td_sigstk.ss_sp = nss.ss_sp; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_flags |= (nss.ss_onstack & SS_ONSTACK); td->td_pflags |= TDP_ALTSTACK; } if (uap->oss != NULL) { s32.ss_sp = PTROUT(oss.ss_sp); CP(oss, s32, ss_onstack); error = copyout(&s32, uap->oss, sizeof(s32)); } return (error); } #endif int freebsd32_nanosleep(struct thread *td, struct freebsd32_nanosleep_args *uap) { return (freebsd32_user_clock_nanosleep(td, CLOCK_REALTIME, TIMER_RELTIME, uap->rqtp, uap->rmtp)); } int freebsd32_clock_nanosleep(struct thread *td, struct freebsd32_clock_nanosleep_args *uap) { int error; error = freebsd32_user_clock_nanosleep(td, uap->clock_id, uap->flags, uap->rqtp, uap->rmtp); return (kern_posix_error(td, error)); } static int freebsd32_user_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, const struct timespec32 *ua_rqtp, struct timespec32 *ua_rmtp) { struct timespec32 rmt32, rqt32; struct timespec rmt, rqt; int error, error2; error = copyin(ua_rqtp, &rqt32, sizeof(rqt32)); if (error) return (error); CP(rqt32, rqt, tv_sec); CP(rqt32, rqt, tv_nsec); error = kern_clock_nanosleep(td, clock_id, flags, &rqt, &rmt); if (error == EINTR && ua_rmtp != NULL && (flags & TIMER_ABSTIME) == 0) { CP(rmt, rmt32, tv_sec); CP(rmt, rmt32, tv_nsec); error2 = copyout(&rmt32, ua_rmtp, sizeof(rmt32)); if (error2 != 0) error = error2; } return (error); } int freebsd32_clock_gettime(struct thread *td, struct freebsd32_clock_gettime_args *uap) { struct timespec ats; struct timespec32 ats32; int error; error = kern_clock_gettime(td, uap->clock_id, &ats); if (error == 0) { CP(ats, ats32, tv_sec); CP(ats, ats32, tv_nsec); error = copyout(&ats32, uap->tp, sizeof(ats32)); } return (error); } int freebsd32_clock_settime(struct thread *td, struct freebsd32_clock_settime_args *uap) { struct timespec ats; struct timespec32 ats32; int error; error = copyin(uap->tp, &ats32, sizeof(ats32)); if (error) return (error); CP(ats32, ats, tv_sec); CP(ats32, ats, tv_nsec); return (kern_clock_settime(td, uap->clock_id, &ats)); } int freebsd32_clock_getres(struct thread *td, struct freebsd32_clock_getres_args *uap) { struct timespec ts; struct timespec32 ts32; int error; if (uap->tp == NULL) return (0); error = kern_clock_getres(td, uap->clock_id, &ts); if (error == 0) { CP(ts, ts32, tv_sec); CP(ts, ts32, tv_nsec); error = copyout(&ts32, uap->tp, sizeof(ts32)); } return (error); } int freebsd32_ktimer_create(struct thread *td, struct freebsd32_ktimer_create_args *uap) { struct sigevent32 ev32; struct sigevent ev, *evp; int error, id; if (uap->evp == NULL) { evp = NULL; } else { evp = &ev; error = copyin(uap->evp, &ev32, sizeof(ev32)); if (error != 0) return (error); error = convert_sigevent32(&ev32, &ev); if (error != 0) return (error); } error = kern_ktimer_create(td, uap->clock_id, evp, &id, -1); if (error == 0) { error = copyout(&id, uap->timerid, sizeof(int)); if (error != 0) kern_ktimer_delete(td, id); } return (error); } int freebsd32_ktimer_settime(struct thread *td, struct freebsd32_ktimer_settime_args *uap) { struct itimerspec32 val32, oval32; struct itimerspec val, oval, *ovalp; int error; error = copyin(uap->value, &val32, sizeof(val32)); if (error != 0) return (error); ITS_CP(val32, val); ovalp = uap->ovalue != NULL ? &oval : NULL; error = kern_ktimer_settime(td, uap->timerid, uap->flags, &val, ovalp); if (error == 0 && uap->ovalue != NULL) { ITS_CP(oval, oval32); error = copyout(&oval32, uap->ovalue, sizeof(oval32)); } return (error); } int freebsd32_ktimer_gettime(struct thread *td, struct freebsd32_ktimer_gettime_args *uap) { struct itimerspec32 val32; struct itimerspec val; int error; error = kern_ktimer_gettime(td, uap->timerid, &val); if (error == 0) { ITS_CP(val, val32); error = copyout(&val32, uap->value, sizeof(val32)); } return (error); } int freebsd32_clock_getcpuclockid2(struct thread *td, struct freebsd32_clock_getcpuclockid2_args *uap) { clockid_t clk_id; int error; error = kern_clock_getcpuclockid2(td, PAIR32TO64(id_t, uap->id), uap->which, &clk_id); if (error == 0) error = copyout(&clk_id, uap->clock_id, sizeof(clockid_t)); return (error); } int freebsd32_thr_new(struct thread *td, struct freebsd32_thr_new_args *uap) { struct thr_param32 param32; struct thr_param param; int error; if (uap->param_size < 0 || uap->param_size > sizeof(struct thr_param32)) return (EINVAL); bzero(¶m, sizeof(struct thr_param)); bzero(¶m32, sizeof(struct thr_param32)); error = copyin(uap->param, ¶m32, uap->param_size); if (error != 0) return (error); param.start_func = PTRIN(param32.start_func); param.arg = PTRIN(param32.arg); param.stack_base = PTRIN(param32.stack_base); param.stack_size = param32.stack_size; param.tls_base = PTRIN(param32.tls_base); param.tls_size = param32.tls_size; param.child_tid = PTRIN(param32.child_tid); param.parent_tid = PTRIN(param32.parent_tid); param.flags = param32.flags; param.rtp = PTRIN(param32.rtp); param.spare[0] = PTRIN(param32.spare[0]); param.spare[1] = PTRIN(param32.spare[1]); param.spare[2] = PTRIN(param32.spare[2]); return (kern_thr_new(td, ¶m)); } int freebsd32_thr_suspend(struct thread *td, struct freebsd32_thr_suspend_args *uap) { struct timespec32 ts32; struct timespec ts, *tsp; int error; error = 0; tsp = NULL; if (uap->timeout != NULL) { error = copyin((const void *)uap->timeout, (void *)&ts32, sizeof(struct timespec32)); if (error != 0) return (error); ts.tv_sec = ts32.tv_sec; ts.tv_nsec = ts32.tv_nsec; tsp = &ts; } return (kern_thr_suspend(td, tsp)); } void siginfo_to_siginfo32(const siginfo_t *src, struct siginfo32 *dst) { bzero(dst, sizeof(*dst)); dst->si_signo = src->si_signo; dst->si_errno = src->si_errno; dst->si_code = src->si_code; dst->si_pid = src->si_pid; dst->si_uid = src->si_uid; dst->si_status = src->si_status; dst->si_addr = (uintptr_t)src->si_addr; dst->si_value.sival_int = src->si_value.sival_int; dst->si_timerid = src->si_timerid; dst->si_overrun = src->si_overrun; } #ifndef _FREEBSD32_SYSPROTO_H_ struct freebsd32_sigqueue_args { pid_t pid; int signum; /* union sigval32 */ int value; }; #endif int freebsd32_sigqueue(struct thread *td, struct freebsd32_sigqueue_args *uap) { union sigval sv; /* * On 32-bit ABIs, sival_int and sival_ptr are the same. * On 64-bit little-endian ABIs, the low bits are the same. * In 64-bit big-endian ABIs, sival_int overlaps with * sival_ptr's HIGH bits. We choose to support sival_int * rather than sival_ptr in this case as it seems to be * more common. */ bzero(&sv, sizeof(sv)); sv.sival_int = (uint32_t)(uint64_t)uap->value; return (kern_sigqueue(td, uap->pid, uap->signum, &sv)); } int freebsd32_sigtimedwait(struct thread *td, struct freebsd32_sigtimedwait_args *uap) { struct timespec32 ts32; struct timespec ts; struct timespec *timeout; sigset_t set; ksiginfo_t ksi; struct siginfo32 si32; int error; if (uap->timeout) { error = copyin(uap->timeout, &ts32, sizeof(ts32)); if (error) return (error); ts.tv_sec = ts32.tv_sec; ts.tv_nsec = ts32.tv_nsec; timeout = &ts; } else timeout = NULL; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, timeout); if (error) return (error); if (uap->info) { siginfo_to_siginfo32(&ksi.ksi_info, &si32); error = copyout(&si32, uap->info, sizeof(struct siginfo32)); } if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } /* * MPSAFE */ int freebsd32_sigwaitinfo(struct thread *td, struct freebsd32_sigwaitinfo_args *uap) { ksiginfo_t ksi; struct siginfo32 si32; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, NULL); if (error) return (error); if (uap->info) { siginfo_to_siginfo32(&ksi.ksi_info, &si32); error = copyout(&si32, uap->info, sizeof(struct siginfo32)); } if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } int freebsd32_cpuset_setid(struct thread *td, struct freebsd32_cpuset_setid_args *uap) { return (kern_cpuset_setid(td, uap->which, PAIR32TO64(id_t, uap->id), uap->setid)); } int freebsd32_cpuset_getid(struct thread *td, struct freebsd32_cpuset_getid_args *uap) { return (kern_cpuset_getid(td, uap->level, uap->which, PAIR32TO64(id_t, uap->id), uap->setid)); } int freebsd32_cpuset_getaffinity(struct thread *td, struct freebsd32_cpuset_getaffinity_args *uap) { return (kern_cpuset_getaffinity(td, uap->level, uap->which, PAIR32TO64(id_t,uap->id), uap->cpusetsize, uap->mask)); } int freebsd32_cpuset_setaffinity(struct thread *td, struct freebsd32_cpuset_setaffinity_args *uap) { return (kern_cpuset_setaffinity(td, uap->level, uap->which, PAIR32TO64(id_t,uap->id), uap->cpusetsize, uap->mask)); } int freebsd32_cpuset_getdomain(struct thread *td, struct freebsd32_cpuset_getdomain_args *uap) { return (kern_cpuset_getdomain(td, uap->level, uap->which, PAIR32TO64(id_t,uap->id), uap->domainsetsize, uap->mask, uap->policy)); } int freebsd32_cpuset_setdomain(struct thread *td, struct freebsd32_cpuset_setdomain_args *uap) { return (kern_cpuset_setdomain(td, uap->level, uap->which, PAIR32TO64(id_t,uap->id), uap->domainsetsize, uap->mask, uap->policy)); } int freebsd32_nmount(struct thread *td, struct freebsd32_nmount_args /* { struct iovec *iovp; unsigned int iovcnt; int flags; } */ *uap) { struct uio *auio; uint64_t flags; int error; /* * Mount flags are now 64-bits. On 32-bit archtectures only * 32-bits are passed in, but from here on everything handles * 64-bit flags correctly. */ flags = uap->flags; AUDIT_ARG_FFLAGS(flags); /* * Filter out MNT_ROOTFS. We do not want clients of nmount() in * userspace to set this flag, but we must filter it out if we want * MNT_UPDATE on the root file system to work. * MNT_ROOTFS should only be set by the kernel when mounting its * root file system. */ flags &= ~MNT_ROOTFS; /* * check that we have an even number of iovec's * and that we have at least two options. */ if ((uap->iovcnt & 1) || (uap->iovcnt < 4)) return (EINVAL); error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = vfs_donmount(td, flags, auio); free(auio, M_IOV); return error; } #if 0 int freebsd32_xxx(struct thread *td, struct freebsd32_xxx_args *uap) { struct yyy32 *p32, s32; struct yyy *p = NULL, s; struct xxx_arg ap; int error; if (uap->zzz) { error = copyin(uap->zzz, &s32, sizeof(s32)); if (error) return (error); /* translate in */ p = &s; } error = kern_xxx(td, p); if (error) return (error); if (uap->zzz) { /* translate out */ error = copyout(&s32, p32, sizeof(s32)); } return (error); } #endif int syscall32_module_handler(struct module *mod, int what, void *arg) { return (kern_syscall_module_handler(freebsd32_sysent, mod, what, arg)); } int syscall32_helper_register(struct syscall_helper_data *sd, int flags) { return (kern_syscall_helper_register(freebsd32_sysent, sd, flags)); } int syscall32_helper_unregister(struct syscall_helper_data *sd) { return (kern_syscall_helper_unregister(freebsd32_sysent, sd)); } int freebsd32_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) { struct sysentvec *sysent; int argc, envc, i; uint32_t *vectp; char *stringp; uintptr_t destp, ustringp; struct freebsd32_ps_strings *arginfo; char canary[sizeof(long) * 8]; int32_t pagesizes32[MAXPAGESIZES]; size_t execpath_len; int error, szsigcode; sysent = imgp->sysent; arginfo = (struct freebsd32_ps_strings *)PROC_PS_STRINGS(imgp->proc); imgp->ps_strings = arginfo; destp = (uintptr_t)arginfo; /* * Install sigcode. */ if (sysent->sv_sigcode_base == 0) { szsigcode = *sysent->sv_szsigcode; destp -= szsigcode; destp = rounddown2(destp, sizeof(uint32_t)); error = copyout(sysent->sv_sigcode, (void *)destp, szsigcode); if (error != 0) return (error); } /* * Copy the image path for the rtld. */ if (imgp->execpath != NULL && imgp->auxargs != NULL) { execpath_len = strlen(imgp->execpath) + 1; destp -= execpath_len; imgp->execpathp = (void *)destp; error = copyout(imgp->execpath, imgp->execpathp, execpath_len); if (error != 0) return (error); } /* * Prepare the canary for SSP. */ arc4rand(canary, sizeof(canary), 0); destp -= sizeof(canary); imgp->canary = (void *)destp; error = copyout(canary, imgp->canary, sizeof(canary)); if (error != 0) return (error); imgp->canarylen = sizeof(canary); /* * Prepare the pagesizes array. */ for (i = 0; i < MAXPAGESIZES; i++) pagesizes32[i] = (uint32_t)pagesizes[i]; destp -= sizeof(pagesizes32); destp = rounddown2(destp, sizeof(uint32_t)); imgp->pagesizes = (void *)destp; error = copyout(pagesizes32, imgp->pagesizes, sizeof(pagesizes32)); if (error != 0) return (error); imgp->pagesizeslen = sizeof(pagesizes32); /* * Allocate room for the argument and environment strings. */ destp -= ARG_MAX - imgp->args->stringspace; destp = rounddown2(destp, sizeof(uint32_t)); ustringp = destp; if (imgp->auxargs) { /* * Allocate room on the stack for the ELF auxargs * array. It has up to AT_COUNT entries. */ destp -= AT_COUNT * sizeof(Elf32_Auxinfo); destp = rounddown2(destp, sizeof(uint32_t)); } vectp = (uint32_t *)destp; /* * Allocate room for the argv[] and env vectors including the * terminating NULL pointers. */ vectp -= imgp->args->argc + 1 + imgp->args->envc + 1; /* * vectp also becomes our initial stack base */ *stack_base = (uintptr_t)vectp; stringp = imgp->args->begin_argv; argc = imgp->args->argc; envc = imgp->args->envc; /* * Copy out strings - arguments and environment. */ error = copyout(stringp, (void *)ustringp, ARG_MAX - imgp->args->stringspace); if (error != 0) return (error); /* * Fill in "ps_strings" struct for ps, w, etc. */ imgp->argv = vectp; if (suword32(&arginfo->ps_argvstr, (uint32_t)(intptr_t)vectp) != 0 || suword32(&arginfo->ps_nargvstr, argc) != 0) return (EFAULT); /* * Fill in argument portion of vector table. */ for (; argc > 0; --argc) { if (suword32(vectp++, ustringp) != 0) return (EFAULT); while (*stringp++ != 0) ustringp++; ustringp++; } /* a null vector table pointer separates the argp's from the envp's */ if (suword32(vectp++, 0) != 0) return (EFAULT); imgp->envv = vectp; if (suword32(&arginfo->ps_envstr, (uint32_t)(intptr_t)vectp) != 0 || suword32(&arginfo->ps_nenvstr, envc) != 0) return (EFAULT); /* * Fill in environment portion of vector table. */ for (; envc > 0; --envc) { if (suword32(vectp++, ustringp) != 0) return (EFAULT); while (*stringp++ != 0) ustringp++; ustringp++; } /* end of vector table is a null pointer */ if (suword32(vectp, 0) != 0) return (EFAULT); if (imgp->auxargs) { vectp++; error = imgp->sysent->sv_copyout_auxargs(imgp, (uintptr_t)vectp); if (error != 0) return (error); } return (0); } int freebsd32_kldstat(struct thread *td, struct freebsd32_kldstat_args *uap) { struct kld_file_stat *stat; struct kld_file_stat32 *stat32; int error, version; if ((error = copyin(&uap->stat->version, &version, sizeof(version))) != 0) return (error); if (version != sizeof(struct kld_file_stat_1_32) && version != sizeof(struct kld_file_stat32)) return (EINVAL); stat = malloc(sizeof(*stat), M_TEMP, M_WAITOK | M_ZERO); stat32 = malloc(sizeof(*stat32), M_TEMP, M_WAITOK | M_ZERO); error = kern_kldstat(td, uap->fileid, stat); if (error == 0) { bcopy(&stat->name[0], &stat32->name[0], sizeof(stat->name)); CP(*stat, *stat32, refs); CP(*stat, *stat32, id); PTROUT_CP(*stat, *stat32, address); CP(*stat, *stat32, size); bcopy(&stat->pathname[0], &stat32->pathname[0], sizeof(stat->pathname)); stat32->version = version; error = copyout(stat32, uap->stat, version); } free(stat, M_TEMP); free(stat32, M_TEMP); return (error); } int freebsd32_posix_fallocate(struct thread *td, struct freebsd32_posix_fallocate_args *uap) { int error; error = kern_posix_fallocate(td, uap->fd, PAIR32TO64(off_t, uap->offset), PAIR32TO64(off_t, uap->len)); return (kern_posix_error(td, error)); } int freebsd32_posix_fadvise(struct thread *td, struct freebsd32_posix_fadvise_args *uap) { int error; error = kern_posix_fadvise(td, uap->fd, PAIR32TO64(off_t, uap->offset), PAIR32TO64(off_t, uap->len), uap->advice); return (kern_posix_error(td, error)); } int convert_sigevent32(struct sigevent32 *sig32, struct sigevent *sig) { CP(*sig32, *sig, sigev_notify); switch (sig->sigev_notify) { case SIGEV_NONE: break; case SIGEV_THREAD_ID: CP(*sig32, *sig, sigev_notify_thread_id); /* FALLTHROUGH */ case SIGEV_SIGNAL: CP(*sig32, *sig, sigev_signo); PTRIN_CP(*sig32, *sig, sigev_value.sival_ptr); break; case SIGEV_KEVENT: CP(*sig32, *sig, sigev_notify_kqueue); CP(*sig32, *sig, sigev_notify_kevent_flags); PTRIN_CP(*sig32, *sig, sigev_value.sival_ptr); break; default: return (EINVAL); } return (0); } int freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap) { void *data; union { struct procctl_reaper_status rs; struct procctl_reaper_pids rp; struct procctl_reaper_kill rk; } x; union { struct procctl_reaper_pids32 rp; } x32; int error, error1, flags, signum; if (uap->com >= PROC_PROCCTL_MD_MIN) return (cpu_procctl(td, uap->idtype, PAIR32TO64(id_t, uap->id), uap->com, PTRIN(uap->data))); switch (uap->com) { case PROC_ASLR_CTL: case PROC_PROTMAX_CTL: case PROC_SPROTECT: case PROC_STACKGAP_CTL: case PROC_TRACE_CTL: case PROC_TRAPCAP_CTL: case PROC_NO_NEW_PRIVS_CTL: case PROC_WXMAP_CTL: error = copyin(PTRIN(uap->data), &flags, sizeof(flags)); if (error != 0) return (error); data = &flags; break; case PROC_REAP_ACQUIRE: case PROC_REAP_RELEASE: if (uap->data != NULL) return (EINVAL); data = NULL; break; case PROC_REAP_STATUS: data = &x.rs; break; case PROC_REAP_GETPIDS: error = copyin(uap->data, &x32.rp, sizeof(x32.rp)); if (error != 0) return (error); CP(x32.rp, x.rp, rp_count); PTRIN_CP(x32.rp, x.rp, rp_pids); data = &x.rp; break; case PROC_REAP_KILL: error = copyin(uap->data, &x.rk, sizeof(x.rk)); if (error != 0) return (error); data = &x.rk; break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: case PROC_NO_NEW_PRIVS_STATUS: case PROC_WXMAP_STATUS: data = &flags; break; case PROC_PDEATHSIG_CTL: error = copyin(uap->data, &signum, sizeof(signum)); if (error != 0) return (error); data = &signum; break; case PROC_PDEATHSIG_STATUS: data = &signum; break; default: return (EINVAL); } error = kern_procctl(td, uap->idtype, PAIR32TO64(id_t, uap->id), uap->com, data); switch (uap->com) { case PROC_REAP_STATUS: if (error == 0) error = copyout(&x.rs, uap->data, sizeof(x.rs)); break; case PROC_REAP_KILL: error1 = copyout(&x.rk, uap->data, sizeof(x.rk)); if (error == 0) error = error1; break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: case PROC_NO_NEW_PRIVS_STATUS: case PROC_WXMAP_STATUS: if (error == 0) error = copyout(&flags, uap->data, sizeof(flags)); break; case PROC_PDEATHSIG_STATUS: if (error == 0) error = copyout(&signum, uap->data, sizeof(signum)); break; } return (error); } int freebsd32_fcntl(struct thread *td, struct freebsd32_fcntl_args *uap) { long tmp; switch (uap->cmd) { /* * Do unsigned conversion for arg when operation * interprets it as flags or pointer. */ case F_SETLK_REMOTE: case F_SETLKW: case F_SETLK: case F_GETLK: case F_SETFD: case F_SETFL: case F_OGETLK: case F_OSETLK: case F_OSETLKW: case F_KINFO: tmp = (unsigned int)(uap->arg); break; default: tmp = uap->arg; break; } return (kern_fcntl_freebsd(td, uap->fd, uap->cmd, tmp)); } int freebsd32_ppoll(struct thread *td, struct freebsd32_ppoll_args *uap) { struct timespec32 ts32; struct timespec ts, *tsp; sigset_t set, *ssp; int error; if (uap->ts != NULL) { error = copyin(uap->ts, &ts32, sizeof(ts32)); if (error != 0) return (error); CP(ts32, ts, tv_sec); CP(ts32, ts, tv_nsec); tsp = &ts; } else tsp = NULL; if (uap->set != NULL) { error = copyin(uap->set, &set, sizeof(set)); if (error != 0) return (error); ssp = &set; } else ssp = NULL; return (kern_poll(td, uap->fds, uap->nfds, tsp, ssp)); } int freebsd32_sched_rr_get_interval(struct thread *td, struct freebsd32_sched_rr_get_interval_args *uap) { struct timespec ts; struct timespec32 ts32; int error; error = kern_sched_rr_get_interval(td, uap->pid, &ts); if (error == 0) { CP(ts, ts32, tv_sec); CP(ts, ts32, tv_nsec); error = copyout(&ts32, uap->interval, sizeof(ts32)); } return (error); } static void timex_to_32(struct timex32 *dst, struct timex *src) { CP(*src, *dst, modes); CP(*src, *dst, offset); CP(*src, *dst, freq); CP(*src, *dst, maxerror); CP(*src, *dst, esterror); CP(*src, *dst, status); CP(*src, *dst, constant); CP(*src, *dst, precision); CP(*src, *dst, tolerance); CP(*src, *dst, ppsfreq); CP(*src, *dst, jitter); CP(*src, *dst, shift); CP(*src, *dst, stabil); CP(*src, *dst, jitcnt); CP(*src, *dst, calcnt); CP(*src, *dst, errcnt); CP(*src, *dst, stbcnt); } static void timex_from_32(struct timex *dst, struct timex32 *src) { CP(*src, *dst, modes); CP(*src, *dst, offset); CP(*src, *dst, freq); CP(*src, *dst, maxerror); CP(*src, *dst, esterror); CP(*src, *dst, status); CP(*src, *dst, constant); CP(*src, *dst, precision); CP(*src, *dst, tolerance); CP(*src, *dst, ppsfreq); CP(*src, *dst, jitter); CP(*src, *dst, shift); CP(*src, *dst, stabil); CP(*src, *dst, jitcnt); CP(*src, *dst, calcnt); CP(*src, *dst, errcnt); CP(*src, *dst, stbcnt); } int freebsd32_ntp_adjtime(struct thread *td, struct freebsd32_ntp_adjtime_args *uap) { struct timex tx; struct timex32 tx32; int error, retval; error = copyin(uap->tp, &tx32, sizeof(tx32)); if (error == 0) { timex_from_32(&tx, &tx32); error = kern_ntp_adjtime(td, &tx, &retval); if (error == 0) { timex_to_32(&tx32, &tx); error = copyout(&tx32, uap->tp, sizeof(tx32)); if (error == 0) td->td_retval[0] = retval; } } return (error); } #ifdef FFCLOCK extern struct mtx ffclock_mtx; extern struct ffclock_estimate ffclock_estimate; extern int8_t ffclock_updated; int freebsd32_ffclock_setestimate(struct thread *td, struct freebsd32_ffclock_setestimate_args *uap) { struct ffclock_estimate cest; struct ffclock_estimate32 cest32; int error; /* Reuse of PRIV_CLOCK_SETTIME. */ if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0) return (error); if ((error = copyin(uap->cest, &cest32, sizeof(struct ffclock_estimate32))) != 0) return (error); CP(cest.update_time, cest32.update_time, sec); memcpy(&cest.update_time.frac, &cest32.update_time.frac, sizeof(uint64_t)); CP(cest, cest32, update_ffcount); CP(cest, cest32, leapsec_next); CP(cest, cest32, period); CP(cest, cest32, errb_abs); CP(cest, cest32, errb_rate); CP(cest, cest32, status); CP(cest, cest32, leapsec_total); CP(cest, cest32, leapsec); mtx_lock(&ffclock_mtx); memcpy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate)); ffclock_updated++; mtx_unlock(&ffclock_mtx); return (error); } int freebsd32_ffclock_getestimate(struct thread *td, struct freebsd32_ffclock_getestimate_args *uap) { struct ffclock_estimate cest; struct ffclock_estimate32 cest32; int error; mtx_lock(&ffclock_mtx); memcpy(&cest, &ffclock_estimate, sizeof(struct ffclock_estimate)); mtx_unlock(&ffclock_mtx); CP(cest32.update_time, cest.update_time, sec); memcpy(&cest32.update_time.frac, &cest.update_time.frac, sizeof(uint64_t)); CP(cest32, cest, update_ffcount); CP(cest32, cest, leapsec_next); CP(cest32, cest, period); CP(cest32, cest, errb_abs); CP(cest32, cest, errb_rate); CP(cest32, cest, status); CP(cest32, cest, leapsec_total); CP(cest32, cest, leapsec); error = copyout(&cest32, uap->cest, sizeof(struct ffclock_estimate32)); return (error); } #else /* !FFCLOCK */ int freebsd32_ffclock_setestimate(struct thread *td, struct freebsd32_ffclock_setestimate_args *uap) { return (ENOSYS); } int freebsd32_ffclock_getestimate(struct thread *td, struct freebsd32_ffclock_getestimate_args *uap) { return (ENOSYS); } #endif /* FFCLOCK */ #ifdef COMPAT_43 int ofreebsd32_sethostid(struct thread *td, struct ofreebsd32_sethostid_args *uap) { int name[] = { CTL_KERN, KERN_HOSTID }; long hostid; hostid = uap->hostid; return (kernel_sysctl(td, name, nitems(name), NULL, NULL, &hostid, sizeof(hostid), NULL, 0)); } #endif diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index 540c2ef6fdaf..8e6024524614 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -1,246 +1,250 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002 Doug Rabson * Copyright (c) 2003 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define __ELF_WORD_SIZE 32 #include +#include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct ia32_mcontext) == 640); CTASSERT(sizeof(struct ia32_ucontext) == 704); CTASSERT(sizeof(struct ia32_sigframe) == 800); CTASSERT(sizeof(struct siginfo32) == 64); #ifdef COMPAT_FREEBSD4 CTASSERT(sizeof(struct ia32_freebsd4_mcontext) == 260); CTASSERT(sizeof(struct ia32_freebsd4_ucontext) == 324); CTASSERT(sizeof(struct ia32_freebsd4_sigframe) == 408); #endif #include "vdso_ia32_offsets.h" extern const char _binary_elf_vdso32_so_1_start[]; extern const char _binary_elf_vdso32_so_1_end[]; extern char _binary_elf_vdso32_so_1_size; extern const char *freebsd32_syscallnames[]; static SYSCTL_NODE(_compat, OID_AUTO, ia32, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "ia32 mode"); static u_long ia32_maxdsiz = IA32_MAXDSIZ; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxdsiz, CTLFLAG_RWTUN, &ia32_maxdsiz, 0, ""); u_long ia32_maxssiz = IA32_MAXSSIZ; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxssiz, CTLFLAG_RWTUN, &ia32_maxssiz, 0, ""); static u_long ia32_maxvmem = IA32_MAXVMEM; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxvmem, CTLFLAG_RWTUN, &ia32_maxvmem, 0, ""); struct sysentvec ia32_freebsd_sysvec = { .sv_size = FREEBSD32_SYS_MAXSYSCALL, .sv_table = freebsd32_sysent, .sv_transtrap = NULL, .sv_fixup = elf32_freebsd_fixup, .sv_sendsig = ia32_sendsig, .sv_sigcode = _binary_elf_vdso32_so_1_start, .sv_szsigcode = (int *)&_binary_elf_vdso32_so_1_size, .sv_sigcodeoff = VDSO_IA32_SIGCODE_OFFSET, .sv_name = "FreeBSD ELF32", .sv_coredump = elf32_coredump, .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = elf32_prepare_notes, .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = FREEBSD32_MINUSER, .sv_maxuser = FREEBSD32_MAXUSER, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_psstringssz = sizeof(struct freebsd32_ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = elf32_freebsd_copyout_auxargs, .sv_copyout_strings = freebsd32_copyout_strings, .sv_setregs = ia32_setregs, .sv_fixlimit = ia32_fixlimit, .sv_maxssiz = &ia32_maxssiz, .sv_flags = SV_ABI_FREEBSD | SV_ASLR | SV_IA32 | SV_ILP32 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER | SV_DSO_SIG, .sv_set_syscall_retval = ia32_set_syscall_retval, .sv_fetch_syscall_args = ia32_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, .sv_set_fork_retval = x86_set_fork_retval, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec); static Elf32_Brandinfo ia32_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &ia32_freebsd_sysvec, .interp_newpath = "/libexec/ld-elf32.so.1", .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(ia32, SI_SUB_EXEC, SI_ORDER_MIDDLE, (sysinit_cfunc_t) elf32_insert_brand_entry, &ia32_brand_info); static Elf32_Brandinfo ia32_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &ia32_freebsd_sysvec, .interp_newpath = "/libexec/ld-elf32.so.1", .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oia32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &ia32_brand_oinfo); static Elf32_Brandinfo kia32_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld.so.1", .sysvec = &ia32_freebsd_sysvec, .brand_note = &elf32_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kia32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &kia32_brand_info); void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { void *buf; size_t len; len = 0; if (use_xsave) { if (dst != NULL) { fpugetregs(td); len += elf32_populate_note(NT_X86_XSTATE, get_pcb_user_save_td(td), dst, cpu_max_ext_state_size, &buf); *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = xsave_mask; } else len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, cpu_max_ext_state_size, NULL); } *off = len; } void ia32_fixlimit(struct rlimit *rl, int which) { switch (which) { case RLIMIT_DATA: if (ia32_maxdsiz != 0) { if (rl->rlim_cur > ia32_maxdsiz) rl->rlim_cur = ia32_maxdsiz; if (rl->rlim_max > ia32_maxdsiz) rl->rlim_max = ia32_maxdsiz; } break; case RLIMIT_STACK: if (ia32_maxssiz != 0) { if (rl->rlim_cur > ia32_maxssiz) rl->rlim_cur = ia32_maxssiz; if (rl->rlim_max > ia32_maxssiz) rl->rlim_max = ia32_maxssiz; } break; case RLIMIT_VMEM: if (ia32_maxvmem != 0) { if (rl->rlim_cur > ia32_maxvmem) rl->rlim_cur = ia32_maxvmem; if (rl->rlim_max > ia32_maxvmem) rl->rlim_max = ia32_maxvmem; } break; } } diff --git a/sys/i386/i386/elf_machdep.c b/sys/i386/i386/elf_machdep.c index 86d9e0228076..e3994335eba5 100644 --- a/sys/i386/i386/elf_machdep.c +++ b/sys/i386/i386/elf_machdep.c @@ -1,310 +1,313 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_cpu.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ASLR | SV_IA32 | SV_ILP32 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, .sv_set_fork_retval = x86_set_fork_retval, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_info); static Elf32_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_oinfo); static Elf32_Brandinfo kfreebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &kfreebsd_brand_info); void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { void *buf; size_t len; len = 0; if (use_xsave) { if (dst != NULL) { npxgetregs(td); len += elf32_populate_note(NT_X86_XSTATE, get_pcb_user_save_td(td), dst, cpu_max_ext_state_size, &buf); *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = xsave_mask; } else len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, cpu_max_ext_state_size, NULL); } *off = len; } bool elf_is_ifunc_reloc(Elf_Size r_info) { return (ELF_R_TYPE(r_info) == R_386_IRELATIVE); } #define ERI_LOCAL 0x0001 /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup, int flags) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = *where; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if ((flags & ERI_LOCAL) != 0) { if (rtype == R_386_RELATIVE) { /* A + B */ addr = elf_relocaddr(lf, relocbase + addend); if (*where != addr) *where = addr; } return (0); } switch (rtype) { case R_386_NONE: /* none */ break; case R_386_32: /* S + A */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); addr += addend; if (*where != addr) *where = addr; break; case R_386_PC32: /* S + A - P */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); addr += addend - (Elf_Addr)where; if (*where != addr) *where = addr; break; case R_386_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation, " "symbol index %d\n", symidx); return (-1); break; case R_386_GLOB_DAT: /* S */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); if (*where != addr) *where = addr; break; case R_386_RELATIVE: break; case R_386_IRELATIVE: addr = relocbase + addend; addr = ((Elf_Addr (*)(void))addr)(); if (*where != addr) *where = addr; break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", rtype, symidx); return (-1); } return(0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, lookup, 0)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, lookup, ERI_LOCAL)); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index a0266108ec84..569c927c7a74 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1,2766 +1,2847 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2017 Dell EMC * Copyright (c) 2000-2001, 2003 David O'Brien * Copyright (c) 1995-1996 Søren Schmidt * Copyright (c) 1996 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ELF_NOTE_ROUNDSIZE 4 #define OLD_EI_BRAND 8 static int __elfN(check_header)(const Elf_Ehdr *hdr); static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int32_t *osrel, uint32_t *fctl0); static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry); static int __elfN(load_section)(struct image_params *imgp, vm_ooffset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot); static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp); static bool __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel); static bool kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel); static bool __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel, bool *has_fctl0, uint32_t *fctl0); static vm_prot_t __elfN(trans_prot)(Elf_Word); static Elf_Word __elfN(untrans_prot)(vm_prot_t); SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW | CTLFLAG_MPSAFE, 0, ""); int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, fallback_brand, CTLFLAG_RWTUN, &__elfN(fallback_brand), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) " brand of last resort"); static int elf_legacy_coredump = 0; SYSCTL_INT(_debug, OID_AUTO, __elfN(legacy_coredump), CTLFLAG_RW, &elf_legacy_coredump, 0, "include all and only RW pages in core dumps"); int __elfN(nxstack) = #if defined(__amd64__) || defined(__powerpc64__) /* both 64 and 32 bit */ || \ (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) || \ defined(__riscv) 1; #else 0; #endif SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, nxstack, CTLFLAG_RW, &__elfN(nxstack), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); #if defined(__amd64__) static int __elfN(vdso) = 1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, vdso, CTLFLAG_RWTUN, &__elfN(vdso), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable vdso preloading"); #else static int __elfN(vdso) = 0; #endif #if __ELF_WORD_SIZE == 32 && (defined(__amd64__) || defined(__i386__)) int i386_read_exec = 0; SYSCTL_INT(_kern_elf32, OID_AUTO, read_exec, CTLFLAG_RW, &i386_read_exec, 0, "enable execution from readable segments"); #endif static u_long __elfN(pie_base) = ET_DYN_LOAD_ADDR; static int sysctl_pie_base(SYSCTL_HANDLER_ARGS) { u_long val; int error; val = __elfN(pie_base); error = sysctl_handle_long(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if ((val & PAGE_MASK) != 0) return (EINVAL); __elfN(pie_base) = val; return (0); } SYSCTL_PROC(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, pie_base, CTLTYPE_ULONG | CTLFLAG_MPSAFE | CTLFLAG_RW, NULL, 0, sysctl_pie_base, "LU", "PIE load base without randomization"); SYSCTL_NODE(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, aslr, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, ""); #define ASLR_NODE_OID __CONCAT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), _aslr) /* * While for 64-bit machines ASLR works properly, there are * still some problems when using 32-bit architectures. For this * reason ASLR is only enabled by default when running native * 64-bit non-PIE executables. */ static int __elfN(aslr_enabled) = __ELF_WORD_SIZE == 64; SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, enable, CTLFLAG_RWTUN, &__elfN(aslr_enabled), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable address map randomization"); /* * Enable ASLR only for 64-bit PIE binaries by default. */ static int __elfN(pie_aslr_enabled) = __ELF_WORD_SIZE == 64; SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, pie_enable, CTLFLAG_RWTUN, &__elfN(pie_aslr_enabled), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable address map randomization for PIE binaries"); /* * Sbrk is now deprecated and it can be assumed, that in most * cases it will not be used anyway. This setting is valid only * for the ASLR enabled and allows for utilizing the bss grow region. */ static int __elfN(aslr_honor_sbrk) = 0; SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, honor_sbrk, CTLFLAG_RW, &__elfN(aslr_honor_sbrk), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": assume sbrk is used"); static int __elfN(aslr_stack) = 1; SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, stack, CTLFLAG_RWTUN, &__elfN(aslr_stack), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable stack address randomization"); static int __elfN(sigfastblock) = 1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, sigfastblock, CTLFLAG_RWTUN, &__elfN(sigfastblock), 0, "enable sigfastblock for new processes"); static bool __elfN(allow_wx) = true; SYSCTL_BOOL(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, allow_wx, CTLFLAG_RWTUN, &__elfN(allow_wx), 0, "Allow pages to be mapped simultaneously writable and executable"); static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; #define aligned(a, t) (rounddown2((u_long)(a), sizeof(t)) == (u_long)(a)) Elf_Brandnote __elfN(freebsd_brandnote) = { .hdr.n_namesz = sizeof(FREEBSD_ABI_VENDOR), .hdr.n_descsz = sizeof(int32_t), .hdr.n_type = NT_FREEBSD_ABI_TAG, .vendor = FREEBSD_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = __elfN(freebsd_trans_osrel) }; static bool __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel) { uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); *osrel = *(const int32_t *)(p); return (true); } static const char GNU_ABI_VENDOR[] = "GNU"; static int GNU_KFREEBSD_ABI_DESC = 3; Elf_Brandnote __elfN(kfreebsd_brandnote) = { .hdr.n_namesz = sizeof(GNU_ABI_VENDOR), .hdr.n_descsz = 16, /* XXX at least 16 */ .hdr.n_type = 1, .vendor = GNU_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = kfreebsd_trans_osrel }; static bool kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel) { const Elf32_Word *desc; uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); desc = (const Elf32_Word *)p; if (desc[0] != GNU_KFREEBSD_ABI_DESC) return (false); /* * Debian GNU/kFreeBSD embed the earliest compatible kernel version * (__FreeBSD_version: Rxx) in the LSB way. */ *osrel = desc[1] * 100000 + desc[2] * 1000 + desc[3]; return (true); } int __elfN(insert_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == NULL) { elf_brand_list[i] = entry; break; } } if (i == MAX_BRANDS) { printf("WARNING: %s: could not insert brandinfo entry: %p\n", __func__, entry); return (-1); } return (0); } int __elfN(remove_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == entry) { elf_brand_list[i] = NULL; break; } } if (i == MAX_BRANDS) return (-1); return (0); } bool __elfN(brand_inuse)(Elf_Brandinfo *entry) { struct proc *p; bool rval = false; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { if (p->p_sysent == entry->sysvec) { rval = true; break; } } sx_sunlock(&allproc_lock); return (rval); } static Elf_Brandinfo * __elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int32_t *osrel, uint32_t *fctl0) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; Elf_Brandinfo *bi, *bi_m; bool ret, has_fctl0; int i, interp_name_len; interp_name_len = interp != NULL ? strlen(interp) + 1 : 0; /* * We support four types of branding -- (1) the ELF EI_OSABI field * that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string * branding w/in the ELF header, (3) path of the `interp_path' * field, and (4) the ".note.ABI-tag" ELF section. */ /* Look for an ".note.ABI-tag" ELF section */ bi_m = NULL; for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL) continue; if (interp != NULL && (bi->flags & BI_BRAND_ONLY_STATIC) != 0) continue; if (hdr->e_machine == bi->machine && (bi->flags & (BI_BRAND_NOTE|BI_BRAND_NOTE_MANDATORY)) != 0) { has_fctl0 = false; *fctl0 = 0; *osrel = 0; ret = __elfN(check_note)(imgp, bi->brand_note, osrel, &has_fctl0, fctl0); /* Give brand a chance to veto check_note's guess */ if (ret && bi->header_supported) { ret = bi->header_supported(imgp, osrel, has_fctl0 ? fctl0 : NULL); } /* * If note checker claimed the binary, but the * interpreter path in the image does not * match default one for the brand, try to * search for other brands with the same * interpreter. Either there is better brand * with the right interpreter, or, failing * this, we return first brand which accepted * our note and, optionally, header. */ if (ret && bi_m == NULL && interp != NULL && (bi->interp_path == NULL || (strlen(bi->interp_path) + 1 != interp_name_len || strncmp(interp, bi->interp_path, interp_name_len) != 0))) { bi_m = bi; ret = 0; } if (ret) return (bi); } } if (bi_m != NULL) return (bi_m); /* If the executable has a brand, search for it in the brand list. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || (bi->flags & BI_BRAND_NOTE_MANDATORY) != 0 || (interp != NULL && (bi->flags & BI_BRAND_ONLY_STATIC) != 0)) continue; if (hdr->e_machine == bi->machine && (hdr->e_ident[EI_OSABI] == bi->brand || (bi->compat_3_brand != NULL && strcmp((const char *)&hdr->e_ident[OLD_EI_BRAND], bi->compat_3_brand) == 0))) { /* Looks good, but give brand a chance to veto */ if (bi->header_supported == NULL || bi->header_supported(imgp, NULL, NULL)) { /* * Again, prefer strictly matching * interpreter path. */ if (interp_name_len == 0 && bi->interp_path == NULL) return (bi); if (bi->interp_path != NULL && strlen(bi->interp_path) + 1 == interp_name_len && strncmp(interp, bi->interp_path, interp_name_len) == 0) return (bi); if (bi_m == NULL) bi_m = bi; } } } if (bi_m != NULL) return (bi_m); /* No known brand, see if the header is recognized by any brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY || bi->header_supported == NULL) continue; if (hdr->e_machine == bi->machine) { ret = bi->header_supported(imgp, NULL, NULL); if (ret) return (bi); } } /* Lacking a known brand, search for a recognized interpreter. */ if (interp != NULL) { for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || (bi->flags & (BI_BRAND_NOTE_MANDATORY | BI_BRAND_ONLY_STATIC)) != 0) continue; if (hdr->e_machine == bi->machine && bi->interp_path != NULL && /* ELF image p_filesz includes terminating zero */ strlen(bi->interp_path) + 1 == interp_name_len && strncmp(interp, bi->interp_path, interp_name_len) == 0 && (bi->header_supported == NULL || bi->header_supported(imgp, NULL, NULL))) return (bi); } } /* Lacking a recognized interpreter, try the default brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || (bi->flags & BI_BRAND_NOTE_MANDATORY) != 0 || (interp != NULL && (bi->flags & BI_BRAND_ONLY_STATIC) != 0)) continue; if (hdr->e_machine == bi->machine && __elfN(fallback_brand) == bi->brand && (bi->header_supported == NULL || bi->header_supported(imgp, NULL, NULL))) return (bi); } return (NULL); } static bool __elfN(phdr_in_zero_page)(const Elf_Ehdr *hdr) { return (hdr->e_phoff <= PAGE_SIZE && (u_int)hdr->e_phentsize * hdr->e_phnum <= PAGE_SIZE - hdr->e_phoff); } static int __elfN(check_header)(const Elf_Ehdr *hdr) { Elf_Brandinfo *bi; int i; if (!IS_ELF(*hdr) || hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_phentsize != sizeof(Elf_Phdr) || hdr->e_version != ELF_TARG_VER) return (ENOEXEC); /* * Make sure we have at least one brand for this machine. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi != NULL && bi->machine == hdr->e_machine) break; } if (i == MAX_BRANDS) return (ENOEXEC); return (0); } static int __elfN(map_partial)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot) { struct sf_buf *sf; int error; vm_offset_t off; /* * Create the page if it doesn't exist yet. Ignore errors. */ vm_map_fixed(map, NULL, 0, trunc_page(start), round_page(end) - trunc_page(start), VM_PROT_ALL, VM_PROT_ALL, MAP_CHECK_EXCL); /* * Find the page from the underlying object. */ if (object != NULL) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, end - start); vm_imgact_unmap_page(sf); if (error != 0) return (KERN_FAILURE); } return (KERN_SUCCESS); } static int __elfN(map_insert)(struct image_params *imgp, vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot, int cow) { struct sf_buf *sf; vm_offset_t off; vm_size_t sz; int error, locked, rv; if (start != trunc_page(start)) { rv = __elfN(map_partial)(map, object, offset, start, round_page(start), prot); if (rv != KERN_SUCCESS) return (rv); offset += round_page(start) - start; start = round_page(start); } if (end != round_page(end)) { rv = __elfN(map_partial)(map, object, offset + trunc_page(end) - start, trunc_page(end), end, prot); if (rv != KERN_SUCCESS) return (rv); end = trunc_page(end); } if (start >= end) return (KERN_SUCCESS); if ((offset & PAGE_MASK) != 0) { /* * The mapping is not page aligned. This means that we have * to copy the data. */ rv = vm_map_fixed(map, NULL, 0, start, end - start, prot | VM_PROT_WRITE, VM_PROT_ALL, MAP_CHECK_EXCL); if (rv != KERN_SUCCESS) return (rv); if (object == NULL) return (KERN_SUCCESS); for (; start < end; start += sz) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); sz = end - start; if (sz > PAGE_SIZE - off) sz = PAGE_SIZE - off; error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, sz); vm_imgact_unmap_page(sf); if (error != 0) return (KERN_FAILURE); offset += sz; } } else { vm_object_reference(object); rv = vm_map_fixed(map, object, offset, start, end - start, prot, VM_PROT_ALL, cow | MAP_CHECK_EXCL | (object != NULL ? MAP_VN_EXEC : 0)); if (rv != KERN_SUCCESS) { locked = VOP_ISLOCKED(imgp->vp); VOP_UNLOCK(imgp->vp); vm_object_deallocate(object); vn_lock(imgp->vp, locked | LK_RETRY); return (rv); } else if (object != NULL) { MPASS(imgp->vp->v_object == object); VOP_SET_TEXT_CHECKED(imgp->vp); } } return (KERN_SUCCESS); } static int __elfN(load_section)(struct image_params *imgp, vm_ooffset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) { struct sf_buf *sf; size_t map_len; vm_map_t map; vm_object_t object; vm_offset_t map_addr; int error, rv, cow; size_t copy_len; vm_ooffset_t file_addr; /* * It's necessary to fail if the filsz + offset taken from the * header is greater than the actual file pager object's size. * If we were to allow this, then the vm_map_find() below would * walk right off the end of the file object and into the ether. * * While I'm here, might as well check for something else that * is invalid: filsz cannot be greater than memsz. */ if ((filsz != 0 && (off_t)filsz + offset > imgp->attr->va_size) || filsz > memsz) { uprintf("elf_load_section: truncated ELF file\n"); return (ENOEXEC); } object = imgp->object; map = &imgp->proc->p_vmspace->vm_map; map_addr = trunc_page((vm_offset_t)vmaddr); file_addr = trunc_page(offset); /* * We have two choices. We can either clear the data in the last page * of an oversized mapping, or we can start the anon mapping a page * early and copy the initialized data into that first page. We * choose the second. */ if (filsz == 0) map_len = 0; else if (memsz > filsz) map_len = trunc_page(offset + filsz) - file_addr; else map_len = round_page(offset + filsz) - file_addr; if (map_len != 0) { /* cow flags: don't dump readonly sections in core */ cow = MAP_COPY_ON_WRITE | MAP_PREFAULT | (prot & VM_PROT_WRITE ? 0 : MAP_DISABLE_COREDUMP); rv = __elfN(map_insert)(imgp, map, object, file_addr, map_addr, map_addr + map_len, prot, cow); if (rv != KERN_SUCCESS) return (EINVAL); /* we can stop now if we've covered it all */ if (memsz == filsz) return (0); } /* * We have to get the remaining bit of the file into the first part * of the oversized map segment. This is normally because the .data * segment in the file is extended to provide bss. It's a neat idea * to try and save a page, but it's a pain in the behind to implement. */ copy_len = filsz == 0 ? 0 : (offset + filsz) - trunc_page(offset + filsz); map_addr = trunc_page((vm_offset_t)vmaddr + filsz); map_len = round_page((vm_offset_t)vmaddr + memsz) - map_addr; /* This had damn well better be true! */ if (map_len != 0) { rv = __elfN(map_insert)(imgp, map, NULL, 0, map_addr, map_addr + map_len, prot, 0); if (rv != KERN_SUCCESS) return (EINVAL); } if (copy_len != 0) { sf = vm_imgact_map_page(object, offset + filsz); if (sf == NULL) return (EIO); /* send the page fragment to user space */ error = copyout((caddr_t)sf_buf_kva(sf), (caddr_t)map_addr, copy_len); vm_imgact_unmap_page(sf); if (error != 0) return (error); } /* * Remove write access to the page if it was only granted by map_insert * to allow copyout. */ if ((prot & VM_PROT_WRITE) == 0) vm_map_protect(map, trunc_page(map_addr), round_page(map_addr + map_len), prot, 0, VM_MAP_PROTECT_SET_PROT); return (0); } static int __elfN(load_sections)(struct image_params *imgp, const Elf_Ehdr *hdr, const Elf_Phdr *phdr, u_long rbase, u_long *base_addrp) { vm_prot_t prot; u_long base_addr; bool first; int error, i; ASSERT_VOP_LOCKED(imgp->vp, __func__); base_addr = 0; first = true; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) continue; /* Loadable segment */ prot = __elfN(trans_prot)(phdr[i].p_flags); error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase, phdr[i].p_memsz, phdr[i].p_filesz, prot); if (error != 0) return (error); /* * Establish the base address if this is the first segment. */ if (first) { base_addr = trunc_page(phdr[i].p_vaddr + rbase); first = false; } } if (base_addrp != NULL) *base_addrp = base_addr; return (0); } /* * Load the file "file" into memory. It may be either a shared object * or an executable. * * The "addr" reference parameter is in/out. On entry, it specifies * the address where a shared object should be loaded. If the file is * an executable, this value is ignored. On exit, "addr" specifies * where the file was actually loaded. * * The "entry" reference parameter is out only. On exit, it specifies * the entry point for the loaded file. */ static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry) { struct { struct nameidata nd; struct vattr attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; struct nameidata *nd; struct vattr *attr; struct image_params *imgp; u_long rbase; u_long base_addr = 0; int error; #ifdef CAPABILITY_MODE /* * XXXJA: This check can go away once we are sufficiently confident * that the checks in namei() are correct. */ if (IN_CAPABILITY_MODE(curthread)) return (ECAPMODE); #endif tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK | M_ZERO); nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; /* * Initialize part of the common data */ imgp->proc = p; imgp->attr = attr; NDINIT(nd, LOOKUP, ISOPEN | FOLLOW | LOCKSHARED | LOCKLEAF, UIO_SYSSPACE, file); if ((error = namei(nd)) != 0) { nd->ni_vp = NULL; goto fail; } NDFREE(nd, NDF_ONLY_PNBUF); imgp->vp = nd->ni_vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. */ error = exec_check_permissions(imgp); if (error) goto fail; error = exec_map_first_page(imgp); if (error) goto fail; imgp->object = nd->ni_vp->v_object; hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) goto fail; if (hdr->e_type == ET_DYN) rbase = *addr; else if (hdr->e_type == ET_EXEC) rbase = 0; else { error = ENOEXEC; goto fail; } /* Only support headers that fit within first page for now */ if (!__elfN(phdr_in_zero_page)(hdr)) { error = ENOEXEC; goto fail; } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) { error = ENOEXEC; goto fail; } error = __elfN(load_sections)(imgp, hdr, phdr, rbase, &base_addr); if (error != 0) goto fail; *addr = base_addr; *entry = (unsigned long)hdr->e_entry + rbase; fail: if (imgp->firstpage) exec_unmap_first_page(imgp); if (nd->ni_vp) { if (imgp->textset) VOP_UNSET_TEXT_CHECKED(nd->ni_vp); vput(nd->ni_vp); } free(tempdata, M_TEMP); return (error); } /* * Select randomized valid address in the map map, between minv and * maxv, with specified alignment. The [minv, maxv) range must belong * to the map. Note that function only allocates the address, it is * up to caller to clamp maxv in a way that the final allocation * length fit into the map. * * Result is returned in *resp, error code indicates that arguments * did not pass sanity checks for overflow and range correctness. */ static int __CONCAT(rnd_, __elfN(base))(vm_map_t map, u_long minv, u_long maxv, u_int align, u_long *resp) { u_long rbase, res; MPASS(vm_map_min(map) <= minv); if (minv >= maxv || minv + align >= maxv || maxv > vm_map_max(map)) { uprintf("Invalid ELF segments layout\n"); return (ENOEXEC); } arc4rand(&rbase, sizeof(rbase), 0); res = roundup(minv, (u_long)align) + rbase % (maxv - minv); res &= ~((u_long)align - 1); if (res >= maxv) res -= align; KASSERT(res >= minv, ("res %#lx < minv %#lx, maxv %#lx rbase %#lx", res, minv, maxv, rbase)); KASSERT(res < maxv, ("res %#lx > maxv %#lx, minv %#lx rbase %#lx", res, maxv, minv, rbase)); *resp = res; return (0); } static int __elfN(enforce_limits)(struct image_params *imgp, const Elf_Ehdr *hdr, const Elf_Phdr *phdr, u_long et_dyn_addr) { struct vmspace *vmspace; const char *err_str; u_long text_size, data_size, total_size, text_addr, data_addr; u_long seg_size, seg_addr; int i; err_str = NULL; text_size = data_size = total_size = text_addr = data_addr = 0; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) continue; seg_addr = trunc_page(phdr[i].p_vaddr + et_dyn_addr); seg_size = round_page(phdr[i].p_memsz + phdr[i].p_vaddr + et_dyn_addr - seg_addr); /* * Make the largest executable segment the official * text segment and all others data. * * Note that obreak() assumes that data_addr + data_size == end * of data load area, and the ELF file format expects segments * to be sorted by address. If multiple data segments exist, * the last one will be used. */ if ((phdr[i].p_flags & PF_X) != 0 && text_size < seg_size) { text_size = seg_size; text_addr = seg_addr; } else { data_size = seg_size; data_addr = seg_addr; } total_size += seg_size; } if (data_addr == 0 && data_size == 0) { data_addr = text_addr; data_size = text_size; } /* * Check limits. It should be safe to check the * limits after loading the segments since we do * not actually fault in all the segments pages. */ PROC_LOCK(imgp->proc); if (data_size > lim_cur_proc(imgp->proc, RLIMIT_DATA)) err_str = "Data segment size exceeds process limit"; else if (text_size > maxtsiz) err_str = "Text segment size exceeds system limit"; else if (total_size > lim_cur_proc(imgp->proc, RLIMIT_VMEM)) err_str = "Total segment size exceeds process limit"; else if (racct_set(imgp->proc, RACCT_DATA, data_size) != 0) err_str = "Data segment size exceeds resource limit"; else if (racct_set(imgp->proc, RACCT_VMEM, total_size) != 0) err_str = "Total segment size exceeds resource limit"; PROC_UNLOCK(imgp->proc); if (err_str != NULL) { uprintf("%s\n", err_str); return (ENOMEM); } vmspace = imgp->proc->p_vmspace; vmspace->vm_tsize = text_size >> PAGE_SHIFT; vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr; vmspace->vm_dsize = data_size >> PAGE_SHIFT; vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr; return (0); } static int __elfN(get_interp)(struct image_params *imgp, const Elf_Phdr *phdr, char **interpp, bool *free_interpp) { struct thread *td; char *interp; int error, interp_name_len; KASSERT(phdr->p_type == PT_INTERP, ("%s: p_type %u != PT_INTERP", __func__, phdr->p_type)); ASSERT_VOP_LOCKED(imgp->vp, __func__); td = curthread; /* Path to interpreter */ if (phdr->p_filesz < 2 || phdr->p_filesz > MAXPATHLEN) { uprintf("Invalid PT_INTERP\n"); return (ENOEXEC); } interp_name_len = phdr->p_filesz; if (phdr->p_offset > PAGE_SIZE || interp_name_len > PAGE_SIZE - phdr->p_offset) { /* * The vnode lock might be needed by the pagedaemon to * clean pages owned by the vnode. Do not allow sleep * waiting for memory with the vnode locked, instead * try non-sleepable allocation first, and if it * fails, go to the slow path were we drop the lock * and do M_WAITOK. A text reference prevents * modifications to the vnode content. */ interp = malloc(interp_name_len + 1, M_TEMP, M_NOWAIT); if (interp == NULL) { VOP_UNLOCK(imgp->vp); interp = malloc(interp_name_len + 1, M_TEMP, M_WAITOK); vn_lock(imgp->vp, LK_SHARED | LK_RETRY); } error = vn_rdwr(UIO_READ, imgp->vp, interp, interp_name_len, phdr->p_offset, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, NULL, td); if (error != 0) { free(interp, M_TEMP); uprintf("i/o error PT_INTERP %d\n", error); return (error); } interp[interp_name_len] = '\0'; *interpp = interp; *free_interpp = true; return (0); } interp = __DECONST(char *, imgp->image_header) + phdr->p_offset; if (interp[interp_name_len - 1] != '\0') { uprintf("Invalid PT_INTERP\n"); return (ENOEXEC); } *interpp = interp; *free_interpp = false; return (0); } static int __elfN(load_interp)(struct image_params *imgp, const Elf_Brandinfo *brand_info, const char *interp, u_long *addr, u_long *entry) { char *path; int error; if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); error = __elfN(load_file)(imgp->proc, path, addr, entry); free(path, M_TEMP); if (error == 0) return (0); } if (brand_info->interp_newpath != NULL && (brand_info->interp_path == NULL || strcmp(interp, brand_info->interp_path) == 0)) { error = __elfN(load_file)(imgp->proc, brand_info->interp_newpath, addr, entry); if (error == 0) return (0); } error = __elfN(load_file)(imgp->proc, interp, addr, entry); if (error == 0) return (0); uprintf("ELF interpreter %s not found, error %d\n", interp, error); return (error); } /* * Impossible et_dyn_addr initial value indicating that the real base * must be calculated later with some randomization applied. */ #define ET_DYN_ADDR_RAND 1 static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { struct thread *td; const Elf_Ehdr *hdr; const Elf_Phdr *phdr; Elf_Auxargs *elf_auxargs; struct vmspace *vmspace; vm_map_t map; char *interp; Elf_Brandinfo *brand_info; struct sysentvec *sv; u_long addr, baddr, et_dyn_addr, entry, proghdr; u_long maxalign, maxsalign, mapsz, maxv, maxv1, anon_loc; uint32_t fctl0; int32_t osrel; bool free_interp; int error, i, n; hdr = (const Elf_Ehdr *)imgp->image_header; /* * Do we have a valid ELF header ? * * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later * if particular brand doesn't support it. */ if (__elfN(check_header)(hdr) != 0 || (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) return (-1); /* * From here on down, we return an errno, not -1, as we've * detected an ELF file. */ if (!__elfN(phdr_in_zero_page)(hdr)) { uprintf("Program headers not in the first page\n"); return (ENOEXEC); } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) { uprintf("Unaligned program headers\n"); return (ENOEXEC); } n = error = 0; baddr = 0; osrel = 0; fctl0 = 0; entry = proghdr = 0; interp = NULL; free_interp = false; td = curthread; /* * Somewhat arbitrary, limit accepted max alignment for the * loadable segment to the max supported superpage size. Too * large alignment requests are not useful and are indicators * of corrupted or outright malicious binary. */ maxalign = PAGE_SIZE; maxsalign = PAGE_SIZE * 1024; for (i = MAXPAGESIZES - 1; i > 0; i--) { if (pagesizes[i] > maxsalign) maxsalign = pagesizes[i]; } mapsz = 0; for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: if (n == 0) baddr = phdr[i].p_vaddr; if (!powerof2(phdr[i].p_align) || phdr[i].p_align > maxsalign) { uprintf("Invalid segment alignment\n"); error = ENOEXEC; goto ret; } if (phdr[i].p_align > maxalign) maxalign = phdr[i].p_align; if (mapsz + phdr[i].p_memsz < mapsz) { uprintf("Mapsize overflow\n"); error = ENOEXEC; goto ret; } mapsz += phdr[i].p_memsz; n++; /* * If this segment contains the program headers, * remember their virtual address for the AT_PHDR * aux entry. Static binaries don't usually include * a PT_PHDR entry. */ if (phdr[i].p_offset == 0 && hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize <= phdr[i].p_filesz) proghdr = phdr[i].p_vaddr + hdr->e_phoff; break; case PT_INTERP: /* Path to interpreter */ if (interp != NULL) { uprintf("Multiple PT_INTERP headers\n"); error = ENOEXEC; goto ret; } error = __elfN(get_interp)(imgp, &phdr[i], &interp, &free_interp); if (error != 0) goto ret; break; case PT_GNU_STACK: if (__elfN(nxstack)) imgp->stack_prot = __elfN(trans_prot)(phdr[i].p_flags); imgp->stack_sz = phdr[i].p_memsz; break; case PT_PHDR: /* Program header table info */ proghdr = phdr[i].p_vaddr; break; } } brand_info = __elfN(get_brandinfo)(imgp, interp, &osrel, &fctl0); if (brand_info == NULL) { uprintf("ELF binary type \"%u\" not known.\n", hdr->e_ident[EI_OSABI]); error = ENOEXEC; goto ret; } sv = brand_info->sysvec; et_dyn_addr = 0; if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) { uprintf("Cannot execute shared object\n"); error = ENOEXEC; goto ret; } /* * Honour the base load address from the dso if it is * non-zero for some reason. */ if (baddr == 0) { if ((sv->sv_flags & SV_ASLR) == 0 || (fctl0 & NT_FREEBSD_FCTL_ASLR_DISABLE) != 0) et_dyn_addr = __elfN(pie_base); else if ((__elfN(pie_aslr_enabled) && (imgp->proc->p_flag2 & P2_ASLR_DISABLE) == 0) || (imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0) et_dyn_addr = ET_DYN_ADDR_RAND; else et_dyn_addr = __elfN(pie_base); } } /* * Avoid a possible deadlock if the current address space is destroyed * and that address space maps the locked vnode. In the common case, * the locked vnode's v_usecount is decremented but remains greater * than zero. Consequently, the vnode lock is not needed by vrele(). * However, in cases where the vnode lock is external, such as nullfs, * v_usecount may become zero. * * The VV_TEXT flag prevents modifications to the executable while * the vnode is unlocked. */ VOP_UNLOCK(imgp->vp); /* * Decide whether to enable randomization of user mappings. * First, reset user preferences for the setid binaries. * Then, account for the support of the randomization by the * ABI, by user preferences, and make special treatment for * PIE binaries. */ if (imgp->credential_setid) { PROC_LOCK(imgp->proc); imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE | P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC); PROC_UNLOCK(imgp->proc); } if ((sv->sv_flags & SV_ASLR) == 0 || (imgp->proc->p_flag2 & P2_ASLR_DISABLE) != 0 || (fctl0 & NT_FREEBSD_FCTL_ASLR_DISABLE) != 0) { KASSERT(et_dyn_addr != ET_DYN_ADDR_RAND, ("et_dyn_addr == RAND and !ASLR")); } else if ((imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0 || (__elfN(aslr_enabled) && hdr->e_type == ET_EXEC) || et_dyn_addr == ET_DYN_ADDR_RAND) { imgp->map_flags |= MAP_ASLR; /* * If user does not care about sbrk, utilize the bss * grow region for mappings as well. We can select * the base for the image anywere and still not suffer * from the fragmentation. */ if (!__elfN(aslr_honor_sbrk) || (imgp->proc->p_flag2 & P2_ASLR_IGNSTART) != 0) imgp->map_flags |= MAP_ASLR_IGNSTART; if (__elfN(aslr_stack)) imgp->map_flags |= MAP_ASLR_STACK; } if ((!__elfN(allow_wx) && (fctl0 & NT_FREEBSD_FCTL_WXNEEDED) == 0 && (imgp->proc->p_flag2 & P2_WXORX_DISABLE) == 0) || (imgp->proc->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0) imgp->map_flags |= MAP_WXORX; error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; imgp->proc->p_elf_brandinfo = brand_info; vmspace = imgp->proc->p_vmspace; map = &vmspace->vm_map; maxv = sv->sv_usrstack; if ((imgp->map_flags & MAP_ASLR_STACK) == 0) maxv -= lim_max(td, RLIMIT_STACK); if (error == 0 && mapsz >= maxv - vm_map_min(map)) { uprintf("Excessive mapping size\n"); error = ENOEXEC; } if (error == 0 && et_dyn_addr == ET_DYN_ADDR_RAND) { KASSERT((map->flags & MAP_ASLR) != 0, ("ET_DYN_ADDR_RAND but !MAP_ASLR")); error = __CONCAT(rnd_, __elfN(base))(map, vm_map_min(map) + mapsz + lim_max(td, RLIMIT_DATA), /* reserve half of the address space to interpreter */ maxv / 2, maxalign, &et_dyn_addr); } vn_lock(imgp->vp, LK_SHARED | LK_RETRY); if (error != 0) goto ret; error = __elfN(load_sections)(imgp, hdr, phdr, et_dyn_addr, NULL); if (error != 0) goto ret; error = __elfN(enforce_limits)(imgp, hdr, phdr, et_dyn_addr); if (error != 0) goto ret; /* * We load the dynamic linker where a userland call * to mmap(0, ...) would put it. The rationale behind this * calculation is that it leaves room for the heap to grow to * its maximum allowed size. */ addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(td, RLIMIT_DATA)); if ((map->flags & MAP_ASLR) != 0) { maxv1 = maxv / 2 + addr / 2; error = __CONCAT(rnd_, __elfN(base))(map, addr, maxv1, (MAXPAGESIZES > 1 && pagesizes[1] != 0) ? pagesizes[1] : pagesizes[0], &anon_loc); if (error != 0) goto ret; map->anon_loc = anon_loc; } else { map->anon_loc = addr; } entry = (u_long)hdr->e_entry + et_dyn_addr; imgp->entry_addr = entry; if (interp != NULL) { VOP_UNLOCK(imgp->vp); if ((map->flags & MAP_ASLR) != 0) { /* Assume that interpreter fits into 1/4 of AS */ maxv1 = maxv / 2 + addr / 2; error = __CONCAT(rnd_, __elfN(base))(map, addr, maxv1, PAGE_SIZE, &addr); } if (error == 0) { error = __elfN(load_interp)(imgp, brand_info, interp, &addr, &imgp->entry_addr); } vn_lock(imgp->vp, LK_SHARED | LK_RETRY); if (error != 0) goto ret; } else addr = et_dyn_addr; error = exec_map_stack(imgp); if (error != 0) goto ret; /* * Construct auxargs table (used by the copyout_auxargs routine) */ elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_NOWAIT); if (elf_auxargs == NULL) { VOP_UNLOCK(imgp->vp); elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); vn_lock(imgp->vp, LK_SHARED | LK_RETRY); } elf_auxargs->execfd = -1; elf_auxargs->phdr = proghdr + et_dyn_addr; elf_auxargs->phent = hdr->e_phentsize; elf_auxargs->phnum = hdr->e_phnum; elf_auxargs->pagesz = PAGE_SIZE; elf_auxargs->base = addr; elf_auxargs->flags = 0; elf_auxargs->entry = entry; elf_auxargs->hdr_eflags = hdr->e_flags; imgp->auxargs = elf_auxargs; imgp->interpreted = 0; imgp->reloc_base = addr; imgp->proc->p_osrel = osrel; imgp->proc->p_fctl0 = fctl0; imgp->proc->p_elf_flags = hdr->e_flags; ret: ASSERT_VOP_LOCKED(imgp->vp, "skipped relock"); if (free_interp) free(interp, M_TEMP); return (error); } #define elf_suword __CONCAT(suword, __ELF_WORD_SIZE) int __elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintptr_t base) { Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; Elf_Auxinfo *argarray, *pos; int error; argarray = pos = malloc(AT_COUNT * sizeof(*pos), M_TEMP, M_WAITOK | M_ZERO); if (args->execfd != -1) AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd); AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); AUXARGS_ENTRY(pos, AT_PHENT, args->phent); AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY(pos, AT_BASE, args->base); AUXARGS_ENTRY(pos, AT_EHDRFLAGS, args->hdr_eflags); if (imgp->execpathp != 0) AUXARGS_ENTRY_PTR(pos, AT_EXECPATH, imgp->execpathp); AUXARGS_ENTRY(pos, AT_OSRELDATE, imgp->proc->p_ucred->cr_prison->pr_osreldate); if (imgp->canary != 0) { AUXARGS_ENTRY_PTR(pos, AT_CANARY, imgp->canary); AUXARGS_ENTRY(pos, AT_CANARYLEN, imgp->canarylen); } AUXARGS_ENTRY(pos, AT_NCPUS, mp_ncpus); if (imgp->pagesizes != 0) { AUXARGS_ENTRY_PTR(pos, AT_PAGESIZES, imgp->pagesizes); AUXARGS_ENTRY(pos, AT_PAGESIZESLEN, imgp->pagesizeslen); } if (imgp->sysent->sv_timekeep_base != 0) { AUXARGS_ENTRY(pos, AT_TIMEKEEP, imgp->sysent->sv_timekeep_base); } AUXARGS_ENTRY(pos, AT_STACKPROT, imgp->sysent->sv_shared_page_obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : imgp->sysent->sv_stackprot); if (imgp->sysent->sv_hwcap != NULL) AUXARGS_ENTRY(pos, AT_HWCAP, *imgp->sysent->sv_hwcap); if (imgp->sysent->sv_hwcap2 != NULL) AUXARGS_ENTRY(pos, AT_HWCAP2, *imgp->sysent->sv_hwcap2); AUXARGS_ENTRY(pos, AT_BSDFLAGS, __elfN(sigfastblock) ? ELF_BSDF_SIGFASTBLK : 0); AUXARGS_ENTRY(pos, AT_ARGC, imgp->args->argc); AUXARGS_ENTRY_PTR(pos, AT_ARGV, imgp->argv); AUXARGS_ENTRY(pos, AT_ENVC, imgp->args->envc); AUXARGS_ENTRY_PTR(pos, AT_ENVV, imgp->envv); AUXARGS_ENTRY_PTR(pos, AT_PS_STRINGS, imgp->ps_strings); if (imgp->sysent->sv_fxrng_gen_base != 0) AUXARGS_ENTRY(pos, AT_FXRNG, imgp->sysent->sv_fxrng_gen_base); if (imgp->sysent->sv_vdso_base != 0 && __elfN(vdso) != 0) AUXARGS_ENTRY(pos, AT_KPRELOAD, imgp->sysent->sv_vdso_base); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); imgp->auxargs = NULL; KASSERT(pos - argarray <= AT_COUNT, ("Too many auxargs")); error = copyout(argarray, (void *)base, sizeof(*argarray) * AT_COUNT); free(argarray, M_TEMP); return (error); } int __elfN(freebsd_fixup)(uintptr_t *stack_base, struct image_params *imgp) { Elf_Addr *base; base = (Elf_Addr *)*stack_base; base--; if (elf_suword(base, imgp->args->argc) == -1) return (EFAULT); *stack_base = (uintptr_t)base; return (0); } /* * Code for generating ELF core dumps. */ typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ }; struct note_info { int type; /* Note type. */ outfunc_t outfunc; /* Output function. */ void *outarg; /* Argument for the output function. */ size_t outsize; /* Output size. */ TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ }; TAILQ_HEAD(note_info_list, note_info); extern int compress_user_cores; extern int compress_user_cores_level; static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_dumpable_segment(struct thread *, segment_callback, void *, int); static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, struct note_info_list *, size_t, int); static void __elfN(putnote)(struct thread *td, struct note_info *, struct sbuf *); static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); static void __elfN(note_ptlwpinfo)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_auxv)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_proc)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); static void note_procstat_files(void *, struct sbuf *, size_t *); static void note_procstat_groups(void *, struct sbuf *, size_t *); static void note_procstat_osrel(void *, struct sbuf *, size_t *); static void note_procstat_rlimit(void *, struct sbuf *, size_t *); static void note_procstat_umask(void *, struct sbuf *, size_t *); static void note_procstat_vmmap(void *, struct sbuf *, size_t *); static int core_compressed_write(void *base, size_t len, off_t offset, void *arg) { return (core_write((struct coredump_params *)arg, base, len, offset, UIO_SYSSPACE, NULL)); } int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int compm, error = 0; struct sseg_closure seginfo; struct note_info_list notelst; struct coredump_params params; struct note_info *ninfo; void *hdr, *tmpbuf; size_t hdrsize, notesz, coresize; hdr = NULL; tmpbuf = NULL; TAILQ_INIT(¬elst); /* Size the program segments. */ __elfN(size_segments)(td, &seginfo, flags); /* * Collect info about the core file header area. */ hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); if (seginfo.count + 1 >= PN_XNUM) hdrsize += sizeof(Elf_Shdr); td->td_proc->p_sysent->sv_elf_core_prepare_notes(td, ¬elst, ¬esz); coresize = round_page(hdrsize + notesz) + seginfo.size; /* Set up core dump parameters. */ params.offset = 0; params.active_cred = cred; params.file_cred = NOCRED; params.td = td; params.vp = vp; params.comp = NULL; #ifdef RACCT if (racct_enable) { PROC_LOCK(td->td_proc); error = racct_add(td->td_proc, RACCT_CORE, coresize); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } } #endif if (coresize >= limit) { error = EFAULT; goto done; } /* Create a compression stream if necessary. */ compm = compress_user_cores; if ((flags & (SVC_PT_COREDUMP | SVC_NOCOMPRESS)) == SVC_PT_COREDUMP && compm == 0) compm = COMPRESS_GZIP; if (compm != 0) { params.comp = compressor_init(core_compressed_write, compm, CORE_BUF_SIZE, compress_user_cores_level, ¶ms); if (params.comp == NULL) { error = EFAULT; goto done; } tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); } /* * Allocate memory for building the header, fill it up, * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, notesz, flags); /* Write the contents of all of the writable segments. */ if (error == 0) { Elf_Phdr *php; off_t offset; int i; php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; offset = round_page(hdrsize + notesz); for (i = 0; i < seginfo.count; i++) { error = core_output((char *)(uintptr_t)php->p_vaddr, php->p_filesz, offset, ¶ms, tmpbuf); if (error != 0) break; offset += php->p_filesz; php++; } if (error == 0 && params.comp != NULL) error = compressor_flush(params.comp); } if (error) { log(LOG_WARNING, "Failed to write core file for process %s (error %d)\n", curproc->p_comm, error); } done: free(tmpbuf, M_TEMP); if (params.comp != NULL) compressor_fini(params.comp); while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { TAILQ_REMOVE(¬elst, ninfo, link); free(ninfo, M_TEMP); } if (hdr != NULL) free(hdr, M_TEMP); return (error); } /* * A callback for each_dumpable_segment() to write out the segment's * program header entry. */ static void cb_put_phdr(vm_map_entry_t entry, void *closure) { struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; phdr->p_offset = phc->offset; phdr->p_vaddr = entry->start; phdr->p_paddr = 0; phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; phdr->p_align = PAGE_SIZE; phdr->p_flags = __elfN(untrans_prot)(entry->protection); phc->offset += phdr->p_filesz; phc->phdr++; } /* * A callback for each_dumpable_segment() to gather information about * the number of segments and their total size. */ static void cb_size_segment(vm_map_entry_t entry, void *closure) { struct sseg_closure *ssc = (struct sseg_closure *)closure; ssc->count++; ssc->size += entry->end - entry->start; } void __elfN(size_segments)(struct thread *td, struct sseg_closure *seginfo, int flags) { seginfo->count = 0; seginfo->size = 0; each_dumpable_segment(td, cb_size_segment, seginfo, flags); } /* * For each writable segment in the process's memory map, call the given * function with a pointer to the map entry and some arbitrary * caller-supplied data. */ static void each_dumpable_segment(struct thread *td, segment_callback func, void *closure, int flags) { struct proc *p = td->td_proc; vm_map_t map = &p->p_vmspace->vm_map; vm_map_entry_t entry; vm_object_t backing_object, object; bool ignore_entry; vm_map_lock_read(map); VM_MAP_ENTRY_FOREACH(entry, map) { /* * Don't dump inaccessible mappings, deal with legacy * coredump mode. * * Note that read-only segments related to the elf binary * are marked MAP_ENTRY_NOCOREDUMP now so we no longer * need to arbitrarily ignore such segments. */ if ((flags & SVC_ALL) == 0) { if (elf_legacy_coredump) { if ((entry->protection & VM_PROT_RW) != VM_PROT_RW) continue; } else { if ((entry->protection & VM_PROT_ALL) == 0) continue; } } /* * Dont include memory segment in the coredump if * MAP_NOCORE is set in mmap(2) or MADV_NOCORE in * madvise(2). Do not dump submaps (i.e. parts of the * kernel map). */ if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) continue; if ((entry->eflags & MAP_ENTRY_NOCOREDUMP) != 0 && (flags & SVC_ALL) == 0) continue; if ((object = entry->object.vm_object) == NULL) continue; /* Ignore memory-mapped devices and such things. */ VM_OBJECT_RLOCK(object); while ((backing_object = object->backing_object) != NULL) { VM_OBJECT_RLOCK(backing_object); VM_OBJECT_RUNLOCK(object); object = backing_object; } ignore_entry = (object->flags & OBJ_FICTITIOUS) != 0; VM_OBJECT_RUNLOCK(object); if (ignore_entry) continue; (*func)(entry, closure); } vm_map_unlock_read(map); } /* * Write the core file header to the file, including padding up to * the page boundary. */ static int __elfN(corehdr)(struct coredump_params *p, int numsegs, void *hdr, size_t hdrsize, struct note_info_list *notelst, size_t notesz, int flags) { struct note_info *ninfo; struct sbuf *sb; int error; /* Fill in the header. */ bzero(hdr, hdrsize); __elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz, flags); sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_core_output, p); sbuf_start_section(sb, NULL); sbuf_bcat(sb, hdr, hdrsize); TAILQ_FOREACH(ninfo, notelst, link) __elfN(putnote)(p->td, ninfo, sb); /* Align up to a page boundary for the program segments. */ sbuf_end_section(sb, -1, PAGE_SIZE, 0); error = sbuf_finish(sb); sbuf_delete(sb); return (error); } void __elfN(prepare_notes)(struct thread *td, struct note_info_list *list, size_t *sizep) { struct proc *p; struct thread *thr; size_t size; p = td->td_proc; size = 0; size += __elfN(register_note)(td, list, NT_PRPSINFO, __elfN(note_prpsinfo), p); /* * To have the debugger select the right thread (LWP) as the initial * thread, we dump the state of the thread passed to us in td first. * This is the thread that causes the core dump and thus likely to * be the right thread one wants to have selected in the debugger. */ thr = td; while (thr != NULL) { size += __elfN(register_note)(td, list, NT_PRSTATUS, __elfN(note_prstatus), thr); size += __elfN(register_note)(td, list, NT_FPREGSET, __elfN(note_fpregset), thr); size += __elfN(register_note)(td, list, NT_THRMISC, __elfN(note_thrmisc), thr); size += __elfN(register_note)(td, list, NT_PTLWPINFO, __elfN(note_ptlwpinfo), thr); size += __elfN(register_note)(td, list, -1, __elfN(note_threadmd), thr); thr = thr == td ? TAILQ_FIRST(&p->p_threads) : TAILQ_NEXT(thr, td_plist); if (thr == td) thr = TAILQ_NEXT(thr, td_plist); } size += __elfN(register_note)(td, list, NT_PROCSTAT_PROC, __elfN(note_procstat_proc), p); size += __elfN(register_note)(td, list, NT_PROCSTAT_FILES, note_procstat_files, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_VMMAP, note_procstat_vmmap, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_GROUPS, note_procstat_groups, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_UMASK, note_procstat_umask, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_RLIMIT, note_procstat_rlimit, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_OSREL, note_procstat_osrel, p); size += __elfN(register_note)(td, list, NT_PROCSTAT_PSSTRINGS, __elfN(note_procstat_psstrings), p); size += __elfN(register_note)(td, list, NT_PROCSTAT_AUXV, __elfN(note_procstat_auxv), p); *sizep = size; } void __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, size_t notesz, int flags) { Elf_Ehdr *ehdr; Elf_Phdr *phdr; Elf_Shdr *shdr; struct phdr_closure phc; Elf_Brandinfo *bi; ehdr = (Elf_Ehdr *)hdr; bi = td->td_proc->p_elf_brandinfo; ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELF_CLASS; ehdr->e_ident[EI_DATA] = ELF_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = td->td_proc->p_sysent->sv_elf_core_osabi; ehdr->e_ident[EI_ABIVERSION] = 0; ehdr->e_ident[EI_PAD] = 0; ehdr->e_type = ET_CORE; ehdr->e_machine = bi->machine; ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(Elf_Ehdr); ehdr->e_flags = td->td_proc->p_elf_flags; ehdr->e_ehsize = sizeof(Elf_Ehdr); ehdr->e_phentsize = sizeof(Elf_Phdr); ehdr->e_shentsize = sizeof(Elf_Shdr); ehdr->e_shstrndx = SHN_UNDEF; if (numsegs + 1 < PN_XNUM) { ehdr->e_phnum = numsegs + 1; ehdr->e_shnum = 0; } else { ehdr->e_phnum = PN_XNUM; ehdr->e_shnum = 1; ehdr->e_shoff = ehdr->e_phoff + (numsegs + 1) * ehdr->e_phentsize; KASSERT(ehdr->e_shoff == hdrsize - sizeof(Elf_Shdr), ("e_shoff: %zu, hdrsize - shdr: %zu", (size_t)ehdr->e_shoff, hdrsize - sizeof(Elf_Shdr))); shdr = (Elf_Shdr *)((char *)hdr + ehdr->e_shoff); memset(shdr, 0, sizeof(*shdr)); /* * A special first section is used to hold large segment and * section counts. This was proposed by Sun Microsystems in * Solaris and has been adopted by Linux; the standard ELF * tools are already familiar with the technique. * * See table 7-7 of the Solaris "Linker and Libraries Guide" * (or 12-7 depending on the version of the document) for more * details. */ shdr->sh_type = SHT_NULL; shdr->sh_size = ehdr->e_shnum; shdr->sh_link = ehdr->e_shstrndx; shdr->sh_info = numsegs + 1; } /* * Fill in the program header entries. */ phdr = (Elf_Phdr *)((char *)hdr + ehdr->e_phoff); /* The note segement. */ phdr->p_type = PT_NOTE; phdr->p_offset = hdrsize; phdr->p_vaddr = 0; phdr->p_paddr = 0; phdr->p_filesz = notesz; phdr->p_memsz = 0; phdr->p_flags = PF_R; phdr->p_align = ELF_NOTE_ROUNDSIZE; phdr++; /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = round_page(hdrsize + notesz); each_dumpable_segment(td, cb_put_phdr, &phc, flags); } size_t __elfN(register_note)(struct thread *td, struct note_info_list *list, int type, outfunc_t out, void *arg) { const struct sysentvec *sv; struct note_info *ninfo; size_t size, notesize; sv = td->td_proc->p_sysent; size = 0; out(arg, NULL, &size); ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); ninfo->type = type; ninfo->outfunc = out; ninfo->outarg = arg; ninfo->outsize = size; TAILQ_INSERT_TAIL(list, ninfo, link); if (type == -1) return (size); notesize = sizeof(Elf_Note) + /* note header */ roundup2(strlen(sv->sv_elf_core_abi_vendor) + 1, ELF_NOTE_ROUNDSIZE) + /* note name */ roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ return (notesize); } static size_t append_note_data(const void *src, void *dst, size_t len) { size_t padded_len; padded_len = roundup2(len, ELF_NOTE_ROUNDSIZE); if (dst != NULL) { bcopy(src, dst, len); bzero((char *)dst + len, padded_len - len); } return (padded_len); } size_t __elfN(populate_note)(int type, void *src, void *dst, size_t size, void **descp) { Elf_Note *note; char *buf; size_t notesize; buf = dst; if (buf != NULL) { note = (Elf_Note *)buf; note->n_namesz = sizeof(FREEBSD_ABI_VENDOR); note->n_descsz = size; note->n_type = type; buf += sizeof(*note); buf += append_note_data(FREEBSD_ABI_VENDOR, buf, sizeof(FREEBSD_ABI_VENDOR)); append_note_data(src, buf, size); if (descp != NULL) *descp = buf; } notesize = sizeof(Elf_Note) + /* note header */ roundup2(sizeof(FREEBSD_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + /* note name */ roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ return (notesize); } static void __elfN(putnote)(struct thread *td, struct note_info *ninfo, struct sbuf *sb) { Elf_Note note; const struct sysentvec *sv; ssize_t old_len, sect_len; size_t new_len, descsz, i; if (ninfo->type == -1) { ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); return; } sv = td->td_proc->p_sysent; note.n_namesz = strlen(sv->sv_elf_core_abi_vendor) + 1; note.n_descsz = ninfo->outsize; note.n_type = ninfo->type; sbuf_bcat(sb, ¬e, sizeof(note)); sbuf_start_section(sb, &old_len); sbuf_bcat(sb, sv->sv_elf_core_abi_vendor, strlen(sv->sv_elf_core_abi_vendor) + 1); sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); if (note.n_descsz == 0) return; sbuf_start_section(sb, &old_len); ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); sect_len = sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); if (sect_len < 0) return; new_len = (size_t)sect_len; descsz = roundup(note.n_descsz, ELF_NOTE_ROUNDSIZE); if (new_len < descsz) { /* * It is expected that individual note emitters will correctly * predict their expected output size and fill up to that size * themselves, padding in a format-specific way if needed. * However, in case they don't, just do it here with zeros. */ for (i = 0; i < descsz - new_len; i++) sbuf_putc(sb, 0); } else if (new_len > descsz) { /* * We can't always truncate sb -- we may have drained some * of it already. */ KASSERT(new_len == descsz, ("%s: Note type %u changed as we " "read it (%zu > %zu). Since it is longer than " "expected, this coredump's notes are corrupt. THIS " "IS A BUG in the note_procstat routine for type %u.\n", __func__, (unsigned)note.n_type, new_len, descsz, (unsigned)note.n_type)); } } /* * Miscellaneous note out functions. */ #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 #include #include typedef struct prstatus32 elf_prstatus_t; typedef struct prpsinfo32 elf_prpsinfo_t; typedef struct fpreg32 elf_prfpregset_t; typedef struct fpreg32 elf_fpregset_t; typedef struct reg32 elf_gregset_t; typedef struct thrmisc32 elf_thrmisc_t; #define ELF_KERN_PROC_MASK KERN_PROC_MASK32 typedef struct kinfo_proc32 elf_kinfo_proc_t; typedef uint32_t elf_ps_strings_t; #else typedef prstatus_t elf_prstatus_t; typedef prpsinfo_t elf_prpsinfo_t; typedef prfpregset_t elf_prfpregset_t; typedef prfpregset_t elf_fpregset_t; typedef gregset_t elf_gregset_t; typedef thrmisc_t elf_thrmisc_t; #define ELF_KERN_PROC_MASK 0 typedef struct kinfo_proc elf_kinfo_proc_t; typedef vm_offset_t elf_ps_strings_t; #endif static void __elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) { struct sbuf sbarg; size_t len; char *cp, *end; struct proc *p; elf_prpsinfo_t *psinfo; int error; p = arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); PROC_LOCK(p); if (p->p_args != NULL) { len = sizeof(psinfo->pr_psargs) - 1; if (len > p->p_args->ar_length) len = p->p_args->ar_length; memcpy(psinfo->pr_psargs, p->p_args->ar_args, len); PROC_UNLOCK(p); error = 0; } else { _PHOLD(p); PROC_UNLOCK(p); sbuf_new(&sbarg, psinfo->pr_psargs, sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN); error = proc_getargv(curthread, p, &sbarg); PRELE(p); if (sbuf_finish(&sbarg) == 0) len = sbuf_len(&sbarg) - 1; else len = sizeof(psinfo->pr_psargs) - 1; sbuf_delete(&sbarg); } if (error || len == 0) strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs)); else { KASSERT(len < sizeof(psinfo->pr_psargs), ("len is too long: %zu vs %zu", len, sizeof(psinfo->pr_psargs))); cp = psinfo->pr_psargs; end = cp + len - 1; for (;;) { cp = memchr(cp, '\0', end - cp); if (cp == NULL) break; *cp = ' '; } } psinfo->pr_pid = p->p_pid; sbuf_bcat(sb, psinfo, sizeof(*psinfo)); free(psinfo, M_TEMP); } *sizep = sizeof(*psinfo); } -static void -__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) +static bool +__elfN(get_prstatus)(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) { - struct thread *td; elf_prstatus_t *status; - td = arg; - if (sb != NULL) { - KASSERT(*sizep == sizeof(*status), ("invalid size")); - status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); + if (buf != NULL) { + KASSERT(*sizep == sizeof(*status), ("%s: invalid size", + __func__)); + status = buf; status->pr_version = PRSTATUS_VERSION; status->pr_statussz = sizeof(elf_prstatus_t); status->pr_gregsetsz = sizeof(elf_gregset_t); status->pr_fpregsetsz = sizeof(elf_fpregset_t); status->pr_osreldate = osreldate; status->pr_cursig = td->td_proc->p_sig; status->pr_pid = td->td_tid; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 fill_regs32(td, &status->pr_reg); #else fill_regs(td, &status->pr_reg); #endif + } + *sizep = sizeof(*status); + return (true); +} + +static bool +__elfN(set_prstatus)(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + elf_prstatus_t *status; + + KASSERT(size == sizeof(*status), ("%s: invalid size", __func__)); + status = buf; +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + set_regs32(td, &status->pr_reg); +#else + set_regs(td, &status->pr_reg); +#endif + return (true); +} + +static struct regset __elfN(regset_prstatus) = { + .note = NT_PRSTATUS, + .size = sizeof(elf_prstatus_t), + .get = __elfN(get_prstatus), + .set = __elfN(set_prstatus), +}; +ELF_REGSET(__elfN(regset_prstatus)); + +static void +__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + elf_prstatus_t *status; + + td = arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*status), ("%s: invalid size", + __func__)); + status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); + __elfN(get_prstatus)(NULL, td, status, sizep); sbuf_bcat(sb, status, sizeof(*status)); free(status, M_TEMP); } *sizep = sizeof(*status); } +static bool +__elfN(get_fpregset)(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) +{ + elf_prfpregset_t *fpregset; + + if (buf != NULL) { + KASSERT(*sizep == sizeof(*fpregset), ("%s: invalid size", + __func__)); + fpregset = buf; +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + fill_fpregs32(td, fpregset); +#else + fill_fpregs(td, fpregset); +#endif + } + *sizep = sizeof(fpregset); + return (true); +} + +static bool +__elfN(set_fpregset)(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + elf_prfpregset_t *fpregset; + + fpregset = buf; + KASSERT(size == sizeof(*fpregset), ("%s: invalid size", __func__)); +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + set_fpregs32(td, fpregset); +#else + set_fpregs(td, fpregset); +#endif + return (true); +} + +static struct regset __elfN(regset_fpregset) = { + .note = NT_FPREGSET, + .size = sizeof(elf_prfpregset_t), + .get = __elfN(get_fpregset), + .set = __elfN(set_fpregset), +}; +ELF_REGSET(__elfN(regset_fpregset)); + static void __elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_prfpregset_t *fpregset; td = arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); -#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - fill_fpregs32(td, fpregset); -#else - fill_fpregs(td, fpregset); -#endif + __elfN(get_fpregset)(NULL, td, fpregset, sizep); sbuf_bcat(sb, fpregset, sizeof(*fpregset)); free(fpregset, M_TEMP); } *sizep = sizeof(*fpregset); } static void __elfN(note_thrmisc)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_thrmisc_t thrmisc; td = arg; if (sb != NULL) { KASSERT(*sizep == sizeof(thrmisc), ("invalid size")); bzero(&thrmisc, sizeof(thrmisc)); strcpy(thrmisc.pr_tname, td->td_name); sbuf_bcat(sb, &thrmisc, sizeof(thrmisc)); } *sizep = sizeof(thrmisc); } static void __elfN(note_ptlwpinfo)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; size_t size; int structsize; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 struct ptrace_lwpinfo32 pl; #else struct ptrace_lwpinfo pl; #endif td = arg; size = sizeof(structsize) + sizeof(pl); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(pl); sbuf_bcat(sb, &structsize, sizeof(structsize)); bzero(&pl, sizeof(pl)); pl.pl_lwpid = td->td_tid; pl.pl_event = PL_EVENT_NONE; pl.pl_sigmask = td->td_sigmask; pl.pl_siglist = td->td_siglist; if (td->td_si.si_signo != 0) { pl.pl_event = PL_EVENT_SIGNAL; pl.pl_flags |= PL_FLAG_SI; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 siginfo_to_siginfo32(&td->td_si, &pl.pl_siginfo); #else pl.pl_siginfo = td->td_si; #endif } strcpy(pl.pl_tdname, td->td_name); /* XXX TODO: supply more information in struct ptrace_lwpinfo*/ sbuf_bcat(sb, &pl, sizeof(pl)); } *sizep = size; } /* * Allow for MD specific notes, as well as any MD * specific preparations for writing MI notes. */ static void __elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; void *buf; size_t size; td = (struct thread *)arg; size = *sizep; if (size != 0 && sb != NULL) buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); else buf = NULL; size = 0; __elfN(dump_thread)(td, buf, &size); KASSERT(sb == NULL || *sizep == size, ("invalid size")); if (size != 0 && sb != NULL) sbuf_bcat(sb, buf, size); free(buf, M_TEMP); *sizep = size; } #ifdef KINFO_PROC_SIZE CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); #endif static void __elfN(note_procstat_proc)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = arg; size = sizeof(structsize) + p->p_numthreads * sizeof(elf_kinfo_proc_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(elf_kinfo_proc_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sx_slock(&proctree_lock); PROC_LOCK(p); kern_proc_out(p, sb, ELF_KERN_PROC_MASK); sx_sunlock(&proctree_lock); } *sizep = size; } #ifdef KINFO_FILE_SIZE CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static void note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size, sect_sz, i; ssize_t start_len, sect_len; int structsize, filedesc_flags; if (coredump_pack_fileinfo) filedesc_flags = KERN_FILEDESC_PACK_KINFO; else filedesc_flags = 0; p = arg; structsize = sizeof(struct kinfo_file); if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_count_drain, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, -1, filedesc_flags); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { sbuf_start_section(sb, &start_len); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, *sizep - sizeof(structsize), filedesc_flags); sect_len = sbuf_end_section(sb, start_len, 0, 0); if (sect_len < 0) return; sect_sz = sect_len; KASSERT(sect_sz <= *sizep, ("kern_proc_filedesc_out did not respect maxlen; " "requested %zu, got %zu", *sizep - sizeof(structsize), sect_sz - sizeof(structsize))); for (i = 0; i < *sizep - sect_sz && sb->s_error == 0; i++) sbuf_putc(sb, 0); } } #ifdef KINFO_VMENTRY_SIZE CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif static void note_procstat_vmmap(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize, vmmap_flags; if (coredump_pack_vmmapinfo) vmmap_flags = KERN_VMMAP_PACK_KINFO; else vmmap_flags = 0; p = arg; structsize = sizeof(struct kinfo_vmentry); if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_count_drain, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb, -1, vmmap_flags); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb, *sizep - sizeof(structsize), vmmap_flags); } } static void note_procstat_groups(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = arg; size = sizeof(structsize) + p->p_ucred->cr_ngroups * sizeof(gid_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(gid_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, p->p_ucred->cr_groups, p->p_ucred->cr_ngroups * sizeof(gid_t)); } *sizep = size; } static void note_procstat_umask(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = arg; size = sizeof(structsize) + sizeof(p->p_pd->pd_cmask); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_pd->pd_cmask); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_pd->pd_cmask, sizeof(p->p_pd->pd_cmask)); } *sizep = size; } static void note_procstat_rlimit(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; struct rlimit rlim[RLIM_NLIMITS]; size_t size; int structsize, i; p = arg; size = sizeof(structsize) + sizeof(rlim); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(rlim); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); for (i = 0; i < RLIM_NLIMITS; i++) lim_rlimit_proc(p, i, &rlim[i]); PROC_UNLOCK(p); sbuf_bcat(sb, rlim, sizeof(rlim)); } *sizep = size; } static void note_procstat_osrel(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = arg; size = sizeof(structsize) + sizeof(p->p_osrel); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_osrel); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_osrel, sizeof(p->p_osrel)); } *sizep = size; } static void __elfN(note_procstat_psstrings)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; elf_ps_strings_t ps_strings; size_t size; int structsize; p = arg; size = sizeof(structsize) + sizeof(ps_strings); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(ps_strings); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 ps_strings = PTROUT(PROC_PS_STRINGS(p)); #else ps_strings = PROC_PS_STRINGS(p); #endif sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &ps_strings, sizeof(ps_strings)); } *sizep = size; } static void __elfN(note_procstat_auxv)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, AT_COUNT * sizeof(Elf_Auxinfo), SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_count_drain, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(Elf_Auxinfo); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); } } static bool __elfN(parse_notes)(struct image_params *imgp, Elf_Note *checknote, const char *note_vendor, const Elf_Phdr *pnote, bool (*cb)(const Elf_Note *, void *, bool *), void *cb_arg) { const Elf_Note *note, *note0, *note_end; const char *note_name; char *buf; int i, error; bool res; /* We need some limit, might as well use PAGE_SIZE. */ if (pnote == NULL || pnote->p_filesz > PAGE_SIZE) return (false); ASSERT_VOP_LOCKED(imgp->vp, "parse_notes"); if (pnote->p_offset > PAGE_SIZE || pnote->p_filesz > PAGE_SIZE - pnote->p_offset) { buf = malloc(pnote->p_filesz, M_TEMP, M_NOWAIT); if (buf == NULL) { VOP_UNLOCK(imgp->vp); buf = malloc(pnote->p_filesz, M_TEMP, M_WAITOK); vn_lock(imgp->vp, LK_SHARED | LK_RETRY); } error = vn_rdwr(UIO_READ, imgp->vp, buf, pnote->p_filesz, pnote->p_offset, UIO_SYSSPACE, IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, curthread); if (error != 0) { uprintf("i/o error PT_NOTE\n"); goto retf; } note = note0 = (const Elf_Note *)buf; note_end = (const Elf_Note *)(buf + pnote->p_filesz); } else { note = note0 = (const Elf_Note *)(imgp->image_header + pnote->p_offset); note_end = (const Elf_Note *)(imgp->image_header + pnote->p_offset + pnote->p_filesz); buf = NULL; } for (i = 0; i < 100 && note >= note0 && note < note_end; i++) { if (!aligned(note, Elf32_Addr) || (const char *)note_end - (const char *)note < sizeof(Elf_Note)) { goto retf; } if (note->n_namesz != checknote->n_namesz || note->n_descsz != checknote->n_descsz || note->n_type != checknote->n_type) goto nextnote; note_name = (const char *)(note + 1); if (note_name + checknote->n_namesz >= (const char *)note_end || strncmp(note_vendor, note_name, checknote->n_namesz) != 0) goto nextnote; if (cb(note, cb_arg, &res)) goto ret; nextnote: note = (const Elf_Note *)((const char *)(note + 1) + roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE) + roundup2(note->n_descsz, ELF_NOTE_ROUNDSIZE)); } retf: res = false; ret: free(buf, M_TEMP); return (res); } struct brandnote_cb_arg { Elf_Brandnote *brandnote; int32_t *osrel; }; static bool brandnote_cb(const Elf_Note *note, void *arg0, bool *res) { struct brandnote_cb_arg *arg; arg = arg0; /* * Fetch the osreldate for binary from the ELF OSABI-note if * necessary. */ *res = (arg->brandnote->flags & BN_TRANSLATE_OSREL) != 0 && arg->brandnote->trans_osrel != NULL ? arg->brandnote->trans_osrel(note, arg->osrel) : true; return (true); } static Elf_Note fctl_note = { .n_namesz = sizeof(FREEBSD_ABI_VENDOR), .n_descsz = sizeof(uint32_t), .n_type = NT_FREEBSD_FEATURE_CTL, }; struct fctl_cb_arg { bool *has_fctl0; uint32_t *fctl0; }; static bool note_fctl_cb(const Elf_Note *note, void *arg0, bool *res) { struct fctl_cb_arg *arg; const Elf32_Word *desc; uintptr_t p; arg = arg0; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); desc = (const Elf32_Word *)p; *arg->has_fctl0 = true; *arg->fctl0 = desc[0]; *res = true; return (true); } /* * Try to find the appropriate ABI-note section for checknote, fetch * the osreldate and feature control flags for binary from the ELF * OSABI-note. Only the first page of the image is searched, the same * as for headers. */ static bool __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *brandnote, int32_t *osrel, bool *has_fctl0, uint32_t *fctl0) { const Elf_Phdr *phdr; const Elf_Ehdr *hdr; struct brandnote_cb_arg b_arg; struct fctl_cb_arg f_arg; int i, j; hdr = (const Elf_Ehdr *)imgp->image_header; phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); b_arg.brandnote = brandnote; b_arg.osrel = osrel; f_arg.has_fctl0 = has_fctl0; f_arg.fctl0 = fctl0; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_NOTE && __elfN(parse_notes)(imgp, &brandnote->hdr, brandnote->vendor, &phdr[i], brandnote_cb, &b_arg)) { for (j = 0; j < hdr->e_phnum; j++) { if (phdr[j].p_type == PT_NOTE && __elfN(parse_notes)(imgp, &fctl_note, FREEBSD_ABI_VENDOR, &phdr[j], note_fctl_cb, &f_arg)) break; } return (true); } } return (false); } /* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw __elfN(execsw) = { .ex_imgact = __CONCAT(exec_, __elfN(imgact)), .ex_name = __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) }; EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); static vm_prot_t __elfN(trans_prot)(Elf_Word flags) { vm_prot_t prot; prot = 0; if (flags & PF_X) prot |= VM_PROT_EXECUTE; if (flags & PF_W) prot |= VM_PROT_WRITE; if (flags & PF_R) prot |= VM_PROT_READ; #if __ELF_WORD_SIZE == 32 && (defined(__amd64__) || defined(__i386__)) if (i386_read_exec && (flags & PF_R)) prot |= VM_PROT_EXECUTE; #endif return (prot); } static Elf_Word __elfN(untrans_prot)(vm_prot_t prot) { Elf_Word flags; flags = 0; if (prot & VM_PROT_EXECUTE) flags |= PF_X; if (prot & VM_PROT_READ) flags |= PF_R; if (prot & VM_PROT_WRITE) flags |= PF_W; return (flags); } diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 81ba4c0d0080..5ab9eed6a114 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,903 +1,905 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 Terrence R. Lambert * All rights reserved. * * Copyright (c) 1982, 1986, 1989, 1991, 1992, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)init_main.c 8.9 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_kdb.h" #include "opt_init_path.h" #include "opt_verbose_sysinit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void mi_startup(void); /* Should be elsewhere */ /* Components of the first process -- never freed. */ static struct session session0; static struct pgrp pgrp0; struct proc proc0; struct thread0_storage thread0_st __aligned(32); struct vmspace vmspace0; struct proc *initproc; int linux_alloc_current_noop(struct thread *td __unused, int flags __unused) { return (0); } int (*lkpi_alloc_current)(struct thread *, int) = linux_alloc_current_noop; #ifndef BOOTHOWTO #define BOOTHOWTO 0 #endif int boothowto = BOOTHOWTO; /* initialized so that it can be patched */ SYSCTL_INT(_debug, OID_AUTO, boothowto, CTLFLAG_RD, &boothowto, 0, "Boot control flags, passed from loader"); #ifndef BOOTVERBOSE #define BOOTVERBOSE 0 #endif int bootverbose = BOOTVERBOSE; SYSCTL_INT(_debug, OID_AUTO, bootverbose, CTLFLAG_RW, &bootverbose, 0, "Control the output of verbose kernel messages"); #ifdef VERBOSE_SYSINIT /* * We'll use the defined value of VERBOSE_SYSINIT from the kernel config to * dictate the default VERBOSE_SYSINIT behavior. Significant values for this * option and associated tunable are: * - 0, 'compiled in but silent by default' * - 1, 'compiled in but verbose by default' (default) */ int verbose_sysinit = VERBOSE_SYSINIT; TUNABLE_INT("debug.verbose_sysinit", &verbose_sysinit); #endif #ifdef INVARIANTS FEATURE(invariants, "Kernel compiled with INVARIANTS, may affect performance"); #endif /* * This ensures that there is at least one entry so that the sysinit_set * symbol is not undefined. A sybsystem ID of SI_SUB_DUMMY is never * executed. */ SYSINIT(placeholder, SI_SUB_DUMMY, SI_ORDER_ANY, NULL, NULL); /* * The sysinit table itself. Items are checked off as the are run. * If we want to register new sysinit types, add them to newsysinit. */ SET_DECLARE(sysinit_set, struct sysinit); struct sysinit **sysinit, **sysinit_end; struct sysinit **newsysinit, **newsysinit_end; /* * Merge a new sysinit set into the current set, reallocating it if * necessary. This can only be called after malloc is running. */ void sysinit_add(struct sysinit **set, struct sysinit **set_end) { struct sysinit **newset; struct sysinit **sipp; struct sysinit **xipp; int count; count = set_end - set; if (newsysinit) count += newsysinit_end - newsysinit; else count += sysinit_end - sysinit; newset = malloc(count * sizeof(*sipp), M_TEMP, M_NOWAIT); if (newset == NULL) panic("cannot malloc for sysinit"); xipp = newset; if (newsysinit) for (sipp = newsysinit; sipp < newsysinit_end; sipp++) *xipp++ = *sipp; else for (sipp = sysinit; sipp < sysinit_end; sipp++) *xipp++ = *sipp; for (sipp = set; sipp < set_end; sipp++) *xipp++ = *sipp; if (newsysinit) free(newsysinit, M_TEMP); newsysinit = newset; newsysinit_end = newset + count; } #if defined (DDB) && defined(VERBOSE_SYSINIT) static const char * symbol_name(vm_offset_t va, db_strategy_t strategy) { const char *name; c_db_sym_t sym; db_expr_t offset; if (va == 0) return (NULL); sym = db_search_symbol(va, strategy, &offset); if (offset != 0) return (NULL); db_symbol_values(sym, &name, NULL); return (name); } #endif /* * System startup; initialize the world, create process 0, mount root * filesystem, and fork to create init and pagedaemon. Most of the * hard work is done in the lower-level initialization routines including * startup(), which does memory initialization and autoconfiguration. * * This allows simple addition of new kernel subsystems that require * boot time initialization. It also allows substitution of subsystem * (for instance, a scheduler, kernel profiler, or VM system) by object * module. Finally, it allows for optional "kernel threads". */ void mi_startup(void) { struct sysinit **sipp; /* system initialization*/ struct sysinit **xipp; /* interior loop of sort*/ struct sysinit *save; /* bubble*/ #if defined(VERBOSE_SYSINIT) int last; int verbose; #endif TSENTER(); if (boothowto & RB_VERBOSE) bootverbose++; if (sysinit == NULL) { sysinit = SET_BEGIN(sysinit_set); sysinit_end = SET_LIMIT(sysinit_set); } restart: /* * Perform a bubble sort of the system initialization objects by * their subsystem (primary key) and order (secondary key). */ for (sipp = sysinit; sipp < sysinit_end; sipp++) { for (xipp = sipp + 1; xipp < sysinit_end; xipp++) { if ((*sipp)->subsystem < (*xipp)->subsystem || ((*sipp)->subsystem == (*xipp)->subsystem && (*sipp)->order <= (*xipp)->order)) continue; /* skip*/ save = *sipp; *sipp = *xipp; *xipp = save; } } #if defined(VERBOSE_SYSINIT) last = SI_SUB_COPYRIGHT; verbose = 0; #if !defined(DDB) printf("VERBOSE_SYSINIT: DDB not enabled, symbol lookups disabled.\n"); #endif #endif /* * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ for (sipp = sysinit; sipp < sysinit_end; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) continue; /* skip dummy task(s)*/ if ((*sipp)->subsystem == SI_SUB_DONE) continue; #if defined(VERBOSE_SYSINIT) if ((*sipp)->subsystem > last && verbose_sysinit != 0) { verbose = 1; last = (*sipp)->subsystem; printf("subsystem %x\n", last); } if (verbose) { #if defined(DDB) const char *func, *data; func = symbol_name((vm_offset_t)(*sipp)->func, DB_STGY_PROC); data = symbol_name((vm_offset_t)(*sipp)->udata, DB_STGY_ANY); if (func != NULL && data != NULL) printf(" %s(&%s)... ", func, data); else if (func != NULL) printf(" %s(%p)... ", func, (*sipp)->udata); else #endif printf(" %p(%p)... ", (*sipp)->func, (*sipp)->udata); } #endif /* Call function */ (*((*sipp)->func))((*sipp)->udata); #if defined(VERBOSE_SYSINIT) if (verbose) printf("done.\n"); #endif /* Check off the one we're just done */ (*sipp)->subsystem = SI_SUB_DONE; /* Check if we've installed more sysinit items via KLD */ if (newsysinit != NULL) { if (sysinit != SET_BEGIN(sysinit_set)) free(sysinit, M_TEMP); sysinit = newsysinit; sysinit_end = newsysinit_end; newsysinit = NULL; newsysinit_end = NULL; goto restart; } } TSEXIT(); /* Here so we don't overlap with start_init. */ mtx_assert(&Giant, MA_OWNED | MA_NOTRECURSED); mtx_unlock(&Giant); /* * Now hand over this thread to swapper. */ swapper(); /* NOTREACHED*/ } static void print_caddr_t(void *data) { printf("%s", (char *)data); } static void print_version(void *data __unused) { int len; /* Strip a trailing newline from version. */ len = strlen(version); while (len > 0 && version[len - 1] == '\n') len--; printf("%.*s %s\n", len, version, machine); printf("%s\n", compiler_version); } SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright); SYSINIT(trademark, SI_SUB_COPYRIGHT, SI_ORDER_SECOND, print_caddr_t, trademark); SYSINIT(version, SI_SUB_COPYRIGHT, SI_ORDER_THIRD, print_version, NULL); #ifdef WITNESS static char wit_warn[] = "WARNING: WITNESS option enabled, expect reduced performance.\n"; SYSINIT(witwarn, SI_SUB_COPYRIGHT, SI_ORDER_FOURTH, print_caddr_t, wit_warn); SYSINIT(witwarn2, SI_SUB_LAST, SI_ORDER_FOURTH, print_caddr_t, wit_warn); #endif #ifdef DIAGNOSTIC static char diag_warn[] = "WARNING: DIAGNOSTIC option enabled, expect reduced performance.\n"; SYSINIT(diagwarn, SI_SUB_COPYRIGHT, SI_ORDER_FIFTH, print_caddr_t, diag_warn); SYSINIT(diagwarn2, SI_SUB_LAST, SI_ORDER_FIFTH, print_caddr_t, diag_warn); #endif static int null_fetch_syscall_args(struct thread *td __unused) { panic("null_fetch_syscall_args"); } static void null_set_syscall_retval(struct thread *td __unused, int error __unused) { panic("null_set_syscall_retval"); } static void null_set_fork_retval(struct thread *td __unused) { } struct sysentvec null_sysvec = { .sv_size = 0, .sv_table = NULL, .sv_transtrap = NULL, .sv_fixup = NULL, .sv_sendsig = NULL, .sv_sigcode = NULL, .sv_szsigcode = NULL, .sv_name = "null", .sv_coredump = NULL, .sv_imgact_try = NULL, .sv_minsigstksz = 0, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = NULL, .sv_setregs = NULL, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = 0, .sv_set_syscall_retval = null_set_syscall_retval, .sv_fetch_syscall_args = null_fetch_syscall_args, .sv_syscallnames = NULL, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_set_fork_retval = null_set_fork_retval, + .sv_regset_begin = NULL, + .sv_regset_end = NULL, }; /* * The two following SYSINIT's are proc0 specific glue code. I am not * convinced that they can not be safely combined, but their order of * operation has been maintained as the same as the original init_main.c * for right now. */ /* ARGSUSED*/ static void proc0_init(void *dummy __unused) { struct proc *p; struct thread *td; struct ucred *newcred; struct uidinfo tmpuinfo; struct loginclass tmplc = { .lc_name = "", }; vm_paddr_t pageablemem; int i; GIANT_REQUIRED; p = &proc0; td = &thread0; /* * Initialize magic number and osrel. */ p->p_magic = P_MAGIC; p->p_osrel = osreldate; /* * Initialize thread and process structures. */ procinit(); /* set up proc zone */ threadinit(); /* set up UMA zones */ /* * Initialise scheduler resources. * Add scheduler specific parts to proc, thread as needed. */ schedinit(); /* scheduler gets its house in order */ /* * Create process 0 (the swapper). */ LIST_INSERT_HEAD(&allproc, p, p_list); LIST_INSERT_HEAD(PIDHASH(0), p, p_hash); mtx_init(&pgrp0.pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); p->p_pgrp = &pgrp0; LIST_INSERT_HEAD(PGRPHASH(0), &pgrp0, pg_hash); LIST_INIT(&pgrp0.pg_members); LIST_INSERT_HEAD(&pgrp0.pg_members, p, p_pglist); pgrp0.pg_session = &session0; mtx_init(&session0.s_mtx, "session", NULL, MTX_DEF); refcount_init(&session0.s_count, 1); session0.s_leader = p; p->p_sysent = &null_sysvec; p->p_flag = P_SYSTEM | P_INMEM | P_KPROC; p->p_flag2 = 0; p->p_state = PRS_NORMAL; p->p_klist = knlist_alloc(&p->p_mtx); STAILQ_INIT(&p->p_ktr); p->p_nice = NZERO; td->td_tid = THREAD0_TID; tidhash_add(td); TD_SET_STATE(td, TDS_RUNNING); td->td_pri_class = PRI_TIMESHARE; td->td_user_pri = PUSER; td->td_base_user_pri = PUSER; td->td_lend_user_pri = PRI_MAX; td->td_priority = PVM; td->td_base_pri = PVM; td->td_oncpu = curcpu; td->td_flags = TDF_INMEM; td->td_pflags = TDP_KTHREAD; td->td_cpuset = cpuset_thread0(); td->td_domain.dr_policy = td->td_cpuset->cs_domain; prison0_init(); p->p_peers = 0; p->p_leader = p; p->p_reaper = p; p->p_treeflag |= P_TREE_REAPER; LIST_INIT(&p->p_reaplist); strncpy(p->p_comm, "kernel", sizeof (p->p_comm)); strncpy(td->td_name, "swapper", sizeof (td->td_name)); callout_init_mtx(&p->p_itcallout, &p->p_mtx, 0); callout_init_mtx(&p->p_limco, &p->p_mtx, 0); callout_init(&td->td_slpcallout, 1); TAILQ_INIT(&p->p_kqtim_stop); /* Create credentials. */ newcred = crget(); newcred->cr_ngroups = 1; /* group 0 */ /* A hack to prevent uifind from tripping over NULL pointers. */ curthread->td_ucred = newcred; tmpuinfo.ui_uid = 1; newcred->cr_uidinfo = newcred->cr_ruidinfo = &tmpuinfo; newcred->cr_uidinfo = uifind(0); newcred->cr_ruidinfo = uifind(0); newcred->cr_loginclass = &tmplc; newcred->cr_loginclass = loginclass_find("default"); /* End hack. creds get properly set later with thread_cow_get_proc */ curthread->td_ucred = NULL; newcred->cr_prison = &prison0; newcred->cr_users++; /* avoid assertion failure */ proc_set_cred_init(p, newcred); newcred->cr_users--; crfree(newcred); #ifdef AUDIT audit_cred_kproc0(newcred); #endif #ifdef MAC mac_cred_create_swapper(newcred); #endif /* Create sigacts. */ p->p_sigacts = sigacts_alloc(); /* Initialize signal state for process 0. */ siginit(&proc0); /* Create the file descriptor table. */ p->p_pd = pdinit(NULL, false); p->p_fd = fdinit(NULL, false, NULL); p->p_fdtol = NULL; /* Create the limits structures. */ p->p_limit = lim_alloc(); for (i = 0; i < RLIM_NLIMITS; i++) p->p_limit->pl_rlimit[i].rlim_cur = p->p_limit->pl_rlimit[i].rlim_max = RLIM_INFINITY; p->p_limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_NOFILE].rlim_max = maxfiles; p->p_limit->pl_rlimit[RLIMIT_NPROC].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_NPROC].rlim_max = maxproc; p->p_limit->pl_rlimit[RLIMIT_DATA].rlim_cur = dfldsiz; p->p_limit->pl_rlimit[RLIMIT_DATA].rlim_max = maxdsiz; p->p_limit->pl_rlimit[RLIMIT_STACK].rlim_cur = dflssiz; p->p_limit->pl_rlimit[RLIMIT_STACK].rlim_max = maxssiz; /* Cast to avoid overflow on i386/PAE. */ pageablemem = ptoa((vm_paddr_t)vm_free_count()); p->p_limit->pl_rlimit[RLIMIT_RSS].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_RSS].rlim_max = pageablemem; p->p_limit->pl_rlimit[RLIMIT_MEMLOCK].rlim_cur = pageablemem / 3; p->p_limit->pl_rlimit[RLIMIT_MEMLOCK].rlim_max = pageablemem; p->p_cpulimit = RLIM_INFINITY; PROC_LOCK(p); thread_cow_get_proc(td, p); PROC_UNLOCK(p); /* Initialize resource accounting structures. */ racct_create(&p->p_racct); p->p_stats = pstats_alloc(); /* Allocate a prototype map so we have something to fork. */ p->p_vmspace = &vmspace0; refcount_init(&vmspace0.vm_refcnt, 1); pmap_pinit0(vmspace_pmap(&vmspace0)); /* * proc0 is not expected to enter usermode, so there is no special * handling for sv_minuser here, like is done for exec_new_vmspace(). */ vm_map_init(&vmspace0.vm_map, vmspace_pmap(&vmspace0), p->p_sysent->sv_minuser, p->p_sysent->sv_maxuser); /* * Call the init and ctor for the new thread and proc. We wait * to do this until all other structures are fairly sane. */ EVENTHANDLER_DIRECT_INVOKE(process_init, p); EVENTHANDLER_DIRECT_INVOKE(thread_init, td); #ifdef KDTRACE_HOOKS kdtrace_proc_ctor(p); kdtrace_thread_ctor(td); #endif EVENTHANDLER_DIRECT_INVOKE(process_ctor, p); EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td); /* * Charge root for one process. */ (void)chgproccnt(p->p_ucred->cr_ruidinfo, 1, 0); PROC_LOCK(p); racct_add_force(p, RACCT_NPROC, 1); PROC_UNLOCK(p); } SYSINIT(p0init, SI_SUB_INTRINSIC, SI_ORDER_FIRST, proc0_init, NULL); /* ARGSUSED*/ static void proc0_post(void *dummy __unused) { struct proc *p; struct rusage ru; struct thread *td; /* * Now we can look at the time, having had a chance to verify the * time from the filesystem. Pretend that proc0 started now. */ sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } microuptime(&p->p_stats->p_start); PROC_STATLOCK(p); rufetch(p, &ru); /* Clears thread stats */ p->p_rux.rux_runtime = 0; p->p_rux.rux_uticks = 0; p->p_rux.rux_sticks = 0; p->p_rux.rux_iticks = 0; PROC_STATUNLOCK(p); FOREACH_THREAD_IN_PROC(p, td) { td->td_runtime = 0; } PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); PCPU_SET(switchtime, cpu_ticks()); PCPU_SET(switchticks, ticks); } SYSINIT(p0post, SI_SUB_INTRINSIC_POST, SI_ORDER_FIRST, proc0_post, NULL); /* *************************************************************************** **** **** The following SYSINIT's and glue code should be moved to the **** respective files on a per subsystem basis. **** *************************************************************************** */ /* * List of paths to try when searching for "init". */ static char init_path[MAXPATHLEN] = #ifdef INIT_PATH __XSTRING(INIT_PATH); #else "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init"; #endif SYSCTL_STRING(_kern, OID_AUTO, init_path, CTLFLAG_RD, init_path, 0, "Path used to search the init process"); /* * Shutdown timeout of init(8). * Unused within kernel, but used to control init(8), hence do not remove. */ #ifndef INIT_SHUTDOWN_TIMEOUT #define INIT_SHUTDOWN_TIMEOUT 120 #endif static int init_shutdown_timeout = INIT_SHUTDOWN_TIMEOUT; SYSCTL_INT(_kern, OID_AUTO, init_shutdown_timeout, CTLFLAG_RW, &init_shutdown_timeout, 0, "Shutdown timeout of init(8). " "Unused within kernel, but used to control init(8)"); /* * Start the initial user process; try exec'ing each pathname in init_path. * The program is invoked with one argument containing the boot flags. */ static void start_init(void *dummy) { struct image_args args; int error; char *var, *path; char *free_init_path, *tmp_init_path; struct thread *td; struct proc *p; struct vmspace *oldvmspace; TSENTER(); /* Here so we don't overlap with mi_startup. */ td = curthread; p = td->td_proc; vfs_mountroot(); /* Wipe GELI passphrase from the environment. */ kern_unsetenv("kern.geom.eli.passphrase"); /* For Multicons, report which console is primary to both */ if (boothowto & RB_MULTIPLE) { if (boothowto & RB_SERIAL) printf("Dual Console: Serial Primary, Video Secondary\n"); else printf("Dual Console: Video Primary, Serial Secondary\n"); } if ((var = kern_getenv("init_path")) != NULL) { strlcpy(init_path, var, sizeof(init_path)); freeenv(var); } free_init_path = tmp_init_path = strdup(init_path, M_TEMP); while ((path = strsep(&tmp_init_path, ":")) != NULL) { if (bootverbose) printf("start_init: trying %s\n", path); memset(&args, 0, sizeof(args)); error = exec_alloc_args(&args); if (error != 0) panic("%s: Can't allocate space for init arguments %d", __func__, error); error = exec_args_add_fname(&args, path, UIO_SYSSPACE); if (error != 0) panic("%s: Can't add fname %d", __func__, error); error = exec_args_add_arg(&args, path, UIO_SYSSPACE); if (error != 0) panic("%s: Can't add argv[0] %d", __func__, error); if (boothowto & RB_SINGLE) error = exec_args_add_arg(&args, "-s", UIO_SYSSPACE); if (error != 0) panic("%s: Can't add argv[0] %d", __func__, error); /* * Now try to exec the program. If can't for any reason * other than it doesn't exist, complain. * * Otherwise, return via fork_trampoline() all the way * to user mode as init! */ KASSERT((td->td_pflags & TDP_EXECVMSPC) == 0, ("nested execve")); oldvmspace = p->p_vmspace; error = kern_execve(td, &args, NULL, oldvmspace); KASSERT(error != 0, ("kern_execve returned success, not EJUSTRETURN")); if (error == EJUSTRETURN) { exec_cleanup(td, oldvmspace); free(free_init_path, M_TEMP); TSEXIT(); return; } if (error != ENOENT) printf("exec %s: error %d\n", path, error); } free(free_init_path, M_TEMP); printf("init: not found in path %s\n", init_path); panic("no init"); } /* * Like kproc_create(), but runs in its own address space. We do this * early to reserve pid 1. Note special case - do not make it * runnable yet, init execution is started when userspace can be served. */ static void create_init(const void *udata __unused) { struct fork_req fr; struct ucred *newcred, *oldcred; struct thread *td; int error; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC | RFSTOPPED; fr.fr_procp = &initproc; error = fork1(&thread0, &fr); if (error) panic("cannot fork init: %d\n", error); KASSERT(initproc->p_pid == 1, ("create_init: initproc->p_pid != 1")); /* divorce init's credentials from the kernel's */ newcred = crget(); sx_xlock(&proctree_lock); PROC_LOCK(initproc); initproc->p_flag |= P_SYSTEM | P_INMEM; initproc->p_treeflag |= P_TREE_REAPER; oldcred = initproc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC mac_cred_create_init(newcred); #endif #ifdef AUDIT audit_cred_proc1(newcred); #endif proc_set_cred(initproc, newcred); td = FIRST_THREAD_IN_PROC(initproc); crcowfree(td); td->td_realucred = crcowget(initproc->p_ucred); td->td_ucred = td->td_realucred; PROC_UNLOCK(initproc); sx_xunlock(&proctree_lock); crfree(oldcred); cpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL); } SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL); /* * Make it runnable now. */ static void kick_init(const void *udata __unused) { struct thread *td; td = FIRST_THREAD_IN_PROC(initproc); thread_lock(td); TD_SET_CAN_RUN(td); sched_add(td, SRQ_BORING); } SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_MIDDLE, kick_init, NULL); /* * DDB(4). */ #ifdef DDB static void db_show_print_syinit(struct sysinit *sip, bool ddb) { const char *sname, *funcname; c_db_sym_t sym; db_expr_t offset; #define xprint(...) \ if (ddb) \ db_printf(__VA_ARGS__); \ else \ printf(__VA_ARGS__) if (sip == NULL) { xprint("%s: no sysinit * given\n", __func__); return; } sym = db_search_symbol((vm_offset_t)sip, DB_STGY_ANY, &offset); db_symbol_values(sym, &sname, NULL); sym = db_search_symbol((vm_offset_t)sip->func, DB_STGY_PROC, &offset); db_symbol_values(sym, &funcname, NULL); xprint("%s(%p)\n", (sname != NULL) ? sname : "", sip); xprint(" %#08x %#08x\n", sip->subsystem, sip->order); xprint(" %p(%s)(%p)\n", sip->func, (funcname != NULL) ? funcname : "", sip->udata); #undef xprint } DB_SHOW_COMMAND(sysinit, db_show_sysinit) { struct sysinit **sipp; db_printf("SYSINIT vs Name(Ptr)\n"); db_printf(" Subsystem Order\n"); db_printf(" Function(Name)(Arg)\n"); for (sipp = sysinit; sipp < sysinit_end; sipp++) { db_show_print_syinit(*sipp, true); if (db_pager_quit) break; } } #endif /* DDB */ diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 0b3a34550506..fc4cd897fcba 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -1,1475 +1,1619 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1994, Sean Eric Fagan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_FREEBSD32 #include #endif +/* Assert it's safe to unlock a process, e.g. to allocate working memory */ +#define PROC_ASSERT_TRACEREQ(p) MPASS(((p)->p_flag2 & P2_PTRACEREQ) != 0) + /* * Functions implemented using PROC_ACTION(): * * proc_read_regs(proc, regs) * Get the current user-visible register set from the process * and copy it into the regs structure (). * The process is stopped at the time read_regs is called. * * proc_write_regs(proc, regs) * Update the current register set from the passed in regs * structure. Take care to avoid clobbering special CPU * registers or privileged bits in the PSL. * Depending on the architecture this may have fix-up work to do, * especially if the IAR or PCW are modified. * The process is stopped at the time write_regs is called. * * proc_read_fpregs, proc_write_fpregs * deal with the floating point register set, otherwise as above. * * proc_read_dbregs, proc_write_dbregs * deal with the processor debug register set, otherwise as above. * * proc_sstep(proc) * Arrange for the process to trap after executing a single instruction. */ #define PROC_ACTION(action) do { \ int error; \ \ PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); \ if ((td->td_proc->p_flag & P_INMEM) == 0) \ error = EIO; \ else \ error = (action); \ return (error); \ } while (0) int proc_read_regs(struct thread *td, struct reg *regs) { PROC_ACTION(fill_regs(td, regs)); } int proc_write_regs(struct thread *td, struct reg *regs) { PROC_ACTION(set_regs(td, regs)); } int proc_read_dbregs(struct thread *td, struct dbreg *dbregs) { PROC_ACTION(fill_dbregs(td, dbregs)); } int proc_write_dbregs(struct thread *td, struct dbreg *dbregs) { PROC_ACTION(set_dbregs(td, dbregs)); } /* * Ptrace doesn't support fpregs at all, and there are no security holes * or translations for fpregs, so we can just copy them. */ int proc_read_fpregs(struct thread *td, struct fpreg *fpregs) { PROC_ACTION(fill_fpregs(td, fpregs)); } int proc_write_fpregs(struct thread *td, struct fpreg *fpregs) { PROC_ACTION(set_fpregs(td, fpregs)); } +static struct regset * +proc_find_regset(struct thread *td, int note) +{ + struct regset **regsetp, **regset_end, *regset; + struct sysentvec *sv; + + sv = td->td_proc->p_sysent; + regsetp = sv->sv_regset_begin; + if (regsetp == NULL) + return (NULL); + regset_end = sv->sv_regset_end; + MPASS(regset_end != NULL); + for (; regsetp < regset_end; regsetp++) { + regset = *regsetp; + if (regset->note != note) + continue; + + return (regset); + } + + return (NULL); +} + +static int +proc_read_regset(struct thread *td, int note, struct iovec *iov) +{ + struct regset *regset; + struct proc *p; + void *buf; + size_t size; + int error; + + regset = proc_find_regset(td, note); + if (regset == NULL) + return (EINVAL); + + if (iov->iov_base == NULL) { + iov->iov_len = regset->size; + if (iov->iov_len == 0) + return (EINVAL); + + return (0); + } + + /* The length is wrong, return an error */ + if (iov->iov_len != regset->size) + return (EINVAL); + + if (regset->get == NULL) + return (EINVAL); + + error = 0; + size = regset->size; + p = td->td_proc; + + /* Drop the proc lock while allocating the temp buffer */ + PROC_ASSERT_TRACEREQ(p); + PROC_UNLOCK(p); + buf = malloc(size, M_TEMP, M_WAITOK); + PROC_LOCK(p); + + if (!regset->get(regset, td, buf, &size)) { + error = EINVAL; + } else { + KASSERT(size == regset->size, + ("%s: Getter function changed the size", __func__)); + + iov->iov_len = size; + PROC_UNLOCK(p); + error = copyout(buf, iov->iov_base, size); + PROC_LOCK(p); + } + + free(buf, M_TEMP); + + return (error); +} + +static int +proc_write_regset(struct thread *td, int note, struct iovec *iov) +{ + struct regset *regset; + struct proc *p; + void *buf; + size_t size; + int error; + + regset = proc_find_regset(td, note); + if (regset == NULL) + return (EINVAL); + + /* The length is wrong, return an error */ + if (iov->iov_len != regset->size) + return (EINVAL); + + if (regset->set == NULL) + return (EINVAL); + + size = regset->size; + p = td->td_proc; + + /* Drop the proc lock while allocating the temp buffer */ + PROC_ASSERT_TRACEREQ(p); + PROC_UNLOCK(p); + buf = malloc(size, M_TEMP, M_WAITOK); + error = copyin(iov->iov_base, buf, size); + PROC_LOCK(p); + + if (error == 0) { + if (!regset->set(regset, td, buf, size)) { + error = EINVAL; + } + } + + free(buf, M_TEMP); + + return (error); +} + #ifdef COMPAT_FREEBSD32 /* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ int proc_read_regs32(struct thread *td, struct reg32 *regs32) { PROC_ACTION(fill_regs32(td, regs32)); } int proc_write_regs32(struct thread *td, struct reg32 *regs32) { PROC_ACTION(set_regs32(td, regs32)); } int proc_read_dbregs32(struct thread *td, struct dbreg32 *dbregs32) { PROC_ACTION(fill_dbregs32(td, dbregs32)); } int proc_write_dbregs32(struct thread *td, struct dbreg32 *dbregs32) { PROC_ACTION(set_dbregs32(td, dbregs32)); } int proc_read_fpregs32(struct thread *td, struct fpreg32 *fpregs32) { PROC_ACTION(fill_fpregs32(td, fpregs32)); } int proc_write_fpregs32(struct thread *td, struct fpreg32 *fpregs32) { PROC_ACTION(set_fpregs32(td, fpregs32)); } #endif int proc_sstep(struct thread *td) { PROC_ACTION(ptrace_single_step(td)); } int proc_rwmem(struct proc *p, struct uio *uio) { vm_map_t map; vm_offset_t pageno; /* page number */ vm_prot_t reqprot; int error, fault_flags, page_offset, writing; /* * Assert that someone has locked this vmspace. (Should be * curthread but we can't assert that.) This keeps the process * from exiting out from under us until this operation completes. */ PROC_ASSERT_HELD(p); PROC_LOCK_ASSERT(p, MA_NOTOWNED); /* * The map we want... */ map = &p->p_vmspace->vm_map; /* * If we are writing, then we request vm_fault() to create a private * copy of each page. Since these copies will not be writeable by the * process, we must explicity request that they be dirtied. */ writing = uio->uio_rw == UIO_WRITE; reqprot = writing ? VM_PROT_COPY | VM_PROT_READ : VM_PROT_READ; fault_flags = writing ? VM_FAULT_DIRTY : VM_FAULT_NORMAL; /* * Only map in one page at a time. We don't have to, but it * makes things easier. This way is trivial - right? */ do { vm_offset_t uva; u_int len; vm_page_t m; uva = (vm_offset_t)uio->uio_offset; /* * Get the page number of this segment. */ pageno = trunc_page(uva); page_offset = uva - pageno; /* * How many bytes to copy */ len = min(PAGE_SIZE - page_offset, uio->uio_resid); /* * Fault and hold the page on behalf of the process. */ error = vm_fault(map, pageno, reqprot, fault_flags, &m); if (error != KERN_SUCCESS) { if (error == KERN_RESOURCE_SHORTAGE) error = ENOMEM; else error = EFAULT; break; } /* * Now do the i/o move. */ error = uiomove_fromphys(&m, page_offset, len, uio); /* Make the I-cache coherent for breakpoints. */ if (writing && error == 0) { vm_map_lock_read(map); if (vm_map_check_protection(map, pageno, pageno + PAGE_SIZE, VM_PROT_EXECUTE)) vm_sync_icache(map, uva, len); vm_map_unlock_read(map); } /* * Release the page. */ vm_page_unwire(m, PQ_ACTIVE); } while (error == 0 && uio->uio_resid > 0); return (error); } static ssize_t proc_iop(struct thread *td, struct proc *p, vm_offset_t va, void *buf, size_t len, enum uio_rw rw) { struct iovec iov; struct uio uio; ssize_t slen; MPASS(len < SSIZE_MAX); slen = (ssize_t)len; iov.iov_base = (caddr_t)buf; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = va; uio.uio_resid = slen; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = rw; uio.uio_td = td; proc_rwmem(p, &uio); if (uio.uio_resid == slen) return (-1); return (slen - uio.uio_resid); } ssize_t proc_readmem(struct thread *td, struct proc *p, vm_offset_t va, void *buf, size_t len) { return (proc_iop(td, p, va, buf, len, UIO_READ)); } ssize_t proc_writemem(struct thread *td, struct proc *p, vm_offset_t va, void *buf, size_t len) { return (proc_iop(td, p, va, buf, len, UIO_WRITE)); } static int ptrace_vm_entry(struct thread *td, struct proc *p, struct ptrace_vm_entry *pve) { struct vattr vattr; vm_map_t map; vm_map_entry_t entry; vm_object_t obj, tobj, lobj; struct vmspace *vm; struct vnode *vp; char *freepath, *fullpath; u_int pathlen; int error, index; error = 0; obj = NULL; vm = vmspace_acquire_ref(p); map = &vm->vm_map; vm_map_lock_read(map); do { KASSERT((map->header.eflags & MAP_ENTRY_IS_SUB_MAP) == 0, ("Submap in map header")); index = 0; VM_MAP_ENTRY_FOREACH(entry, map) { if (index >= pve->pve_entry && (entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) break; index++; } if (index < pve->pve_entry) { error = EINVAL; break; } if (entry == &map->header) { error = ENOENT; break; } /* We got an entry. */ pve->pve_entry = index + 1; pve->pve_timestamp = map->timestamp; pve->pve_start = entry->start; pve->pve_end = entry->end - 1; pve->pve_offset = entry->offset; pve->pve_prot = entry->protection; /* Backing object's path needed? */ if (pve->pve_pathlen == 0) break; pathlen = pve->pve_pathlen; pve->pve_pathlen = 0; obj = entry->object.vm_object; if (obj != NULL) VM_OBJECT_RLOCK(obj); } while (0); vm_map_unlock_read(map); pve->pve_fsid = VNOVAL; pve->pve_fileid = VNOVAL; if (error == 0 && obj != NULL) { lobj = obj; for (tobj = obj; tobj != NULL; tobj = tobj->backing_object) { if (tobj != obj) VM_OBJECT_RLOCK(tobj); if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); lobj = tobj; pve->pve_offset += tobj->backing_object_offset; } vp = vm_object_vnode(lobj); if (vp != NULL) vref(vp); if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); VM_OBJECT_RUNLOCK(obj); if (vp != NULL) { freepath = NULL; fullpath = NULL; vn_fullpath(vp, &fullpath, &freepath); vn_lock(vp, LK_SHARED | LK_RETRY); if (VOP_GETATTR(vp, &vattr, td->td_ucred) == 0) { pve->pve_fileid = vattr.va_fileid; pve->pve_fsid = vattr.va_fsid; } vput(vp); if (fullpath != NULL) { pve->pve_pathlen = strlen(fullpath) + 1; if (pve->pve_pathlen <= pathlen) { error = copyout(fullpath, pve->pve_path, pve->pve_pathlen); } else error = ENAMETOOLONG; } if (freepath != NULL) free(freepath, M_TEMP); } } vmspace_free(vm); if (error == 0) CTR3(KTR_PTRACE, "PT_VM_ENTRY: pid %d, entry %d, start %p", p->p_pid, pve->pve_entry, pve->pve_start); return (error); } /* * Process debugging system call. */ #ifndef _SYS_SYSPROTO_H_ struct ptrace_args { int req; pid_t pid; caddr_t addr; int data; }; #endif int sys_ptrace(struct thread *td, struct ptrace_args *uap) { /* * XXX this obfuscation is to reduce stack usage, but the register * structs may be too large to put on the stack anyway. */ union { struct ptrace_io_desc piod; struct ptrace_lwpinfo pl; struct ptrace_vm_entry pve; struct ptrace_coredump pc; struct dbreg dbreg; struct fpreg fpreg; struct reg reg; + struct iovec vec; char args[sizeof(td->td_sa.args)]; struct ptrace_sc_ret psr; int ptevents; } r; void *addr; int error; if (!allow_ptrace) return (ENOSYS); error = 0; AUDIT_ARG_PID(uap->pid); AUDIT_ARG_CMD(uap->req); AUDIT_ARG_VALUE(uap->data); addr = &r; switch (uap->req) { case PT_GET_EVENT_MASK: case PT_LWPINFO: case PT_GET_SC_ARGS: case PT_GET_SC_RET: break; case PT_GETREGS: bzero(&r.reg, sizeof(r.reg)); break; case PT_GETFPREGS: bzero(&r.fpreg, sizeof(r.fpreg)); break; case PT_GETDBREGS: bzero(&r.dbreg, sizeof(r.dbreg)); break; + case PT_SETREGSET: + error = copyin(uap->addr, &r.vec, sizeof(r.vec)); + break; + case PT_GETREGSET: + error = copyin(uap->addr, &r.vec, sizeof(r.vec)); + break; case PT_SETREGS: error = copyin(uap->addr, &r.reg, sizeof(r.reg)); break; case PT_SETFPREGS: error = copyin(uap->addr, &r.fpreg, sizeof(r.fpreg)); break; case PT_SETDBREGS: error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg)); break; case PT_SET_EVENT_MASK: if (uap->data != sizeof(r.ptevents)) error = EINVAL; else error = copyin(uap->addr, &r.ptevents, uap->data); break; case PT_IO: error = copyin(uap->addr, &r.piod, sizeof(r.piod)); break; case PT_VM_ENTRY: error = copyin(uap->addr, &r.pve, sizeof(r.pve)); break; case PT_COREDUMP: if (uap->data != sizeof(r.pc)) error = EINVAL; else error = copyin(uap->addr, &r.pc, uap->data); break; default: addr = uap->addr; break; } if (error) return (error); error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); if (error) return (error); switch (uap->req) { case PT_VM_ENTRY: error = copyout(&r.pve, uap->addr, sizeof(r.pve)); break; case PT_IO: error = copyout(&r.piod, uap->addr, sizeof(r.piod)); break; case PT_GETREGS: error = copyout(&r.reg, uap->addr, sizeof(r.reg)); break; case PT_GETFPREGS: error = copyout(&r.fpreg, uap->addr, sizeof(r.fpreg)); break; case PT_GETDBREGS: error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg)); break; + case PT_GETREGSET: + error = copyout(&r.vec, uap->addr, sizeof(r.vec)); + break; case PT_GET_EVENT_MASK: /* NB: The size in uap->data is validated in kern_ptrace(). */ error = copyout(&r.ptevents, uap->addr, uap->data); break; case PT_LWPINFO: /* NB: The size in uap->data is validated in kern_ptrace(). */ error = copyout(&r.pl, uap->addr, uap->data); break; case PT_GET_SC_ARGS: error = copyout(r.args, uap->addr, MIN(uap->data, sizeof(r.args))); break; case PT_GET_SC_RET: error = copyout(&r.psr, uap->addr, MIN(uap->data, sizeof(r.psr))); break; } return (error); } #ifdef COMPAT_FREEBSD32 /* * PROC_READ(regs, td2, addr); * becomes either: * proc_read_regs(td2, addr); * or * proc_read_regs32(td2, addr); * .. except this is done at runtime. There is an additional * complication in that PROC_WRITE disallows 32 bit consumers * from writing to 64 bit address space targets. */ #define PROC_READ(w, t, a) wrap32 ? \ proc_read_ ## w ## 32(t, a) : \ proc_read_ ## w (t, a) #define PROC_WRITE(w, t, a) wrap32 ? \ (safe ? proc_write_ ## w ## 32(t, a) : EINVAL ) : \ proc_write_ ## w (t, a) #else #define PROC_READ(w, t, a) proc_read_ ## w (t, a) #define PROC_WRITE(w, t, a) proc_write_ ## w (t, a) #endif void proc_set_traced(struct proc *p, bool stop) { sx_assert(&proctree_lock, SX_XLOCKED); PROC_LOCK_ASSERT(p, MA_OWNED); p->p_flag |= P_TRACED; if (stop) p->p_flag2 |= P2_PTRACE_FSTP; p->p_ptevents = PTRACE_DEFAULT; } void ptrace_unsuspend(struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK(p); p->p_flag &= ~(P_STOPPED_TRACE | P_STOPPED_SIG | P_WAITED); thread_unsuspend(p); PROC_SUNLOCK(p); itimer_proc_continue(p); kqtimer_proc_continue(p); } static int proc_can_ptrace(struct thread *td, struct proc *p) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); if ((p->p_flag & P_WEXIT) != 0) return (ESRCH); if ((error = p_cansee(td, p)) != 0) return (error); if ((error = p_candebug(td, p)) != 0) return (error); /* not being traced... */ if ((p->p_flag & P_TRACED) == 0) return (EPERM); /* not being traced by YOU */ if (p->p_pptr != td->td_proc) return (EBUSY); /* not currently stopped */ if ((p->p_flag & P_STOPPED_TRACE) == 0 || p->p_suspcount != p->p_numthreads || (p->p_flag & P_WAITED) == 0) return (EBUSY); return (0); } static struct thread * ptrace_sel_coredump_thread(struct proc *p) { struct thread *td2; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS((p->p_flag & P_STOPPED_TRACE) != 0); FOREACH_THREAD_IN_PROC(p, td2) { if ((td2->td_dbgflags & TDB_SSWITCH) != 0) return (td2); } return (NULL); } int kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) { struct iovec iov; struct uio uio; struct proc *curp, *p, *pp; struct thread *td2 = NULL, *td3; struct ptrace_io_desc *piod = NULL; struct ptrace_lwpinfo *pl; struct ptrace_sc_ret *psr; struct file *fp; struct ptrace_coredump *pc; struct thr_coredump_req *tcq; int error, num, tmp; lwpid_t tid = 0, *buf; #ifdef COMPAT_FREEBSD32 int wrap32 = 0, safe = 0; #endif bool proctree_locked, p2_req_set; curp = td->td_proc; proctree_locked = false; p2_req_set = false; /* Lock proctree before locking the process. */ switch (req) { case PT_TRACE_ME: case PT_ATTACH: case PT_STEP: case PT_CONTINUE: case PT_TO_SCE: case PT_TO_SCX: case PT_SYSCALL: case PT_FOLLOW_FORK: case PT_LWP_EVENTS: case PT_GET_EVENT_MASK: case PT_SET_EVENT_MASK: case PT_DETACH: case PT_GET_SC_ARGS: sx_xlock(&proctree_lock); proctree_locked = true; break; default: break; } if (req == PT_TRACE_ME) { p = td->td_proc; PROC_LOCK(p); } else { if (pid <= PID_MAX) { if ((p = pfind(pid)) == NULL) { if (proctree_locked) sx_xunlock(&proctree_lock); return (ESRCH); } } else { td2 = tdfind(pid, -1); if (td2 == NULL) { if (proctree_locked) sx_xunlock(&proctree_lock); return (ESRCH); } p = td2->td_proc; tid = pid; pid = p->p_pid; } } AUDIT_ARG_PROCESS(p); if ((p->p_flag & P_WEXIT) != 0) { error = ESRCH; goto fail; } if ((error = p_cansee(td, p)) != 0) goto fail; if ((error = p_candebug(td, p)) != 0) goto fail; /* * System processes can't be debugged. */ if ((p->p_flag & P_SYSTEM) != 0) { error = EINVAL; goto fail; } if (tid == 0) { if ((p->p_flag & P_STOPPED_TRACE) != 0) { KASSERT(p->p_xthread != NULL, ("NULL p_xthread")); td2 = p->p_xthread; } else { td2 = FIRST_THREAD_IN_PROC(p); } tid = td2->td_tid; } #ifdef COMPAT_FREEBSD32 /* * Test if we're a 32 bit client and what the target is. * Set the wrap controls accordingly. */ if (SV_CURPROC_FLAG(SV_ILP32)) { if (SV_PROC_FLAG(td2->td_proc, SV_ILP32)) safe = 1; wrap32 = 1; } #endif /* * Permissions check */ switch (req) { case PT_TRACE_ME: /* * Always legal, when there is a parent process which * could trace us. Otherwise, reject. */ if ((p->p_flag & P_TRACED) != 0) { error = EBUSY; goto fail; } if (p->p_pptr == initproc) { error = EPERM; goto fail; } break; case PT_ATTACH: /* Self */ if (p == td->td_proc) { error = EINVAL; goto fail; } /* Already traced */ if (p->p_flag & P_TRACED) { error = EBUSY; goto fail; } /* Can't trace an ancestor if you're being traced. */ if (curp->p_flag & P_TRACED) { for (pp = curp->p_pptr; pp != NULL; pp = pp->p_pptr) { if (pp == p) { error = EINVAL; goto fail; } } } /* OK */ break; case PT_CLEARSTEP: /* Allow thread to clear single step for itself */ if (td->td_tid == tid) break; /* FALLTHROUGH */ default: /* * Check for ptrace eligibility before waiting for * holds to drain. */ error = proc_can_ptrace(td, p); if (error != 0) goto fail; /* * Block parallel ptrace requests. Most important, do * not allow other thread in debugger to continue the * debuggee until coredump finished. */ while ((p->p_flag2 & P2_PTRACEREQ) != 0) { if (proctree_locked) sx_xunlock(&proctree_lock); error = msleep(&p->p_flag2, &p->p_mtx, PPAUSE | PCATCH | (proctree_locked ? PDROP : 0), "pptrace", 0); if (proctree_locked) { sx_xlock(&proctree_lock); PROC_LOCK(p); } if (error == 0 && td2->td_proc != p) error = ESRCH; if (error == 0) error = proc_can_ptrace(td, p); if (error != 0) goto fail; } /* Ok */ break; } /* * Keep this process around and request parallel ptrace() * request to wait until we finish this request. */ MPASS((p->p_flag2 & P2_PTRACEREQ) == 0); p->p_flag2 |= P2_PTRACEREQ; p2_req_set = true; _PHOLD(p); /* * Actually do the requests */ td->td_retval[0] = 0; switch (req) { case PT_TRACE_ME: /* set my trace flag and "owner" so it can read/write me */ proc_set_traced(p, false); if (p->p_flag & P_PPWAIT) p->p_flag |= P_PPTRACE; CTR1(KTR_PTRACE, "PT_TRACE_ME: pid %d", p->p_pid); break; case PT_ATTACH: /* security check done above */ /* * It would be nice if the tracing relationship was separate * from the parent relationship but that would require * another set of links in the proc struct or for "wait" * to scan the entire proc table. To make life easier, * we just re-parent the process we're trying to trace. * The old parent is remembered so we can put things back * on a "detach". */ proc_set_traced(p, true); proc_reparent(p, td->td_proc, false); CTR2(KTR_PTRACE, "PT_ATTACH: pid %d, oppid %d", p->p_pid, p->p_oppid); sx_xunlock(&proctree_lock); proctree_locked = false; MPASS(p->p_xthread == NULL); MPASS((p->p_flag & P_STOPPED_TRACE) == 0); /* * If already stopped due to a stop signal, clear the * existing stop before triggering a traced SIGSTOP. */ if ((p->p_flag & P_STOPPED_SIG) != 0) { PROC_SLOCK(p); p->p_flag &= ~(P_STOPPED_SIG | P_WAITED); thread_unsuspend(p); PROC_SUNLOCK(p); } kern_psignal(p, SIGSTOP); break; case PT_CLEARSTEP: CTR2(KTR_PTRACE, "PT_CLEARSTEP: tid %d (pid %d)", td2->td_tid, p->p_pid); error = ptrace_clear_single_step(td2); break; case PT_SETSTEP: CTR2(KTR_PTRACE, "PT_SETSTEP: tid %d (pid %d)", td2->td_tid, p->p_pid); error = ptrace_single_step(td2); break; case PT_SUSPEND: CTR2(KTR_PTRACE, "PT_SUSPEND: tid %d (pid %d)", td2->td_tid, p->p_pid); td2->td_dbgflags |= TDB_SUSPEND; thread_lock(td2); td2->td_flags |= TDF_NEEDSUSPCHK; thread_unlock(td2); break; case PT_RESUME: CTR2(KTR_PTRACE, "PT_RESUME: tid %d (pid %d)", td2->td_tid, p->p_pid); td2->td_dbgflags &= ~TDB_SUSPEND; break; case PT_FOLLOW_FORK: CTR3(KTR_PTRACE, "PT_FOLLOW_FORK: pid %d %s -> %s", p->p_pid, p->p_ptevents & PTRACE_FORK ? "enabled" : "disabled", data ? "enabled" : "disabled"); if (data) p->p_ptevents |= PTRACE_FORK; else p->p_ptevents &= ~PTRACE_FORK; break; case PT_LWP_EVENTS: CTR3(KTR_PTRACE, "PT_LWP_EVENTS: pid %d %s -> %s", p->p_pid, p->p_ptevents & PTRACE_LWP ? "enabled" : "disabled", data ? "enabled" : "disabled"); if (data) p->p_ptevents |= PTRACE_LWP; else p->p_ptevents &= ~PTRACE_LWP; break; case PT_GET_EVENT_MASK: if (data != sizeof(p->p_ptevents)) { error = EINVAL; break; } CTR2(KTR_PTRACE, "PT_GET_EVENT_MASK: pid %d mask %#x", p->p_pid, p->p_ptevents); *(int *)addr = p->p_ptevents; break; case PT_SET_EVENT_MASK: if (data != sizeof(p->p_ptevents)) { error = EINVAL; break; } tmp = *(int *)addr; if ((tmp & ~(PTRACE_EXEC | PTRACE_SCE | PTRACE_SCX | PTRACE_FORK | PTRACE_LWP | PTRACE_VFORK)) != 0) { error = EINVAL; break; } CTR3(KTR_PTRACE, "PT_SET_EVENT_MASK: pid %d mask %#x -> %#x", p->p_pid, p->p_ptevents, tmp); p->p_ptevents = tmp; break; case PT_GET_SC_ARGS: CTR1(KTR_PTRACE, "PT_GET_SC_ARGS: pid %d", p->p_pid); if ((td2->td_dbgflags & (TDB_SCE | TDB_SCX)) == 0 #ifdef COMPAT_FREEBSD32 || (wrap32 && !safe) #endif ) { error = EINVAL; break; } bzero(addr, sizeof(td2->td_sa.args)); /* See the explanation in linux_ptrace_get_syscall_info(). */ bcopy(td2->td_sa.args, addr, SV_PROC_ABI(td->td_proc) == SV_ABI_LINUX ? sizeof(td2->td_sa.args) : td2->td_sa.callp->sy_narg * sizeof(register_t)); break; case PT_GET_SC_RET: if ((td2->td_dbgflags & (TDB_SCX)) == 0 #ifdef COMPAT_FREEBSD32 || (wrap32 && !safe) #endif ) { error = EINVAL; break; } psr = addr; bzero(psr, sizeof(*psr)); psr->sr_error = td2->td_errno; if (psr->sr_error == 0) { psr->sr_retval[0] = td2->td_retval[0]; psr->sr_retval[1] = td2->td_retval[1]; } CTR4(KTR_PTRACE, "PT_GET_SC_RET: pid %d error %d retval %#lx,%#lx", p->p_pid, psr->sr_error, psr->sr_retval[0], psr->sr_retval[1]); break; case PT_STEP: case PT_CONTINUE: case PT_TO_SCE: case PT_TO_SCX: case PT_SYSCALL: case PT_DETACH: /* Zero means do not send any signal */ if (data < 0 || data > _SIG_MAXSIG) { error = EINVAL; break; } switch (req) { case PT_STEP: CTR3(KTR_PTRACE, "PT_STEP: tid %d (pid %d), sig = %d", td2->td_tid, p->p_pid, data); error = ptrace_single_step(td2); if (error) goto out; break; case PT_CONTINUE: case PT_TO_SCE: case PT_TO_SCX: case PT_SYSCALL: if (addr != (void *)1) { error = ptrace_set_pc(td2, (u_long)(uintfptr_t)addr); if (error) goto out; } switch (req) { case PT_TO_SCE: p->p_ptevents |= PTRACE_SCE; CTR4(KTR_PTRACE, "PT_TO_SCE: pid %d, events = %#x, PC = %#lx, sig = %d", p->p_pid, p->p_ptevents, (u_long)(uintfptr_t)addr, data); break; case PT_TO_SCX: p->p_ptevents |= PTRACE_SCX; CTR4(KTR_PTRACE, "PT_TO_SCX: pid %d, events = %#x, PC = %#lx, sig = %d", p->p_pid, p->p_ptevents, (u_long)(uintfptr_t)addr, data); break; case PT_SYSCALL: p->p_ptevents |= PTRACE_SYSCALL; CTR4(KTR_PTRACE, "PT_SYSCALL: pid %d, events = %#x, PC = %#lx, sig = %d", p->p_pid, p->p_ptevents, (u_long)(uintfptr_t)addr, data); break; case PT_CONTINUE: CTR3(KTR_PTRACE, "PT_CONTINUE: pid %d, PC = %#lx, sig = %d", p->p_pid, (u_long)(uintfptr_t)addr, data); break; } break; case PT_DETACH: /* * Clear P_TRACED before reparenting * a detached process back to its original * parent. Otherwise the debugee will be set * as an orphan of the debugger. */ p->p_flag &= ~(P_TRACED | P_WAITED); /* * Reset the process parent. */ if (p->p_oppid != p->p_pptr->p_pid) { PROC_LOCK(p->p_pptr); sigqueue_take(p->p_ksi); PROC_UNLOCK(p->p_pptr); pp = proc_realparent(p); proc_reparent(p, pp, false); if (pp == initproc) p->p_sigparent = SIGCHLD; CTR3(KTR_PTRACE, "PT_DETACH: pid %d reparented to pid %d, sig %d", p->p_pid, pp->p_pid, data); } else { CTR2(KTR_PTRACE, "PT_DETACH: pid %d, sig %d", p->p_pid, data); } p->p_ptevents = 0; FOREACH_THREAD_IN_PROC(p, td3) { if ((td3->td_dbgflags & TDB_FSTP) != 0) { sigqueue_delete(&td3->td_sigqueue, SIGSTOP); } td3->td_dbgflags &= ~(TDB_XSIG | TDB_FSTP | TDB_SUSPEND); } if ((p->p_flag2 & P2_PTRACE_FSTP) != 0) { sigqueue_delete(&p->p_sigqueue, SIGSTOP); p->p_flag2 &= ~P2_PTRACE_FSTP; } /* should we send SIGCHLD? */ /* childproc_continued(p); */ break; } sx_xunlock(&proctree_lock); proctree_locked = false; sendsig: MPASS(!proctree_locked); /* * Clear the pending event for the thread that just * reported its event (p_xthread). This may not be * the thread passed to PT_CONTINUE, PT_STEP, etc. if * the debugger is resuming a different thread. * * Deliver any pending signal via the reporting thread. */ MPASS(p->p_xthread != NULL); p->p_xthread->td_dbgflags &= ~TDB_XSIG; p->p_xthread->td_xsig = data; p->p_xthread = NULL; p->p_xsig = data; /* * P_WKILLED is insurance that a PT_KILL/SIGKILL * always works immediately, even if another thread is * unsuspended first and attempts to handle a * different signal or if the POSIX.1b style signal * queue cannot accommodate any new signals. */ if (data == SIGKILL) proc_wkilled(p); /* * Unsuspend all threads. To leave a thread * suspended, use PT_SUSPEND to suspend it before * continuing the process. */ ptrace_unsuspend(p); break; case PT_WRITE_I: case PT_WRITE_D: td2->td_dbgflags |= TDB_USERWR; PROC_UNLOCK(p); error = 0; if (proc_writemem(td, p, (off_t)(uintptr_t)addr, &data, sizeof(int)) != sizeof(int)) error = ENOMEM; else CTR3(KTR_PTRACE, "PT_WRITE: pid %d: %p <= %#x", p->p_pid, addr, data); PROC_LOCK(p); break; case PT_READ_I: case PT_READ_D: PROC_UNLOCK(p); error = tmp = 0; if (proc_readmem(td, p, (off_t)(uintptr_t)addr, &tmp, sizeof(int)) != sizeof(int)) error = ENOMEM; else CTR3(KTR_PTRACE, "PT_READ: pid %d: %p >= %#x", p->p_pid, addr, tmp); td->td_retval[0] = tmp; PROC_LOCK(p); break; case PT_IO: piod = addr; iov.iov_base = piod->piod_addr; iov.iov_len = piod->piod_len; uio.uio_offset = (off_t)(uintptr_t)piod->piod_offs; uio.uio_resid = piod->piod_len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_USERSPACE; uio.uio_td = td; switch (piod->piod_op) { case PIOD_READ_D: case PIOD_READ_I: CTR3(KTR_PTRACE, "PT_IO: pid %d: READ (%p, %#x)", p->p_pid, (uintptr_t)uio.uio_offset, uio.uio_resid); uio.uio_rw = UIO_READ; break; case PIOD_WRITE_D: case PIOD_WRITE_I: CTR3(KTR_PTRACE, "PT_IO: pid %d: WRITE (%p, %#x)", p->p_pid, (uintptr_t)uio.uio_offset, uio.uio_resid); td2->td_dbgflags |= TDB_USERWR; uio.uio_rw = UIO_WRITE; break; default: error = EINVAL; goto out; } PROC_UNLOCK(p); error = proc_rwmem(p, &uio); piod->piod_len -= uio.uio_resid; PROC_LOCK(p); break; case PT_KILL: CTR1(KTR_PTRACE, "PT_KILL: pid %d", p->p_pid); data = SIGKILL; goto sendsig; /* in PT_CONTINUE above */ case PT_SETREGS: CTR2(KTR_PTRACE, "PT_SETREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); td2->td_dbgflags |= TDB_USERWR; error = PROC_WRITE(regs, td2, addr); break; case PT_GETREGS: CTR2(KTR_PTRACE, "PT_GETREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); error = PROC_READ(regs, td2, addr); break; case PT_SETFPREGS: CTR2(KTR_PTRACE, "PT_SETFPREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); td2->td_dbgflags |= TDB_USERWR; error = PROC_WRITE(fpregs, td2, addr); break; case PT_GETFPREGS: CTR2(KTR_PTRACE, "PT_GETFPREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); error = PROC_READ(fpregs, td2, addr); break; case PT_SETDBREGS: CTR2(KTR_PTRACE, "PT_SETDBREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); td2->td_dbgflags |= TDB_USERWR; error = PROC_WRITE(dbregs, td2, addr); break; case PT_GETDBREGS: CTR2(KTR_PTRACE, "PT_GETDBREGS: tid %d (pid %d)", td2->td_tid, p->p_pid); error = PROC_READ(dbregs, td2, addr); break; + case PT_SETREGSET: + CTR2(KTR_PTRACE, "PT_SETREGSET: tid %d (pid %d)", td2->td_tid, + p->p_pid); + error = proc_write_regset(td2, data, addr); + break; + + case PT_GETREGSET: + CTR2(KTR_PTRACE, "PT_GETREGSET: tid %d (pid %d)", td2->td_tid, + p->p_pid); + error = proc_read_regset(td2, data, addr); + break; + case PT_LWPINFO: if (data <= 0 || data > sizeof(*pl)) { error = EINVAL; break; } pl = addr; bzero(pl, sizeof(*pl)); pl->pl_lwpid = td2->td_tid; pl->pl_event = PL_EVENT_NONE; pl->pl_flags = 0; if (td2->td_dbgflags & TDB_XSIG) { pl->pl_event = PL_EVENT_SIGNAL; if (td2->td_si.si_signo != 0 && data >= offsetof(struct ptrace_lwpinfo, pl_siginfo) + sizeof(pl->pl_siginfo)){ pl->pl_flags |= PL_FLAG_SI; pl->pl_siginfo = td2->td_si; } } if (td2->td_dbgflags & TDB_SCE) pl->pl_flags |= PL_FLAG_SCE; else if (td2->td_dbgflags & TDB_SCX) pl->pl_flags |= PL_FLAG_SCX; if (td2->td_dbgflags & TDB_EXEC) pl->pl_flags |= PL_FLAG_EXEC; if (td2->td_dbgflags & TDB_FORK) { pl->pl_flags |= PL_FLAG_FORKED; pl->pl_child_pid = td2->td_dbg_forked; if (td2->td_dbgflags & TDB_VFORK) pl->pl_flags |= PL_FLAG_VFORKED; } else if ((td2->td_dbgflags & (TDB_SCX | TDB_VFORK)) == TDB_VFORK) pl->pl_flags |= PL_FLAG_VFORK_DONE; if (td2->td_dbgflags & TDB_CHILD) pl->pl_flags |= PL_FLAG_CHILD; if (td2->td_dbgflags & TDB_BORN) pl->pl_flags |= PL_FLAG_BORN; if (td2->td_dbgflags & TDB_EXIT) pl->pl_flags |= PL_FLAG_EXITED; pl->pl_sigmask = td2->td_sigmask; pl->pl_siglist = td2->td_siglist; strcpy(pl->pl_tdname, td2->td_name); if ((td2->td_dbgflags & (TDB_SCE | TDB_SCX)) != 0) { pl->pl_syscall_code = td2->td_sa.code; pl->pl_syscall_narg = td2->td_sa.callp->sy_narg; } else { pl->pl_syscall_code = 0; pl->pl_syscall_narg = 0; } CTR6(KTR_PTRACE, "PT_LWPINFO: tid %d (pid %d) event %d flags %#x child pid %d syscall %d", td2->td_tid, p->p_pid, pl->pl_event, pl->pl_flags, pl->pl_child_pid, pl->pl_syscall_code); break; case PT_GETNUMLWPS: CTR2(KTR_PTRACE, "PT_GETNUMLWPS: pid %d: %d threads", p->p_pid, p->p_numthreads); td->td_retval[0] = p->p_numthreads; break; case PT_GETLWPLIST: CTR3(KTR_PTRACE, "PT_GETLWPLIST: pid %d: data %d, actual %d", p->p_pid, data, p->p_numthreads); if (data <= 0) { error = EINVAL; break; } num = imin(p->p_numthreads, data); PROC_UNLOCK(p); buf = malloc(num * sizeof(lwpid_t), M_TEMP, M_WAITOK); tmp = 0; PROC_LOCK(p); FOREACH_THREAD_IN_PROC(p, td2) { if (tmp >= num) break; buf[tmp++] = td2->td_tid; } PROC_UNLOCK(p); error = copyout(buf, addr, tmp * sizeof(lwpid_t)); free(buf, M_TEMP); if (!error) td->td_retval[0] = tmp; PROC_LOCK(p); break; case PT_VM_TIMESTAMP: CTR2(KTR_PTRACE, "PT_VM_TIMESTAMP: pid %d: timestamp %d", p->p_pid, p->p_vmspace->vm_map.timestamp); td->td_retval[0] = p->p_vmspace->vm_map.timestamp; break; case PT_VM_ENTRY: PROC_UNLOCK(p); error = ptrace_vm_entry(td, p, addr); PROC_LOCK(p); break; case PT_COREDUMP: pc = addr; CTR2(KTR_PTRACE, "PT_COREDUMP: pid %d, fd %d", p->p_pid, pc->pc_fd); if ((pc->pc_flags & ~(PC_COMPRESS | PC_ALL)) != 0) { error = EINVAL; break; } PROC_UNLOCK(p); tcq = malloc(sizeof(*tcq), M_TEMP, M_WAITOK | M_ZERO); fp = NULL; error = fget_write(td, pc->pc_fd, &cap_write_rights, &fp); if (error != 0) goto coredump_cleanup_nofp; if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VREG) { error = EPIPE; goto coredump_cleanup; } PROC_LOCK(p); error = proc_can_ptrace(td, p); if (error != 0) goto coredump_cleanup_locked; td2 = ptrace_sel_coredump_thread(p); if (td2 == NULL) { error = EBUSY; goto coredump_cleanup_locked; } KASSERT((td2->td_dbgflags & TDB_COREDUMPRQ) == 0, ("proc %d tid %d req coredump", p->p_pid, td2->td_tid)); tcq->tc_vp = fp->f_vnode; tcq->tc_limit = pc->pc_limit == 0 ? OFF_MAX : pc->pc_limit; tcq->tc_flags = SVC_PT_COREDUMP; if ((pc->pc_flags & PC_COMPRESS) == 0) tcq->tc_flags |= SVC_NOCOMPRESS; if ((pc->pc_flags & PC_ALL) != 0) tcq->tc_flags |= SVC_ALL; td2->td_coredump = tcq; td2->td_dbgflags |= TDB_COREDUMPRQ; thread_run_flash(td2); while ((td2->td_dbgflags & TDB_COREDUMPRQ) != 0) msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0); error = tcq->tc_error; coredump_cleanup_locked: PROC_UNLOCK(p); coredump_cleanup: fdrop(fp, td); coredump_cleanup_nofp: free(tcq, M_TEMP); PROC_LOCK(p); break; default: #ifdef __HAVE_PTRACE_MACHDEP if (req >= PT_FIRSTMACH) { PROC_UNLOCK(p); error = cpu_ptrace(td2, req, addr, data); PROC_LOCK(p); } else #endif /* Unknown request. */ error = EINVAL; break; } out: /* Drop our hold on this process now that the request has completed. */ _PRELE(p); fail: if (p2_req_set) { if ((p->p_flag2 & P2_PTRACEREQ) != 0) wakeup(&p->p_flag2); p->p_flag2 &= ~P2_PTRACEREQ; } PROC_UNLOCK(p); if (proctree_locked) sx_xunlock(&proctree_lock); return (error); } #undef PROC_READ #undef PROC_WRITE diff --git a/sys/powerpc/powerpc/elf32_machdep.c b/sys/powerpc/powerpc/elf32_machdep.c index 993c55a3ce48..fd88900292ac 100644 --- a/sys/powerpc/powerpc/elf32_machdep.c +++ b/sys/powerpc/powerpc/elf32_machdep.c @@ -1,471 +1,473 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #define __ELF_WORD_SIZE 32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __powerpc64__ #include #include extern const char *freebsd32_syscallnames[]; static void ppc32_fixlimit(struct rlimit *rl, int which); static SYSCTL_NODE(_compat, OID_AUTO, ppc32, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "32-bit mode"); #define PPC32_MAXDSIZ (1024*1024*1024) static u_long ppc32_maxdsiz = PPC32_MAXDSIZ; SYSCTL_ULONG(_compat_ppc32, OID_AUTO, maxdsiz, CTLFLAG_RWTUN, &ppc32_maxdsiz, 0, ""); #define PPC32_MAXSSIZ (64*1024*1024) u_long ppc32_maxssiz = PPC32_MAXSSIZ; SYSCTL_ULONG(_compat_ppc32, OID_AUTO, maxssiz, CTLFLAG_RWTUN, &ppc32_maxssiz, 0, ""); #else static void ppc32_runtime_resolve(void); #endif struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, #ifdef __powerpc64__ .sv_table = freebsd32_sysent, #else .sv_table = sysent, #endif .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_copyout_auxargs = __elfN(powerpc_copyout_auxargs), .sv_sendsig = sendsig, .sv_sigcode = sigcode32, .sv_szsigcode = &szsigcode32, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_stackprot = VM_PROT_ALL, #ifdef __powerpc64__ .sv_maxuser = VM_MAXUSER_ADDRESS32, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_psstringssz = sizeof(struct freebsd32_ps_strings), .sv_copyout_strings = freebsd32_copyout_strings, .sv_setregs = ppc32_setregs, .sv_syscallnames = freebsd32_syscallnames, .sv_fixlimit = ppc32_fixlimit, #else .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_syscallnames = syscallnames, .sv_fixlimit = NULL, #endif .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP | SV_ASLR | SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &cpu_features, .sv_hwcap2 = &cpu_features2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, #ifdef __powerpc64__ .interp_newpath = "/libexec/ld-elf32.so.1", #else .interp_newpath = NULL, #endif .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_info); static Elf32_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_oinfo); void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { size_t len; struct pcb *pcb; uint64_t vshr[32]; uint64_t *vsr_dw1; int vsr_idx; len = 0; pcb = td->td_pcb; if (pcb->pcb_flags & PCB_VEC) { save_vec_nodrop(td); if (dst != NULL) { len += elf32_populate_note(NT_PPC_VMX, &pcb->pcb_vec, (char *)dst + len, sizeof(pcb->pcb_vec), NULL); } else len += elf32_populate_note(NT_PPC_VMX, NULL, NULL, sizeof(pcb->pcb_vec), NULL); } if (pcb->pcb_flags & PCB_VSX) { save_fpu_nodrop(td); if (dst != NULL) { /* * Doubleword 0 of VSR0-VSR31 overlap with FPR0-FPR31 and * VSR32-VSR63 overlap with VR0-VR31, so we only copy * the non-overlapping data, which is doubleword 1 of VSR0-VSR31. */ for (vsr_idx = 0; vsr_idx < nitems(vshr); vsr_idx++) { vsr_dw1 = (uint64_t *)&pcb->pcb_fpu.fpr[vsr_idx].vsr[2]; vshr[vsr_idx] = *vsr_dw1; } len += elf32_populate_note(NT_PPC_VSX, vshr, (char *)dst + len, sizeof(vshr), NULL); } else len += elf32_populate_note(NT_PPC_VSX, NULL, NULL, sizeof(vshr), NULL); } *off = len; } #ifndef __powerpc64__ bool elf_is_ifunc_reloc(Elf_Size r_info) { return (ELF_R_TYPE(r_info) == R_PPC_IRELATIVE); } /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Half *hwhere; Elf_Addr addr; Elf_Addr addend, val; Elf_Word rtype, symidx; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: panic("PPC only supports RELA relocations"); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) ((uintptr_t)relocbase + rela->r_offset); hwhere = (Elf_Half *) ((uintptr_t)relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("elf_reloc: unknown relocation mode %d\n", type); } switch (rtype) { case R_PPC_NONE: break; case R_PPC_ADDR32: /* word32 S + A */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); *where = elf_relocaddr(lf, addr + addend); break; case R_PPC_ADDR16_LO: /* #lo(S) */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); /* * addend values are sometimes relative to sections * (i.e. .rodata) in rela, where in reality they * are relative to relocbase. Detect this condition. */ if (addr > relocbase && addr <= (relocbase + addend)) addr = relocbase; addr = elf_relocaddr(lf, addr + addend); *hwhere = addr & 0xffff; break; case R_PPC_ADDR16_HA: /* #ha(S) */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); /* * addend values are sometimes relative to sections * (i.e. .rodata) in rela, where in reality they * are relative to relocbase. Detect this condition. */ if (addr > relocbase && addr <= (relocbase + addend)) addr = relocbase; addr = elf_relocaddr(lf, addr + addend); *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) & 0xffff; break; case R_PPC_RELATIVE: /* word32 B + A */ *where = elf_relocaddr(lf, relocbase + addend); break; case R_PPC_JMP_SLOT: /* PLT jump slot entry */ /* * We currently only support Secure-PLT jump slots. * Given that we reject BSS-PLT modules during load, we * don't need to check again. * The method we are using here is equivilent to * LD_BIND_NOW. */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); *where = elf_relocaddr(lf, addr + addend); break; case R_PPC_IRELATIVE: addr = relocbase + addend; val = ((Elf32_Addr (*)(void))addr)(); if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", (int)rtype, symidx); return (-1); } return (0); } void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) { Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) continue; where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf) { /* Only sync the cache for non-kernel modules */ if (lf->id != 1) __syncicache(lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } static void ppc32_runtime_resolve() { /* * Since we don't support lazy binding, panic immediately if anyone * manages to call the runtime resolver. */ panic("kldload: Runtime resolver was called unexpectedly!"); } int elf_cpu_parse_dynamic(caddr_t loadbase, Elf_Dyn *dynamic) { Elf_Dyn *dp; bool has_plt = false; bool secure_plt = false; Elf_Addr *got; for (dp = dynamic; dp->d_tag != DT_NULL; dp++) { switch (dp->d_tag) { case DT_PPC_GOT: secure_plt = true; got = (Elf_Addr *)(loadbase + dp->d_un.d_ptr); /* Install runtime resolver canary. */ got[1] = (Elf_Addr)ppc32_runtime_resolve; got[2] = (Elf_Addr)0; break; case DT_PLTGOT: has_plt = true; break; } } if (has_plt && !secure_plt) { printf("kldload: BSS-PLT modules are not supported.\n"); return (-1); } return (0); } #endif #ifdef __powerpc64__ static void ppc32_fixlimit(struct rlimit *rl, int which) { switch (which) { case RLIMIT_DATA: if (ppc32_maxdsiz != 0) { if (rl->rlim_cur > ppc32_maxdsiz) rl->rlim_cur = ppc32_maxdsiz; if (rl->rlim_max > ppc32_maxdsiz) rl->rlim_max = ppc32_maxdsiz; } break; case RLIMIT_STACK: if (ppc32_maxssiz != 0) { if (rl->rlim_cur > ppc32_maxssiz) rl->rlim_cur = ppc32_maxssiz; if (rl->rlim_max > ppc32_maxssiz) rl->rlim_max = ppc32_maxssiz; } break; } } #endif diff --git a/sys/powerpc/powerpc/elf64_machdep.c b/sys/powerpc/powerpc/elf64_machdep.c index 83a488a25692..5c2e26d064b5 100644 --- a/sys/powerpc/powerpc/elf64_machdep.c +++ b/sys/powerpc/powerpc/elf64_machdep.c @@ -1,461 +1,467 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void exec_setregs_funcdesc(struct thread *td, struct image_params *imgp, uintptr_t stack); struct sysentvec elf64_freebsd_sysvec_v1 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode64, .sv_szsigcode = &szsigcode64, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(powerpc_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs_funcdesc, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP | SV_ASLR | SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &cpu_features, .sv_hwcap2 = &cpu_features2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; struct sysentvec elf64_freebsd_sysvec_v2 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode64, /* Fixed up in ppc64_init_sysvecs(). */ .sv_szsigcode = &szsigcode64, .sv_name = "FreeBSD ELF64 V2", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_ALL, .sv_copyout_auxargs = __elfN(powerpc_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP | SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &cpu_features, .sv_hwcap2 = &cpu_features2, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; static boolean_t ppc64_elfv1_header_match(struct image_params *params, int32_t *, uint32_t *); static boolean_t ppc64_elfv2_header_match(struct image_params *params, int32_t *, uint32_t *); static Elf64_Brandinfo freebsd_brand_info_elfv1 = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v1, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv1_header_match }; SYSINIT(elf64v1, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_info_elfv1); static Elf64_Brandinfo freebsd_brand_info_elfv2 = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v2, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv2_header_match }; SYSINIT(elf64v2, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_info_elfv2); static Elf64_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v1, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv1_header_match }; SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_oinfo); void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); static void ppc64_init_sysvecs(void *arg) { exec_sysvec_init(&elf64_freebsd_sysvec_v2); exec_sysvec_init_secondary(&elf64_freebsd_sysvec_v2, &elf64_freebsd_sysvec_v1); /* * Adjust elfv2 sigcode after elfv1 sysvec is initialized. * exec_sysvec_init_secondary() assumes secondary sysvecs use * identical signal code, and skips allocating a second copy. * Since the ELFv2 trampoline is a strict subset of the ELFv1 code, * we can work around this by adjusting the base address. This also * avoids two copies of the trampoline code being allocated! */ elf64_freebsd_sysvec_v2.sv_sigcode_base += (uintptr_t)sigcode64_elfv2 - (uintptr_t)&sigcode64; elf64_freebsd_sysvec_v2.sv_szsigcode = &szsigcode64_elfv2; } SYSINIT(elf64_sysvec, SI_SUB_EXEC, SI_ORDER_ANY, ppc64_init_sysvecs, NULL); static boolean_t ppc64_elfv1_header_match(struct image_params *params, int32_t *osrel __unused, uint32_t *fctl0 __unused) { const Elf64_Ehdr *hdr = (const Elf64_Ehdr *)params->image_header; int abi = (hdr->e_flags & 3); return (abi == 0 || abi == 1); } static boolean_t ppc64_elfv2_header_match(struct image_params *params, int32_t *osrel __unused, uint32_t *fctl0 __unused) { const Elf64_Ehdr *hdr = (const Elf64_Ehdr *)params->image_header; int abi = (hdr->e_flags & 3); return (abi == 2); } static void exec_setregs_funcdesc(struct thread *td, struct image_params *imgp, uintptr_t stack) { struct trapframe *tf; register_t entry_desc[3]; tf = trapframe(td); exec_setregs(td, imgp, stack); /* * For 64-bit ELFv1, we need to disentangle the function * descriptor * * 0. entry point * 1. TOC value (r2) * 2. Environment pointer (r11) */ (void)copyin((void *)imgp->entry_addr, entry_desc, sizeof(entry_desc)); tf->srr0 = entry_desc[0] + imgp->reloc_base; tf->fixreg[2] = entry_desc[1] + imgp->reloc_base; tf->fixreg[11] = entry_desc[2] + imgp->reloc_base; } void elf64_dump_thread(struct thread *td, void *dst, size_t *off) { size_t len; struct pcb *pcb; uint64_t vshr[32]; uint64_t *vsr_dw1; int vsr_idx; len = 0; pcb = td->td_pcb; if (pcb->pcb_flags & PCB_VEC) { save_vec_nodrop(td); if (dst != NULL) { len += elf64_populate_note(NT_PPC_VMX, &pcb->pcb_vec, (char *)dst + len, sizeof(pcb->pcb_vec), NULL); } else len += elf64_populate_note(NT_PPC_VMX, NULL, NULL, sizeof(pcb->pcb_vec), NULL); } if (pcb->pcb_flags & PCB_VSX) { save_fpu_nodrop(td); if (dst != NULL) { /* * Doubleword 0 of VSR0-VSR31 overlap with FPR0-FPR31 and * VSR32-VSR63 overlap with VR0-VR31, so we only copy * the non-overlapping data, which is doubleword 1 of VSR0-VSR31. */ for (vsr_idx = 0; vsr_idx < nitems(vshr); vsr_idx++) { vsr_dw1 = (uint64_t *)&pcb->pcb_fpu.fpr[vsr_idx].vsr[2]; vshr[vsr_idx] = *vsr_dw1; } len += elf64_populate_note(NT_PPC_VSX, vshr, (char *)dst + len, sizeof(vshr), NULL); } else len += elf64_populate_note(NT_PPC_VSX, NULL, NULL, sizeof(vshr), NULL); } *off = len; } bool elf_is_ifunc_reloc(Elf_Size r_info) { return (ELF_R_TYPE(r_info) == R_PPC_IRELATIVE); } /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend, val; Elf_Word rtype, symidx; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: panic("PPC only supports RELA relocations"); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("elf_reloc: unknown relocation mode %d\n", type); } switch (rtype) { case R_PPC_NONE: break; case R_PPC64_ADDR64: /* doubleword64 S + A */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); addr += addend; *where = addr; break; case R_PPC_RELATIVE: /* doubleword64 B + A */ *where = elf_relocaddr(lf, relocbase + addend); break; case R_PPC_JMP_SLOT: /* function descriptor copy */ lookup(lf, symidx, 1, &addr); #if !defined(_CALL_ELF) || _CALL_ELF == 1 memcpy(where, (Elf_Addr *)addr, 3*sizeof(Elf_Addr)); #else *where = addr; #endif __asm __volatile("dcbst 0,%0; sync" :: "r"(where) : "memory"); break; case R_PPC_IRELATIVE: addr = relocbase + addend; val = ((Elf64_Addr (*)(void))addr)(); if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", (int)rtype, symidx); return (-1); } return (0); } void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) { Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) continue; where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf) { /* Only sync the cache for non-kernel modules */ if (lf->id != 1) __syncicache(lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } diff --git a/sys/riscv/riscv/elf_machdep.c b/sys/riscv/riscv/elf_machdep.c index 09b071eab6ef..117f28c941d3 100644 --- a/sys/riscv/riscv/elf_machdep.c +++ b/sys/riscv/riscv/elf_machdep.c @@ -1,532 +1,535 @@ /*- * Copyright 1996-1998 John D. Polstra. * Copyright (c) 2015 Ruslan Bukin * Copyright (c) 2016 Yukishige Shibata * All rights reserved. * * Portions of this software were developed by SRI International and the * University of Cambridge Computer Laboratory under DARPA/AFRL contract * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. * * Portions of this software were developed by the University of Cambridge * Computer Laboratory as part of the CTSRD Project, with support from the * UK Higher Education Innovation Fund (HEIF). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include static const char *riscv_machine_arch(struct proc *p); u_long elf_hwcap; struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP | SV_TIMEKEEP | SV_ASLR | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &elf_hwcap, .sv_machine_arch = riscv_machine_arch, .sv_onexec_old = exec_onexec_old, .sv_onexit = exit_onexit, + .sv_regset_begin = SET_BEGIN(__elfN(regset)), + .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static const char * riscv_machine_arch(struct proc *p) { if ((p->p_elf_flags & EF_RISCV_FLOAT_ABI_MASK) == EF_RISCV_FLOAT_ABI_SOFT) return (MACHINE_ARCH "sf"); return (MACHINE_ARCH); } static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_RISCV, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info); static bool debug_kld; SYSCTL_BOOL(_debug, OID_AUTO, kld_reloc, CTLFLAG_RW, &debug_kld, 0, "Activate debug prints in elf_reloc_internal()"); struct type2str_ent { int type; const char *str; }; void elf64_dump_thread(struct thread *td, void *dst, size_t *off) { } /* * Following 4 functions are used to manupilate bits on 32bit interger value. * FIXME: I implemetend for ease-to-understand rather than for well-optimized. */ static uint32_t gen_bitmask(int msb, int lsb) { uint32_t mask; if (msb == sizeof(mask) * 8 - 1) mask = ~0; else mask = (1U << (msb + 1)) - 1; if (lsb > 0) mask &= ~((1U << lsb) - 1); return (mask); } static uint32_t extract_bits(uint32_t x, int msb, int lsb) { uint32_t mask; mask = gen_bitmask(msb, lsb); x &= mask; x >>= lsb; return (x); } static uint32_t insert_bits(uint32_t d, uint32_t s, int msb, int lsb) { uint32_t mask; mask = gen_bitmask(msb, lsb); d &= ~mask; s <<= lsb; s &= mask; return (d | s); } static uint32_t insert_imm(uint32_t insn, uint32_t imm, int imm_msb, int imm_lsb, int insn_lsb) { int insn_msb; uint32_t v; v = extract_bits(imm, imm_msb, imm_lsb); insn_msb = (imm_msb - imm_lsb) + insn_lsb; return (insert_bits(insn, v, insn_msb, insn_lsb)); } /* * The RISC-V ISA is designed so that all of immediate values are * sign-extended. * An immediate value is sometimes generated at runtime by adding * 12bit sign integer and 20bit signed integer. This requests 20bit * immediate value to be ajusted if the MSB of the 12bit immediate * value is asserted (sign-extended value is treated as negative value). * * For example, 0x123800 can be calculated by adding upper 20 bit of * 0x124000 and sign-extended 12bit immediate whose bit pattern is * 0x800 as follows: * 0x123800 * = 0x123000 + 0x800 * = (0x123000 + 0x1000) + (-0x1000 + 0x800) * = (0x123000 + 0x1000) + (0xff...ff800) * = 0x124000 + sign-extention(0x800) */ static uint32_t calc_hi20_imm(uint32_t value) { /* * There is the arithmetical hack that can remove conditional * statement. But I implement it in straightforward way. */ if ((value & 0x800) != 0) value += 0x1000; return (value & ~0xfff); } static const struct type2str_ent t2s[] = { { R_RISCV_NONE, "R_RISCV_NONE" }, { R_RISCV_64, "R_RISCV_64" }, { R_RISCV_JUMP_SLOT, "R_RISCV_JUMP_SLOT" }, { R_RISCV_RELATIVE, "R_RISCV_RELATIVE" }, { R_RISCV_JAL, "R_RISCV_JAL" }, { R_RISCV_CALL, "R_RISCV_CALL" }, { R_RISCV_PCREL_HI20, "R_RISCV_PCREL_HI20" }, { R_RISCV_PCREL_LO12_I, "R_RISCV_PCREL_LO12_I" }, { R_RISCV_PCREL_LO12_S, "R_RISCV_PCREL_LO12_S" }, { R_RISCV_HI20, "R_RISCV_HI20" }, { R_RISCV_LO12_I, "R_RISCV_LO12_I" }, { R_RISCV_LO12_S, "R_RISCV_LO12_S" }, }; static const char * reloctype_to_str(int type) { int i; for (i = 0; i < sizeof(t2s) / sizeof(t2s[0]); ++i) { if (type == t2s[i].type) return t2s[i].str; } return "*unknown*"; } bool elf_is_ifunc_reloc(Elf_Size r_info __unused) { return (false); } /* * Currently kernel loadable module for RISCV is compiled with -fPIC option. * (see also additional CFLAGS definition for RISCV in sys/conf/kmod.mk) * Only R_RISCV_64, R_RISCV_JUMP_SLOT and RISCV_RELATIVE are emitted in * the module. Other relocations will be processed when kernel loadable * modules are built in non-PIC. * * FIXME: only RISCV64 is supported. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Size rtype, symidx; const Elf_Rela *rela; Elf_Addr val, addr; Elf64_Addr *where; Elf_Addr addend; uint32_t before32_1; uint32_t before32; uint64_t before64; uint32_t *insn32p; uint32_t imm20; int error; switch (type) { case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *)(relocbase + rela->r_offset); insn32p = (uint32_t *)where; addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: printf("%s:%d unknown reloc type %d\n", __FUNCTION__, __LINE__, type); return (-1); } switch (rtype) { case R_RISCV_NONE: break; case R_RISCV_64: case R_RISCV_JUMP_SLOT: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr; before64 = *where; if (*where != val) *where = val; if (debug_kld) printf("%p %c %-24s %016lx -> %016lx\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before64, *where); break; case R_RISCV_RELATIVE: before64 = *where; *where = elf_relocaddr(lf, relocbase + addend); if (debug_kld) printf("%p %c %-24s %016lx -> %016lx\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before64, *where); break; case R_RISCV_JAL: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr - (Elf_Addr)where; if (val <= -(1UL << 20) || (1UL << 20) <= val) { printf("kldload: huge offset against R_RISCV_JAL\n"); return (-1); } before32 = *insn32p; *insn32p = insert_imm(*insn32p, val, 20, 20, 31); *insn32p = insert_imm(*insn32p, val, 10, 1, 21); *insn32p = insert_imm(*insn32p, val, 11, 11, 20); *insn32p = insert_imm(*insn32p, val, 19, 12, 12); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_CALL: /* * R_RISCV_CALL relocates 8-byte region that consists * of the sequence of AUIPC and JALR. */ /* Calculate and check the pc relative offset. */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr - (Elf_Addr)where; if (val <= -(1UL << 32) || (1UL << 32) <= val) { printf("kldload: huge offset against R_RISCV_CALL\n"); return (-1); } /* Relocate AUIPC. */ before32 = insn32p[0]; imm20 = calc_hi20_imm(val); insn32p[0] = insert_imm(insn32p[0], imm20, 31, 12, 12); /* Relocate JALR. */ before32_1 = insn32p[1]; insn32p[1] = insert_imm(insn32p[1], val, 11, 0, 20); if (debug_kld) printf("%p %c %-24s %08x %08x -> %08x %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, insn32p[0], before32_1, insn32p[1]); break; case R_RISCV_PCREL_HI20: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr - (Elf_Addr)where; insn32p = (uint32_t *)where; before32 = *insn32p; imm20 = calc_hi20_imm(val); *insn32p = insert_imm(*insn32p, imm20, 31, 12, 12); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_PCREL_LO12_I: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr - (Elf_Addr)where; insn32p = (uint32_t *)where; before32 = *insn32p; *insn32p = insert_imm(*insn32p, addr, 11, 0, 20); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_PCREL_LO12_S: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr - (Elf_Addr)where; insn32p = (uint32_t *)where; before32 = *insn32p; *insn32p = insert_imm(*insn32p, addr, 11, 5, 25); *insn32p = insert_imm(*insn32p, addr, 4, 0, 7); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_HI20: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr; insn32p = (uint32_t *)where; before32 = *insn32p; imm20 = calc_hi20_imm(val); *insn32p = insert_imm(*insn32p, imm20, 31, 12, 12); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_LO12_I: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr; insn32p = (uint32_t *)where; before32 = *insn32p; *insn32p = insert_imm(*insn32p, addr, 11, 0, 20); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; case R_RISCV_LO12_S: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); val = addr; insn32p = (uint32_t *)where; before32 = *insn32p; *insn32p = insert_imm(*insn32p, addr, 11, 5, 25); *insn32p = insert_imm(*insn32p, addr, 4, 0, 7); if (debug_kld) printf("%p %c %-24s %08x -> %08x\n", where, (local ? 'l' : 'g'), reloctype_to_str(rtype), before32, *insn32p); break; default: printf("kldload: unexpected relocation type %ld, " "symbol index %ld\n", rtype, symidx); return (-1); } return (0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index 4cd7a3fceaec..197ac1692dfb 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -1,258 +1,260 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1984, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ptrace.h 8.2 (Berkeley) 1/4/94 * $FreeBSD$ */ #ifndef _SYS_PTRACE_H_ #define _SYS_PTRACE_H_ #include #include #include #define PT_TRACE_ME 0 /* child declares it's being traced */ #define PT_READ_I 1 /* read word in child's I space */ #define PT_READ_D 2 /* read word in child's D space */ /* was PT_READ_U 3 * read word in child's user structure */ #define PT_WRITE_I 4 /* write word in child's I space */ #define PT_WRITE_D 5 /* write word in child's D space */ /* was PT_WRITE_U 6 * write word in child's user structure */ #define PT_CONTINUE 7 /* continue the child */ #define PT_KILL 8 /* kill the child process */ #define PT_STEP 9 /* single step the child */ #define PT_ATTACH 10 /* trace some running process */ #define PT_DETACH 11 /* stop tracing a process */ #define PT_IO 12 /* do I/O to/from stopped process. */ #define PT_LWPINFO 13 /* Info about the LWP that stopped. */ #define PT_GETNUMLWPS 14 /* get total number of threads */ #define PT_GETLWPLIST 15 /* get thread list */ #define PT_CLEARSTEP 16 /* turn off single step */ #define PT_SETSTEP 17 /* turn on single step */ #define PT_SUSPEND 18 /* suspend a thread */ #define PT_RESUME 19 /* resume a thread */ #define PT_TO_SCE 20 #define PT_TO_SCX 21 #define PT_SYSCALL 22 #define PT_FOLLOW_FORK 23 #define PT_LWP_EVENTS 24 /* report LWP birth and exit */ #define PT_GET_EVENT_MASK 25 /* get mask of optional events */ #define PT_SET_EVENT_MASK 26 /* set mask of optional events */ #define PT_GET_SC_ARGS 27 /* fetch syscall args */ #define PT_GET_SC_RET 28 /* fetch syscall results */ #define PT_COREDUMP 29 /* create a coredump */ #define PT_GETREGS 33 /* get general-purpose registers */ #define PT_SETREGS 34 /* set general-purpose registers */ #define PT_GETFPREGS 35 /* get floating-point registers */ #define PT_SETFPREGS 36 /* set floating-point registers */ #define PT_GETDBREGS 37 /* get debugging registers */ #define PT_SETDBREGS 38 /* set debugging registers */ #define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */ #define PT_VM_ENTRY 41 /* Get VM map (entry) */ +#define PT_GETREGSET 42 /* Get a target register set */ +#define PT_SETREGSET 43 /* Set a target register set */ #define PT_FIRSTMACH 64 /* for machine-specific requests */ #include /* machine-specific requests, if any */ /* Events used with PT_GET_EVENT_MASK and PT_SET_EVENT_MASK */ #define PTRACE_EXEC 0x0001 #define PTRACE_SCE 0x0002 #define PTRACE_SCX 0x0004 #define PTRACE_SYSCALL (PTRACE_SCE | PTRACE_SCX) #define PTRACE_FORK 0x0008 #define PTRACE_LWP 0x0010 #define PTRACE_VFORK 0x0020 #define PTRACE_DEFAULT (PTRACE_EXEC) struct ptrace_io_desc { int piod_op; /* I/O operation */ void *piod_offs; /* child offset */ void *piod_addr; /* parent offset */ size_t piod_len; /* request length */ }; /* * Operations in piod_op. */ #define PIOD_READ_D 1 /* Read from D space */ #define PIOD_WRITE_D 2 /* Write to D space */ #define PIOD_READ_I 3 /* Read from I space */ #define PIOD_WRITE_I 4 /* Write to I space */ /* Argument structure for PT_LWPINFO. */ struct ptrace_lwpinfo { lwpid_t pl_lwpid; /* LWP described. */ int pl_event; /* Event that stopped the LWP. */ #define PL_EVENT_NONE 0 #define PL_EVENT_SIGNAL 1 int pl_flags; /* LWP flags. */ #define PL_FLAG_SA 0x01 /* M:N thread */ #define PL_FLAG_BOUND 0x02 /* M:N bound thread */ #define PL_FLAG_SCE 0x04 /* syscall enter point */ #define PL_FLAG_SCX 0x08 /* syscall leave point */ #define PL_FLAG_EXEC 0x10 /* exec(2) succeeded */ #define PL_FLAG_SI 0x20 /* siginfo is valid */ #define PL_FLAG_FORKED 0x40 /* new child */ #define PL_FLAG_CHILD 0x80 /* I am from child */ #define PL_FLAG_BORN 0x100 /* new LWP */ #define PL_FLAG_EXITED 0x200 /* exiting LWP */ #define PL_FLAG_VFORKED 0x400 /* new child via vfork */ #define PL_FLAG_VFORK_DONE 0x800 /* vfork parent has resumed */ sigset_t pl_sigmask; /* LWP signal mask */ sigset_t pl_siglist; /* LWP pending signal */ struct __siginfo pl_siginfo; /* siginfo for signal */ char pl_tdname[MAXCOMLEN + 1]; /* LWP name */ pid_t pl_child_pid; /* New child pid */ u_int pl_syscall_code; u_int pl_syscall_narg; }; #if defined(_WANT_LWPINFO32) || (defined(_KERNEL) && defined(__LP64__)) struct ptrace_lwpinfo32 { lwpid_t pl_lwpid; /* LWP described. */ int pl_event; /* Event that stopped the LWP. */ int pl_flags; /* LWP flags. */ sigset_t pl_sigmask; /* LWP signal mask */ sigset_t pl_siglist; /* LWP pending signal */ struct siginfo32 pl_siginfo; /* siginfo for signal */ char pl_tdname[MAXCOMLEN + 1]; /* LWP name. */ pid_t pl_child_pid; /* New child pid */ u_int pl_syscall_code; u_int pl_syscall_narg; }; #endif /* Argument structure for PT_GET_SC_RET. */ struct ptrace_sc_ret { register_t sr_retval[2]; /* Only valid if sr_error == 0. */ int sr_error; }; /* Argument structure for PT_VM_ENTRY. */ struct ptrace_vm_entry { int pve_entry; /* Entry number used for iteration. */ int pve_timestamp; /* Generation number of VM map. */ u_long pve_start; /* Start VA of range. */ u_long pve_end; /* End VA of range (incl). */ u_long pve_offset; /* Offset in backing object. */ u_int pve_prot; /* Protection of memory range. */ u_int pve_pathlen; /* Size of path. */ long pve_fileid; /* File ID. */ uint32_t pve_fsid; /* File system ID. */ char *pve_path; /* Path name of object. */ }; /* Argument structure for PT_COREDUMP */ struct ptrace_coredump { int pc_fd; /* File descriptor to write dump to. */ uint32_t pc_flags; /* Flags PC_* */ off_t pc_limit; /* Maximum size of the coredump, 0 for no limit. */ }; /* Flags for PT_COREDUMP pc_flags */ #define PC_COMPRESS 0x00000001 /* Allow compression */ #define PC_ALL 0x00000002 /* Include non-dumpable entries */ #ifdef _KERNEL struct thr_coredump_req { struct vnode *tc_vp; /* vnode to write coredump to. */ off_t tc_limit; /* max coredump file size. */ int tc_flags; /* user flags */ int tc_error; /* request result */ }; int ptrace_set_pc(struct thread *_td, unsigned long _addr); int ptrace_single_step(struct thread *_td); int ptrace_clear_single_step(struct thread *_td); #ifdef __HAVE_PTRACE_MACHDEP int cpu_ptrace(struct thread *_td, int _req, void *_addr, int _data); #endif /* * These are prototypes for functions that implement some of the * debugging functionality exported by procfs / linprocfs and by the * ptrace(2) syscall. They used to be part of procfs, but they don't * really belong there. */ struct reg; struct fpreg; struct dbreg; struct uio; int proc_read_regs(struct thread *_td, struct reg *_reg); int proc_write_regs(struct thread *_td, struct reg *_reg); int proc_read_fpregs(struct thread *_td, struct fpreg *_fpreg); int proc_write_fpregs(struct thread *_td, struct fpreg *_fpreg); int proc_read_dbregs(struct thread *_td, struct dbreg *_dbreg); int proc_write_dbregs(struct thread *_td, struct dbreg *_dbreg); int proc_sstep(struct thread *_td); int proc_rwmem(struct proc *_p, struct uio *_uio); ssize_t proc_readmem(struct thread *_td, struct proc *_p, vm_offset_t _va, void *_buf, size_t _len); ssize_t proc_writemem(struct thread *_td, struct proc *_p, vm_offset_t _va, void *_buf, size_t _len); #ifdef COMPAT_FREEBSD32 struct reg32; struct fpreg32; struct dbreg32; int proc_read_regs32(struct thread *_td, struct reg32 *_reg32); int proc_write_regs32(struct thread *_td, struct reg32 *_reg32); int proc_read_fpregs32(struct thread *_td, struct fpreg32 *_fpreg32); int proc_write_fpregs32(struct thread *_td, struct fpreg32 *_fpreg32); int proc_read_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32); int proc_write_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32); #endif void ptrace_unsuspend(struct proc *p); extern bool allow_ptrace; #else /* !_KERNEL */ #include __BEGIN_DECLS int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data); __END_DECLS #endif /* !_KERNEL */ #endif /* !_SYS_PTRACE_H_ */ diff --git a/sys/sys/reg.h b/sys/sys/reg.h index 910ea802029e..e06a5f9a19ed 100644 --- a/sys/sys/reg.h +++ b/sys/sys/reg.h @@ -1,68 +1,87 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2014,2019 Andrew Turner * Copyright (c) 2014-2015 The FreeBSD Foundation * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_REG_H_ #define _SYS_REG_H_ #include #ifdef _KERNEL +struct sbuf; +struct regset; + +typedef bool (regset_get)(struct regset *, struct thread *, void *, + size_t *); +typedef bool (regset_set)(struct regset *, struct thread *, void *, size_t); + +struct regset { + int note; + size_t size; + regset_get *get; + regset_set *set; +}; + +#if defined(__ELF_WORD_SIZE) +SET_DECLARE(__elfN(regset), struct regset); +#define ELF_REGSET(_regset) DATA_SET(__elfN(regset), _regset) +#endif + int fill_regs(struct thread *, struct reg *); int set_regs(struct thread *, struct reg *); int fill_fpregs(struct thread *, struct fpreg *); int set_fpregs(struct thread *, struct fpreg *); int fill_dbregs(struct thread *, struct dbreg *); int set_dbregs(struct thread *, struct dbreg *); #ifdef COMPAT_FREEBSD32 int fill_regs32(struct thread *, struct reg32 *); int set_regs32(struct thread *, struct reg32 *); #ifndef fill_fpregs32 int fill_fpregs32(struct thread *, struct fpreg32 *); #endif #ifndef set_fpregs32 int set_fpregs32(struct thread *, struct fpreg32 *); #endif #ifndef fill_dbregs32 int fill_dbregs32(struct thread *, struct dbreg32 *); #endif #ifndef set_dbregs32 int set_dbregs32(struct thread *, struct dbreg32 *); #endif #endif #endif #endif diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index 9883870891da..7551b8ec92c4 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -1,345 +1,347 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1988, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_SYSENT_H_ #define _SYS_SYSENT_H_ #include struct rlimit; struct sysent; struct thread; struct ksiginfo; struct syscall_args; enum systrace_probe_t { SYSTRACE_ENTRY, SYSTRACE_RETURN, }; typedef int sy_call_t(struct thread *, void *); typedef void (*systrace_probe_func_t)(struct syscall_args *, enum systrace_probe_t, int); typedef void (*systrace_args_func_t)(int, void *, uint64_t *, int *); #ifdef _KERNEL extern systrace_probe_func_t systrace_probe_func; extern bool systrace_enabled; #ifdef KDTRACE_HOOKS #define SYSTRACE_ENABLED() (systrace_enabled) #else #define SYSTRACE_ENABLED() (0) #endif #endif /* _KERNEL */ struct sysent { /* system call table */ sy_call_t *sy_call; /* implementing function */ systrace_args_func_t sy_systrace_args_func; /* optional argument conversion function. */ u_int8_t sy_narg; /* number of arguments */ u_int8_t sy_flags; /* General flags for system calls. */ au_event_t sy_auevent; /* audit event associated with syscall */ u_int32_t sy_entry; /* DTrace entry ID for systrace. */ u_int32_t sy_return; /* DTrace return ID for systrace. */ u_int32_t sy_thrcnt; }; /* * A system call is permitted in capability mode. */ #define SYF_CAPENABLED 0x00000001 #define SY_THR_FLAGMASK 0x7 #define SY_THR_STATIC 0x1 #define SY_THR_DRAINING 0x2 #define SY_THR_ABSENT 0x4 #define SY_THR_INCR 0x8 #ifdef KLD_MODULE #define SY_THR_STATIC_KLD 0 #else #define SY_THR_STATIC_KLD SY_THR_STATIC #endif struct image_params; struct proc; struct __sigset; struct trapframe; struct vnode; struct note_info_list; struct sysentvec { int sv_size; /* number of entries */ struct sysent *sv_table; /* pointer to sysent */ int (*sv_transtrap)(int, int); /* translate trap-to-signal mapping */ int (*sv_fixup)(uintptr_t *, struct image_params *); /* stack fixup function */ void (*sv_sendsig)(void (*)(int), struct ksiginfo *, struct __sigset *); /* send signal */ const char *sv_sigcode; /* start of sigtramp code */ int *sv_szsigcode; /* size of sigtramp code */ int sv_sigcodeoff; char *sv_name; /* name of binary type */ int (*sv_coredump)(struct thread *, struct vnode *, off_t, int); /* function to dump core, or NULL */ int sv_elf_core_osabi; const char *sv_elf_core_abi_vendor; void (*sv_elf_core_prepare_notes)(struct thread *, struct note_info_list *, size_t *); int (*sv_imgact_try)(struct image_params *); int (*sv_copyout_auxargs)(struct image_params *, uintptr_t); int sv_minsigstksz; /* minimum signal stack size */ vm_offset_t sv_minuser; /* VM_MIN_ADDRESS */ vm_offset_t sv_maxuser; /* VM_MAXUSER_ADDRESS */ vm_offset_t sv_usrstack; /* USRSTACK */ vm_offset_t sv_psstrings; /* PS_STRINGS */ size_t sv_psstringssz; /* PS_STRINGS size */ int sv_stackprot; /* vm protection for stack */ int (*sv_copyout_strings)(struct image_params *, uintptr_t *); void (*sv_setregs)(struct thread *, struct image_params *, uintptr_t); void (*sv_fixlimit)(struct rlimit *, int); u_long *sv_maxssiz; u_int sv_flags; void (*sv_set_syscall_retval)(struct thread *, int); int (*sv_fetch_syscall_args)(struct thread *); const char **sv_syscallnames; vm_offset_t sv_timekeep_base; vm_offset_t sv_shared_page_base; vm_offset_t sv_shared_page_len; vm_offset_t sv_sigcode_base; void *sv_shared_page_obj; vm_offset_t sv_vdso_base; void (*sv_schedtail)(struct thread *); void (*sv_thread_detach)(struct thread *); int (*sv_trap)(struct thread *); u_long *sv_hwcap; /* Value passed in AT_HWCAP. */ u_long *sv_hwcap2; /* Value passed in AT_HWCAP2. */ const char *(*sv_machine_arch)(struct proc *); vm_offset_t sv_fxrng_gen_base; void (*sv_onexec_old)(struct thread *td); int (*sv_onexec)(struct proc *, struct image_params *); void (*sv_onexit)(struct proc *); void (*sv_ontdexit)(struct thread *td); int (*sv_setid_allowed)(struct thread *td, struct image_params *imgp); void (*sv_set_fork_retval)(struct thread *); /* Only used on x86 */ + struct regset **sv_regset_begin; + struct regset **sv_regset_end; }; #define SV_ILP32 0x000100 /* 32-bit executable. */ #define SV_LP64 0x000200 /* 64-bit executable. */ #define SV_IA32 0x004000 /* Intel 32-bit executable. */ #define SV_AOUT 0x008000 /* a.out executable. */ #define SV_SHP 0x010000 /* Shared page. */ #define SV_AVAIL1 0x020000 /* Unused */ #define SV_TIMEKEEP 0x040000 /* Shared page timehands. */ #define SV_ASLR 0x080000 /* ASLR allowed. */ #define SV_RNG_SEED_VER 0x100000 /* random(4) reseed generation. */ #define SV_SIG_DISCIGN 0x200000 /* Do not discard ignored signals */ #define SV_SIG_WAITNDQ 0x400000 /* Wait does not dequeue SIGCHLD */ #define SV_DSO_SIG 0x800000 /* Signal trampoline packed in dso */ #define SV_ABI_MASK 0xff #define SV_PROC_FLAG(p, x) ((p)->p_sysent->sv_flags & (x)) #define SV_PROC_ABI(p) ((p)->p_sysent->sv_flags & SV_ABI_MASK) #define SV_CURPROC_FLAG(x) SV_PROC_FLAG(curproc, x) #define SV_CURPROC_ABI() SV_PROC_ABI(curproc) /* same as ELFOSABI_XXX, to prevent header pollution */ #define SV_ABI_LINUX 3 #define SV_ABI_FREEBSD 9 #define SV_ABI_UNDEF 255 /* sv_coredump flags */ #define SVC_PT_COREDUMP 0x00000001 /* dump requested by ptrace(2) */ #define SVC_NOCOMPRESS 0x00000002 /* disable compression. */ #define SVC_ALL 0x00000004 /* dump everything */ #ifdef _KERNEL extern struct sysentvec aout_sysvec; extern struct sysent sysent[]; extern const char *syscallnames[]; struct nosys_args { register_t dummy; }; int nosys(struct thread *, struct nosys_args *); #define NO_SYSCALL (-1) struct module; struct syscall_module_data { int (*chainevh)(struct module *, int, void *); /* next handler */ void *chainarg; /* arg for next event handler */ int *offset; /* offset into sysent */ struct sysent *new_sysent; /* new sysent */ struct sysent old_sysent; /* old sysent */ int flags; /* flags for syscall_register */ }; /* separate initialization vector so it can be used in a substructure */ #define SYSENT_INIT_VALS(_syscallname) { \ .sy_narg = (sizeof(struct _syscallname ## _args ) \ / sizeof(register_t)), \ .sy_call = (sy_call_t *)&sys_##_syscallname, \ .sy_auevent = SYS_AUE_##_syscallname, \ .sy_systrace_args_func = NULL, \ .sy_entry = 0, \ .sy_return = 0, \ .sy_flags = 0, \ .sy_thrcnt = 0 \ } #define MAKE_SYSENT(syscallname) \ static struct sysent syscallname##_sysent = SYSENT_INIT_VALS(syscallname); #define MAKE_SYSENT_COMPAT(syscallname) \ static struct sysent syscallname##_sysent = { \ (sizeof(struct syscallname ## _args ) \ / sizeof(register_t)), \ (sy_call_t *)& syscallname, \ SYS_AUE_##syscallname \ } #define SYSCALL_MODULE(name, offset, new_sysent, evh, arg) \ static struct syscall_module_data name##_syscall_mod = { \ evh, arg, offset, new_sysent, { 0, NULL, AUE_NULL } \ }; \ \ static moduledata_t name##_mod = { \ "sys/" #name, \ syscall_module_handler, \ &name##_syscall_mod \ }; \ DECLARE_MODULE(name, name##_mod, SI_SUB_SYSCALLS, SI_ORDER_MIDDLE) #define SYSCALL_MODULE_HELPER(syscallname) \ static int syscallname##_syscall = SYS_##syscallname; \ MAKE_SYSENT(syscallname); \ SYSCALL_MODULE(syscallname, \ & syscallname##_syscall, & syscallname##_sysent, \ NULL, NULL) #define SYSCALL_MODULE_PRESENT(syscallname) \ (sysent[SYS_##syscallname].sy_call != (sy_call_t *)lkmnosys && \ sysent[SYS_##syscallname].sy_call != (sy_call_t *)lkmressys) /* * Syscall registration helpers with resource allocation handling. */ struct syscall_helper_data { struct sysent new_sysent; struct sysent old_sysent; int syscall_no; int registered; }; #define SYSCALL_INIT_HELPER_F(syscallname, flags) { \ .new_sysent = { \ .sy_narg = (sizeof(struct syscallname ## _args ) \ / sizeof(register_t)), \ .sy_call = (sy_call_t *)& sys_ ## syscallname, \ .sy_auevent = SYS_AUE_##syscallname, \ .sy_flags = (flags) \ }, \ .syscall_no = SYS_##syscallname \ } #define SYSCALL_INIT_HELPER_COMPAT_F(syscallname, flags) { \ .new_sysent = { \ .sy_narg = (sizeof(struct syscallname ## _args ) \ / sizeof(register_t)), \ .sy_call = (sy_call_t *)& syscallname, \ .sy_auevent = SYS_AUE_##syscallname, \ .sy_flags = (flags) \ }, \ .syscall_no = SYS_##syscallname \ } #define SYSCALL_INIT_HELPER(syscallname) \ SYSCALL_INIT_HELPER_F(syscallname, 0) #define SYSCALL_INIT_HELPER_COMPAT(syscallname) \ SYSCALL_INIT_HELPER_COMPAT_F(syscallname, 0) #define SYSCALL_INIT_LAST { \ .syscall_no = NO_SYSCALL \ } int syscall_module_handler(struct module *mod, int what, void *arg); int syscall_helper_register(struct syscall_helper_data *sd, int flags); int syscall_helper_unregister(struct syscall_helper_data *sd); /* Implementation, exposed for COMPAT code */ int kern_syscall_register(struct sysent *sysents, int *offset, struct sysent *new_sysent, struct sysent *old_sysent, int flags); int kern_syscall_deregister(struct sysent *sysents, int offset, const struct sysent *old_sysent); int kern_syscall_module_handler(struct sysent *sysents, struct module *mod, int what, void *arg); int kern_syscall_helper_register(struct sysent *sysents, struct syscall_helper_data *sd, int flags); int kern_syscall_helper_unregister(struct sysent *sysents, struct syscall_helper_data *sd); struct proc; const char *syscallname(struct proc *p, u_int code); /* Special purpose system call functions. */ struct nosys_args; int lkmnosys(struct thread *, struct nosys_args *); int lkmressys(struct thread *, struct nosys_args *); int syscall_thread_enter(struct thread *td, struct sysent *se); void syscall_thread_exit(struct thread *td, struct sysent *se); int shared_page_alloc(int size, int align); int shared_page_fill(int size, int align, const void *data); void shared_page_write(int base, int size, const void *data); void exec_sysvec_init(void *param); void exec_sysvec_init_secondary(struct sysentvec *sv, struct sysentvec *sv2); void exec_inittk(void); void exit_onexit(struct proc *p); void exec_free_abi_mappings(struct proc *p); void exec_onexec_old(struct thread *td); #define INIT_SYSENTVEC(name, sv) \ SYSINIT(name, SI_SUB_EXEC, SI_ORDER_ANY, \ (sysinit_cfunc_t)exec_sysvec_init, sv); #endif /* _KERNEL */ #endif /* !_SYS_SYSENT_H_ */ diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c index 16c047db574a..3e3a445c6152 100644 --- a/tests/sys/kern/ptrace_test.c +++ b/tests/sys/kern/ptrace_test.c @@ -1,4324 +1,4385 @@ /*- * Copyright (c) 2015 John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include #define _WANT_MIPS_REGNUM #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Architectures with a user-visible breakpoint(). */ #if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \ defined(__i386__) || defined(__mips__) || defined(__riscv) || \ defined(__sparc64__) #define HAVE_BREAKPOINT #endif /* * Adjust PC to skip over a breakpoint when stopped for a breakpoint trap. */ #ifdef HAVE_BREAKPOINT #if defined(__aarch64__) #define SKIP_BREAK(reg) ((reg)->elr += 4) #elif defined(__amd64__) || defined(__i386__) #define SKIP_BREAK(reg) #elif defined(__arm__) #define SKIP_BREAK(reg) ((reg)->r_pc += 4) #elif defined(__mips__) #define SKIP_BREAK(reg) ((reg)->r_regs[PC] += 4) #elif defined(__riscv) #define SKIP_BREAK(reg) ((reg)->sepc += 4) #elif defined(__sparc64__) #define SKIP_BREAK(reg) do { \ (reg)->r_tpc = (reg)->r_tnpc + 4; \ (reg)->r_tnpc += 8; \ } while (0) #endif #endif /* * A variant of ATF_REQUIRE that is suitable for use in child * processes. This only works if the parent process is tripped up by * the early exit and fails some requirement itself. */ #define CHILD_REQUIRE(exp) do { \ if (!(exp)) \ child_fail_require(__FILE__, __LINE__, \ #exp " not met\n"); \ } while (0) #define CHILD_REQUIRE_EQ(actual, expected) do { \ __typeof__(expected) _e = expected; \ __typeof__(actual) _a = actual; \ if (_e != _a) \ child_fail_require(__FILE__, __LINE__, #actual \ " (%jd) == " #expected " (%jd) not met\n", \ (intmax_t)_a, (intmax_t)_e); \ } while (0) static __dead2 void child_fail_require(const char *file, int line, const char *fmt, ...) { va_list ap; char buf[1024]; /* Use write() not fprintf() to avoid possible duplicate output. */ snprintf(buf, sizeof(buf), "%s:%d: ", file, line); write(STDERR_FILENO, buf, strlen(buf)); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); write(STDERR_FILENO, buf, strlen(buf)); va_end(ap); _exit(32); } #define REQUIRE_EQ(actual, expected) do { \ __typeof__(expected) _e = expected; \ __typeof__(actual) _a = actual; \ ATF_REQUIRE_MSG(_e == _a, #actual " (%jd) == " \ #expected " (%jd) not met", (intmax_t)_a, (intmax_t)_e); \ } while (0) static void trace_me(void) { /* Attach the parent process as a tracer of this process. */ CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); /* Trigger a stop. */ raise(SIGSTOP); } static void attach_child(pid_t pid) { pid_t wpid; int status; REQUIRE_EQ(ptrace(PT_ATTACH, pid, NULL, 0), 0); wpid = waitpid(pid, &status, 0); REQUIRE_EQ(wpid, pid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); } static void wait_for_zombie(pid_t pid) { /* * Wait for a process to exit. This is kind of gross, but * there is not a better way. * * Prior to r325719, the kern.proc.pid. sysctl failed * with ESRCH. After that change, a valid struct kinfo_proc * is returned for zombies with ki_stat set to SZOMB. */ for (;;) { struct kinfo_proc kp; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = pid; len = sizeof(kp); if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) { REQUIRE_EQ(errno, ESRCH); break; } if (kp.ki_stat == SZOMB) break; usleep(5000); } } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_TRACE_ME. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me); ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc) { pid_t child, wpid; int status; ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ trace_me(); _exit(1); } /* Parent process. */ /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_ATTACH. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach); ATF_TC_BODY(ptrace__parent_wait_after_attach, tc) { pid_t child, wpid; int cpipe[2], status; char c; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for the parent to attach. */ CHILD_REQUIRE_EQ(0, read(cpipe[1], &c, sizeof(c))); _exit(1); } close(cpipe[1]); /* Parent process. */ /* Attach to the child process. */ attach_child(child); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal the child to exit. */ close(cpipe[0]); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a parent process "sees" the exit of a debugged process only * after the debugger has seen it. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc) { pid_t child, debugger, wpid; int cpipe[2], dpipe[2], status; char c; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); _exit(1); } close(cpipe[1]); REQUIRE_EQ(pipe(dpipe), 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE_EQ(read(dpipe[1], &c, sizeof(c)), 0); wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Release the child. */ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); REQUIRE_EQ(read(cpipe[0], &c, sizeof(c)), 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, 0); /* Signal the debugger to wait for the child. */ close(dpipe[0]); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); REQUIRE_EQ(wpid, debugger); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); } /* * Verify that a parent process "sees" the exit of a debugged process * only after a non-direct-child debugger has seen it. In particular, * various wait() calls in the parent must avoid failing with ESRCH by * checking the parent's orphan list for the debugee. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc) { pid_t child, debugger, fpid, wpid; int cpipe[2], dpipe[2], status; char c; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); _exit(1); } close(cpipe[1]); REQUIRE_EQ(pipe(dpipe), 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger parent. */ /* * Fork again and drop the debugger parent so that the * debugger is not a child of the main parent. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(2); /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE_EQ(read(dpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger parent process to exit. */ wpid = waitpid(debugger, &status, 0); REQUIRE_EQ(wpid, debugger); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); /* A WNOHANG wait here should see the non-exited child. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, 0); /* Wait for the debugger to attach to the child. */ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Release the child. */ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); REQUIRE_EQ(read(cpipe[0], &c, sizeof(c)), 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, 0); /* Signal the debugger to wait for the child. */ REQUIRE_EQ(write(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Wait for the debugger. */ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), 0); close(dpipe[0]); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); } /* * Make sure that we can collect the exit status of an orphaned process. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_exits_before_child); ATF_TC_BODY(ptrace__parent_exits_before_child, tc) { ssize_t n; int cpipe1[2], cpipe2[2], gcpipe[2], status; pid_t child, gchild; REQUIRE_EQ(pipe(cpipe1), 0); REQUIRE_EQ(pipe(cpipe2), 0); REQUIRE_EQ(pipe(gcpipe), 0); REQUIRE_EQ(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL), 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { CHILD_REQUIRE((gchild = fork()) != -1); if (gchild == 0) { status = 1; do { n = read(gcpipe[0], &status, sizeof(status)); } while (n == -1 && errno == EINTR); _exit(status); } CHILD_REQUIRE_EQ(write(cpipe1[1], &gchild, sizeof(gchild)), (ssize_t)sizeof(gchild)); CHILD_REQUIRE_EQ(read(cpipe2[0], &status, sizeof(status)), (ssize_t)sizeof(status)); _exit(status); } REQUIRE_EQ(read(cpipe1[0], &gchild, sizeof(gchild)), (ssize_t)sizeof(gchild)); REQUIRE_EQ(ptrace(PT_ATTACH, gchild, NULL, 0), 0); status = 0; REQUIRE_EQ(write(cpipe2[1], &status, sizeof(status)), (ssize_t)sizeof(status)); REQUIRE_EQ(waitpid(child, &status, 0), child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); status = 0; REQUIRE_EQ(write(gcpipe[1], &status, sizeof(status)), (ssize_t)sizeof(status)); REQUIRE_EQ(waitpid(gchild, &status, 0), gchild); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(ptrace(PT_DETACH, gchild, (caddr_t)1, 0), 0); REQUIRE_EQ(waitpid(gchild, &status, 0), gchild); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); REQUIRE_EQ(close(cpipe1[0]), 0); REQUIRE_EQ(close(cpipe1[1]), 0); REQUIRE_EQ(close(cpipe2[0]), 0); REQUIRE_EQ(close(cpipe2[1]), 0); REQUIRE_EQ(close(gcpipe[0]), 0); REQUIRE_EQ(close(gcpipe[1]), 0); } /* * The parent process should always act the same regardless of how the * debugger is attached to it. */ static __dead2 void follow_fork_parent(bool use_vfork) { pid_t fpid, wpid; int status; if (use_vfork) CHILD_REQUIRE((fpid = vfork()) != -1); else CHILD_REQUIRE((fpid = fork()) != -1); if (fpid == 0) /* Child */ _exit(2); wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE_EQ(wpid, fpid); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE_EQ(WEXITSTATUS(status), 2); _exit(1); } /* * Helper routine for follow fork tests. This waits for two stops * that report both "sides" of a fork. It returns the pid of the new * child process. */ static pid_t handle_fork_events(pid_t parent, struct ptrace_lwpinfo *ppl) { struct ptrace_lwpinfo pl; bool fork_reported[2]; pid_t child, wpid; int i, status; fork_reported[0] = false; fork_reported[1] = false; child = -1; /* * Each process should report a fork event. The parent should * report a PL_FLAG_FORKED event, and the child should report * a PL_FLAG_CHILD event. */ for (i = 0; i < 2; i++) { wpid = wait(&status); ATF_REQUIRE(wpid > 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != 0); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != (PL_FLAG_FORKED | PL_FLAG_CHILD)); if (pl.pl_flags & PL_FLAG_CHILD) { ATF_REQUIRE(wpid != parent); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(!fork_reported[1]); if (child == -1) child = wpid; else REQUIRE_EQ(child, wpid); if (ppl != NULL) ppl[1] = pl; fork_reported[1] = true; } else { REQUIRE_EQ(wpid, parent); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(!fork_reported[0]); if (child == -1) child = pl.pl_child_pid; else REQUIRE_EQ(child, pl.pl_child_pid); if (ppl != NULL) ppl[0] = pl; fork_reported[0] = true; } } return (child); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached); ATF_TC_BODY(ptrace__follow_fork_both_attached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached); ATF_TC_BODY(ptrace__follow_fork_child_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the grandchild now, only the * child. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached); ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. * * Even though the child process is detached, it is still a * child of the debugger, so it will still report it's exit * after the grandchild. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void attach_fork_parent(int cpipe[2]) { pid_t fpid; close(cpipe[0]); /* Double-fork to disassociate from the debugger. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(3); /* Send the pid of the disassociated child to the debugger. */ fpid = getpid(); CHILD_REQUIRE_EQ(write(cpipe[1], &fpid, sizeof(fpid)), (ssize_t)sizeof(fpid)); /* Wait for the debugger to attach. */ CHILD_REQUIRE_EQ(read(cpipe[1], &fpid, sizeof(fpid)), 0); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 3); /* Read the pid of the fork parent. */ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])), (ssize_t)sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The fork parent can't exit until the child reports status, * so the child should report its exit first to the debugger. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 3); /* Read the pid of the fork parent. */ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])), (ssize_t)sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the child now, only the fork * parent. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. In this test the parent * that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 3); /* Read the pid of the fork parent. */ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])), (ssize_t)sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the fork parent now, only * the child. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a child process does not see an unrelated debugger as its * parent but sees its original parent process. */ ATF_TC_WITHOUT_HEAD(ptrace__getppid); ATF_TC_BODY(ptrace__getppid, tc) { pid_t child, debugger, ppid, wpid; int cpipe[2], dpipe[2], status; char c; REQUIRE_EQ(pipe(cpipe), 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Report the parent PID to the parent. */ ppid = getppid(); CHILD_REQUIRE_EQ(write(cpipe[1], &ppid, sizeof(ppid)), (ssize_t)sizeof(ppid)); _exit(1); } close(cpipe[1]); REQUIRE_EQ(pipe(dpipe), 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Wait for traced child to exit. */ wpid = waitpid(child, &status, 0); CHILD_REQUIRE_EQ(wpid, child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Release the child. */ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c)); /* Read the parent PID from the child. */ REQUIRE_EQ(read(cpipe[0], &ppid, sizeof(ppid)), (ssize_t)sizeof(ppid)); close(cpipe[0]); REQUIRE_EQ(ppid, getpid()); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); REQUIRE_EQ(wpid, debugger); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via fork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_fork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); REQUIRE_EQ(pl[0].pl_syscall_code, (unsigned)SYS_fork); REQUIRE_EQ(pl[0].pl_syscall_code, pl[1].pl_syscall_code); REQUIRE_EQ(pl[0].pl_syscall_narg, pl[1].pl_syscall_narg); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via vfork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_vfork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_vfork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); REQUIRE_EQ(pl[0].pl_syscall_code, (unsigned)SYS_vfork); REQUIRE_EQ(pl[0].pl_syscall_code, pl[1].pl_syscall_code); REQUIRE_EQ(pl[0].pl_syscall_narg, pl[1].pl_syscall_narg); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void * simple_thread(void *arg __unused) { pthread_exit(NULL); } static __dead2 void simple_thread_main(void) { pthread_t thread; CHILD_REQUIRE_EQ(pthread_create(&thread, NULL, simple_thread, NULL), 0); CHILD_REQUIRE_EQ(pthread_join(thread, NULL), 0); exit(1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * thread reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_thread); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t mainlwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); mainlwp = pl.pl_lwpid; /* * Continue the child ignoring the SIGSTOP and tracing all * system call exits. */ ATF_REQUIRE(ptrace(PT_TO_SCX, fpid, (caddr_t)1, 0) != -1); /* * Wait for the new thread to arrive. pthread_create() might * invoke any number of system calls. For now we just wait * for the new thread to arrive and make sure it reports a * valid system call code. If ptrace grows thread event * reporting then this test can be made more precise. */ for (;;) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl.pl_syscall_code != 0); if (pl.pl_lwpid != mainlwp) /* New thread seen. */ break; REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } /* Wait for the child to exit. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); for (;;) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFEXITED(status)) break; ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that the expected LWP events are reported for a child thread. */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events); ATF_TC_BODY(ptrace__lwp_events, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)), (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next event should be for the child thread's death. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)), (PL_FLAG_EXITED | PL_FLAG_SCE)); REQUIRE_EQ(pl.pl_lwpid, lwps[1]); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void * exec_thread(void *arg __unused) { execl("/usr/bin/true", "true", NULL); exit(127); } static __dead2 void exec_thread_main(void) { pthread_t thread; CHILD_REQUIRE_EQ(pthread_create(&thread, NULL, exec_thread, NULL), 0); for (;;) sleep(60); exit(1); } /* * Verify that the expected LWP events are reported for a multithreaded * process that calls execve(2). */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events_exec); ATF_TC_BODY(ptrace__lwp_events_exec, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)), (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next event should be for the main thread's death due to * single threading from execve(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)), (PL_FLAG_EXITED)); REQUIRE_EQ(pl.pl_lwpid, lwps[0]); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)), (PL_FLAG_EXEC | PL_FLAG_SCX)); REQUIRE_EQ(pl.pl_lwpid, lwps[1]); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void handler(int sig __unused) { } static void signal_main(void) { signal(SIGINFO, handler); raise(SIGINFO); exit(0); } /* * Verify that the expected ptrace event is reported for a signal. */ ATF_TC_WITHOUT_HEAD(ptrace__siginfo); ATF_TC_BODY(ptrace__siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); signal_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next event should be for the SIGINFO. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGINFO); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_code, SI_LWP); REQUIRE_EQ(pl.pl_siginfo.si_pid, wpid); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that the expected ptrace events are reported for PTRACE_EXEC. */ ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_disable); ATF_TC_BODY(ptrace__ptrace_exec_disable, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); events = 0; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_enable); ATF_TC_BODY(ptrace__ptrace_exec_enable, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); events = PTRACE_EXEC; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)), (PL_FLAG_EXEC | PL_FLAG_SCX)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__event_mask); ATF_TC_BODY(ptrace__event_mask, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(0); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* PT_FOLLOW_FORK should toggle the state of PTRACE_FORK. */ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_FORK); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_FORK)); /* PT_LWP_EVENTS should toggle the state of PTRACE_LWP. */ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_LWP); ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_LWP)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that the expected ptrace events are reported for PTRACE_VFORK. */ ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork); ATF_TC_BODY(ptrace__ptrace_vfork, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); events |= PTRACE_VFORK; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); /* The next event should report the end of the vfork. */ wpid = wait(&status); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_VFORK_DONE) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); wpid = wait(&status); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork_follow); ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, children[0], (caddr_t)&events, sizeof(events)) == 0); events |= PTRACE_FORK | PTRACE_VFORK; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, children[0], (caddr_t)&events, sizeof(events)) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORKED) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = waitpid(children[1], &status, 0); REQUIRE_EQ(wpid, children[1]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); /* * The child should report it's vfork() completion before it * exits. */ wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl[0], sizeof(pl[0])) != -1); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORK_DONE) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); wpid = wait(&status); REQUIRE_EQ(wpid, children[0]); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #ifdef HAVE_BREAKPOINT /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped due to a breakpoint trap. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_breakpoint); ATF_TC_BODY(ptrace__PT_KILL_breakpoint, tc) { pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); breakpoint(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The second wait() should report hitting the breakpoint. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); /* Kill the child process. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #endif /* HAVE_BREAKPOINT */ /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped inside of a system call. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_system_call); ATF_TC_BODY(ptrace__PT_KILL_system_call, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Kill the child process. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that no more events are reported after PT_KILL except for the * process exit when killing a multithreaded process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_threads); ATF_TC_BODY(ptrace__PT_KILL_threads, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t main_lwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)), (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != main_lwp); /* Kill the child process. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void * mask_usr1_thread(void *arg) { pthread_barrier_t *pbarrier; sigset_t sigmask; pbarrier = (pthread_barrier_t*)arg; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR1); CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(pbarrier); for (;;) sleep(60); return (NULL); } /* * Verify that the SIGKILL from PT_KILL takes priority over other signals * and prevents spurious stops due to those other signals. */ ATF_TC(ptrace__PT_KILL_competing_signal); ATF_TC_HEAD(ptrace__PT_KILL_competing_signal, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__PT_KILL_competing_signal, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE_EQ(cpuset(&setid), 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); trace_me(); for (;;) sleep(60); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Send a signal that only the second thread can handle. */ REQUIRE_EQ(kill(fpid, SIGUSR2), 0); /* The second wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); /* Send a signal that only the first thread can handle. */ REQUIRE_EQ(kill(fpid, SIGUSR1), 0); /* Replace the SIGUSR2 with a kill. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL (not the SIGUSR signal). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that the SIGKILL from PT_KILL takes priority over other stop events * and prevents spurious stops caused by those events. */ ATF_TC(ptrace__PT_KILL_competing_stop); ATF_TC_HEAD(ptrace__PT_KILL_competing_stop, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__PT_KILL_competing_stop, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; lwpid_t main_lwp; struct ptrace_lwpinfo pl; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE_EQ(cpuset(&setid), 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); /* Sync up with the test before doing the getpid(). */ raise(SIGSTOP); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* * Continue until child is done with setup, which is indicated with * SIGSTOP. Ignore system calls in the meantime. */ for (;;) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); if (WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); } else { REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); break; } REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); } /* Proceed, allowing main thread to hit syscall entry for getpid(). */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_lwpid, main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Prevent the main thread from hitting its syscall exit for now. */ REQUIRE_EQ(ptrace(PT_SUSPEND, main_lwp, 0, 0), 0); /* * Proceed, allowing second thread to hit syscall exit for * pthread_barrier_wait(). */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_lwpid != main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Send a signal that only the second thread can handle. */ REQUIRE_EQ(kill(fpid, SIGUSR2), 0); REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The next wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); /* Allow the main thread to try to finish its system call. */ REQUIRE_EQ(ptrace(PT_RESUME, main_lwp, 0, 0), 0); /* * At this point, the main thread is in the middle of a system call and * has been resumed. The second thread has taken a SIGUSR2 which will * be replaced with a SIGKILL below. The main thread will get to run * first. It should notice the kill request (even though the signal * replacement occurred in the other thread) and exit accordingly. It * should not stop for the system call exit event. */ /* Replace the SIGUSR2 with a kill. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL (not a syscall exit). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void sigusr1_handler(int sig) { CHILD_REQUIRE_EQ(sig, SIGUSR1); _exit(2); } /* * Verify that even if the signal queue is full for a child process, * a PT_KILL will kill the process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_KILL_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) REQUIRE_EQ(kill(fpid, SIGUSR1), 0); /* Kill the child process. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that when stopped at a system call entry, a signal can be * requested with PT_CONTINUE which will be delivered once the system * call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } else { ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); break; } } wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void sigusr1_counting_handler(int sig) { static int counter = 0; CHILD_REQUIRE_EQ(sig, SIGUSR1); counter++; if (counter == 2) _exit(2); } /* * Verify that, when continuing from a stop at system call entry and exit, * a signal can be requested from both stops, and both will be delivered when * the system call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The third wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Continue the child process with a signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } else { ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); break; } } wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that even if the signal queue is full for a child process, * a PT_CONTINUE with a signal will not result in loss of that signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR); ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) REQUIRE_EQ(kill(fpid, SIGUSR2), 0); /* Continue with signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); for (;;) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFSTOPPED(status)) { REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } else { /* * The last wait() should report normal _exit from the * SIGUSR1 handler. */ ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); break; } } wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static sem_t sigusr1_sem; static int got_usr1; static void sigusr1_sempost_handler(int sig __unused) { got_usr1++; CHILD_REQUIRE_EQ(sem_post(&sigusr1_sem), 0); } /* * Verify that even if the signal queue is full for a child process, * and the signal is masked, a PT_CONTINUE with a signal will not * result in loss of that signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; int max_pending_per_proc; size_t len; int i; sigset_t sigmask; ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR); REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0); CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0); CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0); trace_me(); CHILD_REQUIRE_EQ(got_usr1, 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0); /* Wait to receive the SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE_EQ(got_usr1, 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) REQUIRE_EQ(kill(fpid, SIGUSR2), 0); /* Continue with signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* Collect and ignore all of the SIGUSR2. */ for (i = 0; i < max_pending_per_proc; ++i) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } /* Now our PT_CONTINUE'd SIGUSR1 should cause a stop after unmask. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1); /* Continue the child, ignoring the SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last wait() should report exit after receiving SIGUSR1. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that, after stopping due to a signal, that signal can be * replaced with another signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_change_sig); ATF_TC_BODY(ptrace__PT_CONTINUE_change_sig, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); sleep(20); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Send a signal without ptrace. */ REQUIRE_EQ(kill(fpid, SIGINT), 0); /* The second wait() should report a SIGINT was received. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGINT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGINT); /* Continue the child process with a different signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTERM), 0); /* * The last wait() should report having died due to the new * signal, SIGTERM. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGTERM); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a signal can be passed through to the child even when there * was no true signal originally. Such cases arise when a SIGTRAP is * invented for e.g, system call stops. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry, tc) { struct ptrace_lwpinfo pl; struct rlimit rl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); /* SIGTRAP expected to cause exit on syscall entry. */ rl.rlim_cur = rl.rlim_max = 0; REQUIRE_EQ(setrlimit(RLIMIT_CORE, &rl), 0); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a SIGTRAP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTRAP), 0); for (;;) { /* * The last wait() should report exit due to SIGTRAP. In the * meantime, catch and proceed past any syscall stops. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } else { ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGTRAP); break; } } wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * A mixed bag PT_CONTINUE with signal test. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_mix); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_mix, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue with the first SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The next wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Send an ABRT without ptrace. */ REQUIRE_EQ(kill(fpid, SIGABRT), 0); /* Continue normally. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next wait() should report the SIGABRT. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT); /* Continue, replacing the SIGABRT with another SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } else { ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 2); break; } } wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify a signal delivered by ptrace is noticed by kevent(2). */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_kqueue, tc) { pid_t fpid, wpid; int status, kq, nevents; struct kevent kev; ATF_REQUIRE(signal(SIGUSR1, SIG_IGN) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE((kq = kqueue()) > 0); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); CHILD_REQUIRE_EQ(kevent(kq, &kev, 1, NULL, 0, NULL), 0); trace_me(); for (;;) { nevents = kevent(kq, NULL, 0, &kev, 1, NULL); if (nevents == -1 && errno == EINTR) continue; CHILD_REQUIRE(nevents > 0); CHILD_REQUIRE_EQ(kev.filter, EVFILT_SIGNAL); CHILD_REQUIRE_EQ(kev.ident, (uintptr_t)SIGUSR1); break; } exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue with the SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* * The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void * signal_thread(void *arg) { int err; sigset_t sigmask; pthread_barrier_t *pbarrier = (pthread_barrier_t*)arg; /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free our companion thread from the barrier. */ pthread_barrier_wait(pbarrier); /* * Swap ignore duties; the next SIGUSR1 should go to the * other thread. */ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0); CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0); CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(pbarrier); /* Wait until our companion has received its SIGUSR1. */ pthread_barrier_wait(pbarrier); return (NULL); } /* * Verify that a traced process with blocked signal received the * signal from kill() once unmasked. */ ATF_TC_WITHOUT_HEAD(ptrace__killed_with_sigmask); ATF_TC_BODY(ptrace__killed_with_sigmask, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; sigset_t sigmask; REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0); CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0); CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0); trace_me(); CHILD_REQUIRE_EQ(got_usr1, 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0); /* Wait to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE_EQ(got_usr1, 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGSTOP); /* Send blocked SIGUSR1 which should cause a stop. */ REQUIRE_EQ(kill(fpid, SIGUSR1), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next wait() should report the kill(SIGUSR1) was received. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1); /* Continue the child, allowing in the SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that a traced process with blocked signal received the * signal from PT_CONTINUE once unmasked. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigmask); ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigmask, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; sigset_t sigmask; REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0); CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0); CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0); trace_me(); CHILD_REQUIRE_EQ(got_usr1, 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0); /* Wait to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE_EQ(got_usr1, 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGSTOP); /* Continue the child replacing SIGSTOP with SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The next wait() should report the SIGUSR1 was received. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1); /* Continue the child, ignoring the SIGUSR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that if ptrace stops due to a signal but continues with * a different signal that the new signal is routed to a thread * that can accept it, and that the thread is awakened by the signal * in a timely manner. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_thread_sigmask); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_thread_sigmask, tc) { pid_t fpid, wpid; int status, err; pthread_t t; sigset_t sigmask; pthread_barrier_t barrier; REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0); REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE_EQ(pthread_create(&t, NULL, signal_thread, (void *)&barrier), 0); /* The other thread should receive the first SIGUSR1. */ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0); CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0); CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0); trace_me(); /* Wait until other thread has received its SIGUSR1. */ pthread_barrier_wait(&barrier); /* * Swap ignore duties; the next SIGUSR1 should go to this * thread. */ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL), 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(&barrier); /* * Sync up with test code; we're ready for the next SIGUSR1 * now. */ raise(SIGSTOP); /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free the other thread from the barrier. */ pthread_barrier_wait(&barrier); CHILD_REQUIRE_EQ(pthread_join(t, NULL), 0); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * Send a signal without ptrace that either thread will accept (USR2, * in this case). */ REQUIRE_EQ(kill(fpid, SIGUSR2), 0); /* The second wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); /* Continue the child, changing the signal to USR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The next wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); REQUIRE_EQ(kill(fpid, SIGUSR2), 0); /* The next wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGUSR2); /* Continue the child, changing the signal to USR1. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } +/* + * Verify that PT_GETREGSET returns registers and PT_SETREGSET updates them. + */ +ATF_TC_WITHOUT_HEAD(ptrace__PT_REGSET); +ATF_TC_BODY(ptrace__PT_REGSET, tc) +{ + struct prstatus prstatus; + struct iovec vec; + pid_t child, wpid; + int status; + + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + trace_me(); + exit(1); + } + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(child, &status, 0); + REQUIRE_EQ(wpid, child); + ATF_REQUIRE(WIFSTOPPED(status)); + REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); + + /* Check the size is returned when vec.iov_base is NULL */ + vec.iov_base = NULL; + vec.iov_len = 0; + ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) != + -1); + ATF_REQUIRE(vec.iov_len == sizeof(prstatus)); + ATF_REQUIRE(vec.iov_base == NULL); + + /* Read the registers. */ + memset(&prstatus, 0, sizeof(prstatus)); + vec.iov_base = &prstatus; + ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) != + -1); + ATF_REQUIRE(vec.iov_len == sizeof(prstatus)); + ATF_REQUIRE(vec.iov_base == &prstatus); + ATF_REQUIRE(prstatus.pr_statussz == sizeof(prstatus)); + + /* Write the registers back. */ + ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) != + -1); + + REQUIRE_EQ(ptrace(PT_CONTINUE, child, (caddr_t)1, 0), 0); + + /* The second wait() should report the exit status. */ + wpid = waitpid(child, &status, 0); + REQUIRE_EQ(wpid, child); + ATF_REQUIRE(WIFEXITED(status)); + REQUIRE_EQ(WEXITSTATUS(status), 1); + + /* The child should no longer exist. */ + wpid = waitpid(child, &status, 0); + REQUIRE_EQ(wpid, -1); + REQUIRE_EQ(errno, ECHILD); +} + static void * raise_sigstop_thread(void *arg __unused) { raise(SIGSTOP); return NULL; } static void * sleep_thread(void *arg __unused) { sleep(60); return NULL; } static void terminate_with_pending_sigstop(bool sigstop_from_main_thread) { pid_t fpid, wpid; int status, i; cpuset_t setmask; cpusetid_t setid; pthread_t t; /* * Become the reaper for this process tree. We need to be able to check * that both child and grandchild have died. */ REQUIRE_EQ(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL), 0); fpid = fork(); ATF_REQUIRE(fpid >= 0); if (fpid == 0) { fpid = fork(); CHILD_REQUIRE(fpid >= 0); if (fpid == 0) { trace_me(); /* Pin to CPU 0 to serialize thread execution. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); CHILD_REQUIRE_EQ(cpuset(&setid), 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); if (sigstop_from_main_thread) { /* * We expect the SIGKILL sent when our parent * dies to be delivered to the new thread. * Raise the SIGSTOP in this thread so the * threads compete. */ CHILD_REQUIRE(pthread_create(&t, NULL, sleep_thread, NULL) == 0); raise(SIGSTOP); } else { /* * We expect the SIGKILL to be delivered to * this thread. After creating the new thread, * just get off the CPU so the other thread can * raise the SIGSTOP. */ CHILD_REQUIRE(pthread_create(&t, NULL, raise_sigstop_thread, NULL) == 0); sleep(60); } exit(0); } /* First stop is trace_me() immediately after fork. */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE_EQ(wpid, fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); CHILD_REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Second stop is from the raise(SIGSTOP). */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE_EQ(wpid, fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* * Terminate tracing process without detaching. Our child * should be killed. */ exit(0); } /* * We should get a normal exit from our immediate child and a SIGKILL * exit from our grandchild. The latter case is the interesting one. * Our grandchild should not have stopped due to the SIGSTOP that was * left dangling when its parent died. */ for (i = 0; i < 2; ++i) { wpid = wait(&status); if (wpid == fpid) { ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); } else { ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); } } } /* * These two tests ensure that if the tracing process exits without detaching * just after the child received a SIGSTOP, the child is cleanly killed and * doesn't go to sleep due to the SIGSTOP. The parent's death will send a * SIGKILL to the child. If the SIGKILL and the SIGSTOP are handled by * different threads, the SIGKILL must win. There are two variants of this * test, designed to catch the case where the SIGKILL is delivered to the * younger thread (the first test) and the case where the SIGKILL is delivered * to the older thread (the second test). This behavior has changed in the * past, so make no assumption. */ ATF_TC(ptrace__parent_terminate_with_pending_sigstop1); ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop1, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop1, tc) { terminate_with_pending_sigstop(true); } ATF_TC(ptrace__parent_terminate_with_pending_sigstop2); ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop2, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop2, tc) { terminate_with_pending_sigstop(false); } /* * Verify that after ptrace() discards a SIGKILL signal, the event mask * is not modified. */ ATF_TC_WITHOUT_HEAD(ptrace__event_mask_sigkill_discard); ATF_TC_BODY(ptrace__event_mask_sigkill_discard, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, event_mask, new_event_mask; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); raise(SIGSTOP); exit(0); } /* The first wait() should report the stop from trace_me(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Set several unobtrusive event bits. */ event_mask = PTRACE_EXEC | PTRACE_FORK | PTRACE_LWP; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, wpid, (caddr_t)&event_mask, sizeof(event_mask)) == 0); /* Send a SIGKILL without using ptrace. */ REQUIRE_EQ(kill(fpid, SIGKILL), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next stop should be due to the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGKILL); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGKILL); /* Continue the child ignoring the SIGKILL. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Check the current event mask. It should not have changed. */ new_event_mask = 0; ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, wpid, (caddr_t)&new_event_mask, sizeof(new_event_mask)) == 0); REQUIRE_EQ(event_mask, new_event_mask); /* Continue the child to let it exit. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } static void * flock_thread(void *arg) { int fd; fd = *(int *)arg; (void)flock(fd, LOCK_EX); (void)flock(fd, LOCK_UN); return (NULL); } /* * Verify that PT_ATTACH will suspend threads sleeping in an SBDRY section. * We rely on the fact that the lockf implementation sets SBDRY before blocking * on a lock. This is a regression test for r318191. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_ATTACH_with_SBDRY_thread); ATF_TC_BODY(ptrace__PT_ATTACH_with_SBDRY_thread, tc) { pthread_barrier_t barrier; pthread_barrierattr_t battr; char tmpfile[64]; pid_t child, wpid; int error, fd, i, status; REQUIRE_EQ(pthread_barrierattr_init(&battr), 0); ATF_REQUIRE(pthread_barrierattr_setpshared(&battr, PTHREAD_PROCESS_SHARED) == 0); REQUIRE_EQ(pthread_barrier_init(&barrier, &battr, 2), 0); (void)snprintf(tmpfile, sizeof(tmpfile), "./ptrace.XXXXXX"); fd = mkstemp(tmpfile); ATF_REQUIRE(fd >= 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { pthread_t t[2]; int cfd; error = pthread_barrier_wait(&barrier); if (error != 0 && error != PTHREAD_BARRIER_SERIAL_THREAD) _exit(1); cfd = open(tmpfile, O_RDONLY); if (cfd < 0) _exit(1); /* * We want at least two threads blocked on the file lock since * the SIGSTOP from PT_ATTACH may kick one of them out of * sleep. */ if (pthread_create(&t[0], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_create(&t[1], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_join(t[0], NULL) != 0) _exit(1); if (pthread_join(t[1], NULL) != 0) _exit(1); _exit(0); } REQUIRE_EQ(flock(fd, LOCK_EX), 0); error = pthread_barrier_wait(&barrier); ATF_REQUIRE(error == 0 || error == PTHREAD_BARRIER_SERIAL_THREAD); /* * Give the child some time to block. Is there a better way to do this? */ sleep(1); /* * Attach and give the child 3 seconds to stop. */ REQUIRE_EQ(ptrace(PT_ATTACH, child, NULL, 0), 0); for (i = 0; i < 3; i++) { wpid = waitpid(child, &status, WNOHANG); if (wpid == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) break; sleep(1); } ATF_REQUIRE_MSG(i < 3, "failed to stop child process after PT_ATTACH"); REQUIRE_EQ(ptrace(PT_DETACH, child, NULL, 0), 0); REQUIRE_EQ(flock(fd, LOCK_UN), 0); REQUIRE_EQ(unlink(tmpfile), 0); REQUIRE_EQ(close(fd), 0); } static void sigusr1_step_handler(int sig) { CHILD_REQUIRE_EQ(sig, SIGUSR1); raise(SIGABRT); } /* * Verify that PT_STEP with a signal invokes the signal before * stepping the next instruction (and that the next instruction is * stepped correctly). */ ATF_TC_WITHOUT_HEAD(ptrace__PT_STEP_with_signal); ATF_TC_BODY(ptrace__PT_STEP_with_signal, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); signal(SIGUSR1, sigusr1_step_handler); raise(SIGABRT); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next stop should report the SIGABRT in the child body. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT); /* Step the child process inserting SIGUSR1. */ REQUIRE_EQ(ptrace(PT_STEP, fpid, (caddr_t)1, SIGUSR1), 0); /* The next stop should report the SIGABRT in the signal handler. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT); /* Continue the child process discarding the signal. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next stop should report a trace trap from PT_STEP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP); REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_TRACE); /* Continue the child to let it exit. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #ifdef HAVE_BREAKPOINT /* * Verify that a SIGTRAP event with the TRAP_BRKPT code is reported * for a breakpoint trap. */ ATF_TC_WITHOUT_HEAD(ptrace__breakpoint_siginfo); ATF_TC_BODY(ptrace__breakpoint_siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); breakpoint(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The second wait() should report hitting the breakpoint. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP); REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT); /* Kill the child process. */ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSIGNALED(status)); REQUIRE_EQ(WTERMSIG(status), SIGKILL); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #endif /* HAVE_BREAKPOINT */ /* * Verify that a SIGTRAP event with the TRAP_TRACE code is reported * for a single-step trap from PT_STEP. */ ATF_TC_WITHOUT_HEAD(ptrace__step_siginfo); ATF_TC_BODY(ptrace__step_siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Step the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_STEP, fpid, (caddr_t)1, 0), 0); /* The second wait() should report a single-step trap. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP); REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_TRACE); /* Continue the child process. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK) static void * continue_thread(void *arg __unused) { breakpoint(); return (NULL); } static __dead2 void continue_thread_main(void) { pthread_t threads[2]; CHILD_REQUIRE(pthread_create(&threads[0], NULL, continue_thread, NULL) == 0); CHILD_REQUIRE(pthread_create(&threads[1], NULL, continue_thread, NULL) == 0); CHILD_REQUIRE_EQ(pthread_join(threads[0], NULL), 0); CHILD_REQUIRE_EQ(pthread_join(threads[1], NULL), 0); exit(1); } /* * Ensure that PT_CONTINUE clears the status of the thread that * triggered the stop even if a different thread's LWP was passed to * PT_CONTINUE. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_different_thread); ATF_TC_BODY(ptrace__PT_CONTINUE_different_thread, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; bool hit_break[2]; struct reg reg; int i, j, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); continue_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0); /* Continue the child ignoring the SIGSTOP. */ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* One of the new threads should report it's birth. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)), (PL_FLAG_BORN | PL_FLAG_SCX)); lwps[0] = pl.pl_lwpid; /* * Suspend this thread to ensure both threads are alive before * hitting the breakpoint. */ ATF_REQUIRE(ptrace(PT_SUSPEND, lwps[0], NULL, 0) != -1); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* Second thread should report it's birth. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)), (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; /* Resume both threads waiting for breakpoint events. */ hit_break[0] = hit_break[1] = false; ATF_REQUIRE(ptrace(PT_RESUME, lwps[0], NULL, 0) != -1); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* One thread should report a breakpoint. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP); REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT); if (pl.pl_lwpid == lwps[0]) i = 0; else i = 1; hit_break[i] = true; ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); SKIP_BREAK(®); ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); /* * Resume both threads but pass the other thread's LWPID to * PT_CONTINUE. */ REQUIRE_EQ(ptrace(PT_CONTINUE, lwps[i ^ 1], (caddr_t)1, 0), 0); /* * Will now get two thread exit events and one more breakpoint * event. */ for (j = 0; j < 3; j++) { wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); if (pl.pl_lwpid == lwps[0]) i = 0; else i = 1; ATF_REQUIRE_MSG(lwps[i] != 0, "event for exited thread"); if (pl.pl_flags & PL_FLAG_EXITED) { ATF_REQUIRE_MSG(hit_break[i], "exited thread did not report breakpoint"); lwps[i] = 0; } else { ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP); REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT); ATF_REQUIRE_MSG(!hit_break[i], "double breakpoint event"); hit_break[i] = true; ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); SKIP_BREAK(®); ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); } REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); } /* Both threads should have exited. */ REQUIRE_EQ(lwps[0], 0); REQUIRE_EQ(lwps[1], 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } #endif /* * Verify that PT_LWPINFO doesn't return stale siginfo. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_LWPINFO_stale_siginfo); ATF_TC_BODY(ptrace__PT_LWPINFO_stale_siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); raise(SIGABRT); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The next stop should report the SIGABRT in the child body. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT); /* * Continue the process ignoring the signal, but enabling * syscall traps. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* * The next stop should report a system call entry from * exit(). PL_FLAGS_SI should not be set. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); REQUIRE_EQ((pl.pl_flags & PL_FLAG_SI), 0); /* Disable syscall tracing and continue the child to let it exit. */ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); events &= ~PTRACE_SYSCALL; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * A simple test of PT_GET_SC_ARGS and PT_GET_SC_RET. */ ATF_TC_WITHOUT_HEAD(ptrace__syscall_args); ATF_TC_BODY(ptrace__syscall_args, tc) { struct ptrace_lwpinfo pl; struct ptrace_sc_ret psr; pid_t fpid, wpid; register_t args[2]; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); kill(getpid(), 0); /* Close a fd that should not exist. */ close(12345); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* * Continue the process ignoring the signal, but enabling * syscall traps. */ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall entry from getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_getpid); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall exit from getpid(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_getpid); ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr, sizeof(psr)) != -1); REQUIRE_EQ(psr.sr_error, 0); REQUIRE_EQ(psr.sr_retval[0], wpid); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall entry from kill(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_kill); REQUIRE_EQ(pl.pl_syscall_narg, 2u); ATF_REQUIRE(ptrace(PT_GET_SC_ARGS, wpid, (caddr_t)args, sizeof(args)) != -1); REQUIRE_EQ(args[0], wpid); REQUIRE_EQ(args[1], 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall exit from kill(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_kill); ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr, sizeof(psr)) != -1); REQUIRE_EQ(psr.sr_error, 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall entry from close(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_close); REQUIRE_EQ(pl.pl_syscall_narg, 1u); ATF_REQUIRE(ptrace(PT_GET_SC_ARGS, wpid, (caddr_t)args, sizeof(args)) != -1); REQUIRE_EQ(args[0], 12345); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* * The next stop should be the syscall exit from close(). */ wpid = waitpid(fpid, &status, 0); REQUIRE_EQ(wpid, fpid); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_close); ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr, sizeof(psr)) != -1); REQUIRE_EQ(psr.sr_error, EBADF); /* Disable syscall tracing and continue the child to let it exit. */ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); events &= ~PTRACE_SYSCALL; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 1); wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Verify that when the process is traced that it isn't reparent * to the init process when we close all process descriptors. */ ATF_TC(ptrace__proc_reparent); ATF_TC_HEAD(ptrace__proc_reparent, tc) { atf_tc_set_md_var(tc, "timeout", "2"); } ATF_TC_BODY(ptrace__proc_reparent, tc) { pid_t traced, debuger, wpid; int pd, status; traced = pdfork(&pd, 0); ATF_REQUIRE(traced >= 0); if (traced == 0) { raise(SIGSTOP); exit(0); } ATF_REQUIRE(pd >= 0); debuger = fork(); ATF_REQUIRE(debuger >= 0); if (debuger == 0) { /* The traced process is reparented to debuger. */ REQUIRE_EQ(ptrace(PT_ATTACH, traced, 0, 0), 0); wpid = waitpid(traced, &status, 0); REQUIRE_EQ(wpid, traced); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); REQUIRE_EQ(close(pd), 0); REQUIRE_EQ(ptrace(PT_DETACH, traced, (caddr_t)1, 0), 0); /* We closed pd so we should not have any child. */ wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); exit(0); } REQUIRE_EQ(close(pd), 0); wpid = waitpid(debuger, &status, 0); REQUIRE_EQ(wpid, debuger); REQUIRE_EQ(WEXITSTATUS(status), 0); /* Check if we still have any child. */ wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); } /* * Ensure that traced processes created with pdfork(2) are visible to * waitid(P_ALL). */ ATF_TC_WITHOUT_HEAD(ptrace__procdesc_wait_child); ATF_TC_BODY(ptrace__procdesc_wait_child, tc) { pid_t child, wpid; int pd, status; child = pdfork(&pd, 0); ATF_REQUIRE(child >= 0); if (child == 0) { trace_me(); (void)raise(SIGSTOP); exit(0); } wpid = waitpid(child, &status, 0); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); wpid = wait(&status); REQUIRE_EQ(wpid, child); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* * If process was created by pdfork, the return code have to * be collected through process descriptor. */ wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); ATF_REQUIRE(close(pd) != -1); } /* * Ensure that traced processes created with pdfork(2) are not visible * after returning to parent - waitid(P_ALL). */ ATF_TC_WITHOUT_HEAD(ptrace__procdesc_reparent_wait_child); ATF_TC_BODY(ptrace__procdesc_reparent_wait_child, tc) { pid_t traced, debuger, wpid; int pd, status; traced = pdfork(&pd, 0); ATF_REQUIRE(traced >= 0); if (traced == 0) { raise(SIGSTOP); exit(0); } ATF_REQUIRE(pd >= 0); /* Wait until the child process has stopped before fork()ing again. */ REQUIRE_EQ(traced, waitpid(traced, &status, WSTOPPED)); debuger = fork(); ATF_REQUIRE(debuger >= 0); if (debuger == 0) { /* The traced process is reparented to debuger. */ REQUIRE_EQ(ptrace(PT_ATTACH, traced, 0, 0), 0); wpid = waitpid(traced, &status, 0); REQUIRE_EQ(wpid, traced); ATF_REQUIRE(WIFSTOPPED(status)); REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); /* Allow process to die. */ REQUIRE_EQ(ptrace(PT_CONTINUE, traced, (caddr_t)1, 0), 0); wpid = waitpid(traced, &status, 0); REQUIRE_EQ(wpid, traced); ATF_REQUIRE(WIFEXITED(status)); REQUIRE_EQ(WEXITSTATUS(status), 0); /* Reparent back to the orginal process. */ REQUIRE_EQ(close(pd), 0); exit(0); } wpid = waitpid(debuger, &status, 0); REQUIRE_EQ(wpid, debuger); REQUIRE_EQ(WEXITSTATUS(status), 0); /* * We have a child but it has a process descriptori * so we should not be able to collect it process. */ wpid = wait(&status); REQUIRE_EQ(wpid, -1); REQUIRE_EQ(errno, ECHILD); REQUIRE_EQ(close(pd), 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me); ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__parent_exits_before_child); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__getppid); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread); ATF_TP_ADD_TC(tp, ptrace__lwp_events); ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec); ATF_TP_ADD_TC(tp, ptrace__siginfo); ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable); ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable); ATF_TP_ADD_TC(tp, ptrace__event_mask); ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork); ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow); #ifdef HAVE_BREAKPOINT ATF_TP_ADD_TC(tp, ptrace__PT_KILL_breakpoint); #endif ATF_TP_ADD_TC(tp, ptrace__PT_KILL_system_call); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_threads); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_signal); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_stop); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_change_sig); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_mix); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TP_ADD_TC(tp, ptrace__killed_with_sigmask); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigmask); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask); + ATF_TP_ADD_TC(tp, ptrace__PT_REGSET); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2); ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard); ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_with_SBDRY_thread); ATF_TP_ADD_TC(tp, ptrace__PT_STEP_with_signal); #ifdef HAVE_BREAKPOINT ATF_TP_ADD_TC(tp, ptrace__breakpoint_siginfo); #endif ATF_TP_ADD_TC(tp, ptrace__step_siginfo); #if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK) ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_different_thread); #endif ATF_TP_ADD_TC(tp, ptrace__PT_LWPINFO_stale_siginfo); ATF_TP_ADD_TC(tp, ptrace__syscall_args); ATF_TP_ADD_TC(tp, ptrace__proc_reparent); ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child); ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child); return (atf_no_error()); }