How can Linux ptrace be unsafe or contain a race condition?
The major problem is that many syscall arguments, like filenames, are passed to the kernel as userspace pointers. Any task that is allowed to run simultaneously and has write access to the memory that the pointer points to can effectively modify these arguments after they are inspected by your supervisor and before the kernel acts on them. By the time the kernel follows the pointer, the pointed-to contents may have been deliberately changed by another schedulable task (process or thread) with access to that memory. For example:
Thread 1 Supervisor Thread 2
-----------------------------------------------------------------------------------------------------
strcpy(filename, "/dev/null");
open(filename, O_RDONLY);
Check filename - OK
strcpy(filename, "/home/user/.ssh/id_rsa");
(in kernel) opens "/home/user/.ssh/id_rsa"
One way to stop this is to disallow calling clone()
with the CLONE_VM
flag, and in addition prevent any creation of writeable MAP_SHARED
memory mappings (or at least keep track of them such that you deny any syscall that tries to directly reference data from such a mapping). You could also copy any such argument into a non-shared bounce-buffer before allowing the syscall to proceed. This will effectively prevent any threaded application from running in the sandbox.
The alternative is to SIGSTOP
every other process in the traced group around every potentially dangerous syscall, wait for them to actually stop, then allow the syscall to proceed. After it returns, you then SIGCONT
them (unless they were already stopped). Needless to say, this may have a significant performance impact.
(There are also analogous problems with syscall arguments that are passed on the stack, and with shared open file tables).
calling ptrace inside a ptraced Linux process
You are indeed seeing a race condition. You can cause it to happen repeatably by putting sleep(1);
immediately before the second fork()
call.
The race condition is caused because process A is not correctly passing signals on to process B. That means that if process B starts tracing process C after process A has started tracing process B, process B never gets the SIGCHLD
signal indicating that process C has stopped, so it can never continue it.
To fix the problem, you just need to fix your SIGCHLD
handler:
static void sigchld_handler(int sig){
int result, status;
pid_t child_pid = wait(&status); // find who send us this SIGCHLD
printf("%d received SIGCHLD on %d\n", getpid(), child_pid);
if (WIFSTOPPED(status))
{
result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
if(result) {
perror("continuing after SIGCHLD");
}
}
}
Preventing processes to execute certain system calls
If you want to do it the ptrace
way, you have some options (and some are really simple). First of all, I recommend you to follow the tutorial explained here. With it you can learn how to know what system calls are being called, and also the basic ptrace
knowledge (don't worry, it's a very short tutorial). The options (that I know) you have are the following:
- The easiest one would be to kill the child, that is this exact code here.
- Secondly you could make the child fail, just by changing the registers with
PTRACE_SETREGS
, putting wrong values in them, and you can also change the return value of the system call if you want (again, withPTRACE_SETREGS
). - Finally you could skip the system call. But for that you should know the address after the system call call, make the intruction register point there and set it (again, with
PTRACE_SETREGS
).
Using ptrace to write a program supervisor in userspace
This looks like a good place to start.
http://www.linuxjournal.com/article/6100
Related Topics
How to Avoid "No Such File or Directory" Error for 'Make Clean' Makefile Target
Bash Not Trapping Interrupts During Rsync/Subshell Exec Statements
Getmodulehandle(Null) on Linux
How to Change the Watchdog Timer in Linux Embedded
Unterminated Address Regex While Using Sed
Is It Good Practice to Use Mkdir as File-Based Locking on Linux
Mod_Rewrite with Relative Path Redirects
How to Use Find -Exec in Cmake Execute_Process
How to Create a Zip File Without Entire Directory Structure
How to Find List of Odbc Drivers Installed on Linux
Xargs Sh -C Skipping the First Argument
How to Retrieve Advertising Payload from Ibeacon/Ble
How to Replace Finding Words with the Different in Each Occurrence in Vi/Vim Editor
Linux: Handling a Segmentation Fault and Getting a Core Dump
Bash: How to Traverse Directory Structure and Execute Commands