| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "debug.h" | |
| 6 #include "sandbox_impl.h" | |
| 7 | |
| 8 namespace playground { | |
| 9 | |
| 10 long Sandbox::sandbox_clone(int flags, char* stack, int* pid, int* ctid, | |
| 11 void* tls, void *wrapper_sp) { | |
| 12 long long tm; | |
| 13 Debug::syscall(&tm, __NR_clone, "Executing handler"); | |
| 14 struct { | |
| 15 int sysnum; | |
| 16 long long cookie; | |
| 17 Clone clone_req; | |
| 18 } __attribute__((packed)) request; | |
| 19 request.sysnum = __NR_clone; | |
| 20 request.cookie = cookie(); | |
| 21 request.clone_req.flags = flags; | |
| 22 request.clone_req.stack = stack; | |
| 23 request.clone_req.pid = pid; | |
| 24 request.clone_req.ctid = ctid; | |
| 25 request.clone_req.tls = tls; | |
| 26 | |
| 27 // TODO(markus): Passing stack == 0 currently does not do the same thing | |
| 28 // that the kernel would do without the sandbox. This is just going to | |
| 29 // cause a crash. We should detect this case, and replace the stack pointer | |
| 30 // with the correct value, instead. | |
| 31 // This is complicated by the fact that we will temporarily be executing | |
| 32 // both threads from the same stack. Some synchronization will be necessary. | |
| 33 // Fortunately, this complication also explains why hardly anybody ever | |
| 34 // does this. | |
| 35 // See trusted_thread.cc for more information. | |
| 36 long rc; | |
| 37 if (stack == 0) { | |
| 38 rc = -EINVAL; | |
| 39 } else { | |
| 40 // Pass along the address on the stack where syscallWrapper() stored the | |
| 41 // original CPU registers. These registers will be restored in the newly | |
| 42 // created thread prior to returning from the wrapped system call. | |
| 43 #if defined(__x86_64__) | |
| 44 memcpy(&request.clone_req.regs64, wrapper_sp, | |
| 45 sizeof(request.clone_req.regs64) + sizeof(void *)); | |
| 46 #elif defined(__i386__) | |
| 47 memcpy(&request.clone_req.regs32, wrapper_sp, | |
| 48 sizeof(request.clone_req.regs32) + sizeof(void *)); | |
| 49 #else | |
| 50 #error Unsupported target platform | |
| 51 #endif | |
| 52 | |
| 53 // In order to unblock the signal mask in the newly created thread and | |
| 54 // after entering Seccomp mode, we have to call sigreturn(). But that | |
| 55 // requires access to a proper stack frame describing a valid signal. | |
| 56 // We trigger a signal now and make sure the stack frame ends up on the | |
| 57 // new stack. Our segv() handler (in sandbox.cc) does that for us. | |
| 58 // See trusted_thread.cc for more details on how threads get created. | |
| 59 // | |
| 60 // In general we rely on the kernel for generating the signal stack | |
| 61 // frame, as the exact binary format has been extended several times over | |
| 62 // the course of the kernel's development. Fortunately, the kernel | |
| 63 // developers treat the initial part of the stack frame as a stable part | |
| 64 // of the ABI. So, we can rely on fixed, well-defined offsets for accessing | |
| 65 // register values and for accessing the signal mask. | |
| 66 #if defined(__x86_64__) | |
| 67 // Red zone compensation. The instrumented system call will remove 128 | |
| 68 // bytes from the thread's stack prior to returning to the original | |
| 69 // call site. | |
| 70 stack -= 128; | |
| 71 request.clone_req.stack = stack; | |
| 72 void *dummy; | |
| 73 asm volatile("mov %%rsp, %%rcx\n" | |
| 74 "mov %3, %%rsp\n" | |
| 75 "int $0\n" | |
| 76 "mov %%rcx, %%rsp\n" | |
| 77 : "=a"(request.clone_req.stack), "=&c"(dummy) | |
| 78 : "a"(__NR_clone + 0xF000), "m"(request.clone_req.stack) | |
| 79 : "memory"); | |
| 80 #elif defined(__i386__) | |
| 81 void *dummy; | |
| 82 asm volatile("mov %%esp, %%ecx\n" | |
| 83 "mov %3, %%esp\n" | |
| 84 "int $0\n" | |
| 85 "mov %%ecx, %%esp\n" | |
| 86 : "=a"(request.clone_req.stack), "=&c"(dummy) | |
| 87 : "a"(__NR_clone + 0xF000), "m"(request.clone_req.stack) | |
| 88 : "memory"); | |
| 89 #else | |
| 90 #error Unsupported target platform | |
| 91 #endif | |
| 92 | |
| 93 // Adjust the signal stack frame so that it contains the correct stack | |
| 94 // pointer upon returning from sigreturn(). | |
| 95 #if defined(__x86_64__) | |
| 96 *(char **)(request.clone_req.stack + 0xA0) = stack; | |
| 97 #elif defined(__i386__) | |
| 98 *(char **)(request.clone_req.stack + 0x1C) = stack; | |
| 99 #else | |
| 100 #error Unsupported target platform | |
| 101 #endif | |
| 102 | |
| 103 SysCalls sys; | |
| 104 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 105 sizeof(request) || | |
| 106 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 107 die("Failed to forward clone() request [sandbox]"); | |
| 108 } | |
| 109 } | |
| 110 Debug::elapsed(tm, __NR_clone); | |
| 111 return rc; | |
| 112 } | |
| 113 | |
| 114 bool Sandbox::process_clone(int parentMapsFd, int sandboxFd, int threadFdPub, | |
| 115 int threadFd, SecureMem::Args* mem) { | |
| 116 // Read request | |
| 117 Clone clone_req; | |
| 118 SysCalls sys; | |
| 119 if (read(sys, sandboxFd, &clone_req, sizeof(clone_req)) !=sizeof(clone_req)){ | |
| 120 die("Failed to read parameters for clone() [process]"); | |
| 121 } | |
| 122 | |
| 123 // TODO(markus): add policy restricting parameters for clone | |
| 124 if ((clone_req.flags & ~CLONE_DETACHED) != (CLONE_VM|CLONE_FS|CLONE_FILES| | |
| 125 CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS| | |
| 126 CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID)) { | |
| 127 SecureMem::abandonSystemCall(threadFd, -EPERM); | |
| 128 return false; | |
| 129 } else { | |
| 130 SecureMem::Args* newMem = getNewSecureMem(); | |
| 131 if (!newMem) { | |
| 132 SecureMem::abandonSystemCall(threadFd, -ENOMEM); | |
| 133 return false; | |
| 134 } else { | |
| 135 // clone() has unusual semantics. We don't want to return back into the | |
| 136 // trusted thread, but instead we need to continue execution at the IP | |
| 137 // where we got called initially. | |
| 138 SecureMem::lockSystemCall(parentMapsFd, mem); | |
| 139 mem->ret = clone_req.ret; | |
| 140 #if defined(__x86_64__) | |
| 141 mem->rbp = clone_req.regs64.rbp; | |
| 142 mem->rbx = clone_req.regs64.rbx; | |
| 143 mem->rcx = clone_req.regs64.rcx; | |
| 144 mem->rdx = clone_req.regs64.rdx; | |
| 145 mem->rsi = clone_req.regs64.rsi; | |
| 146 mem->rdi = clone_req.regs64.rdi; | |
| 147 mem->r8 = clone_req.regs64.r8; | |
| 148 mem->r9 = clone_req.regs64.r9; | |
| 149 mem->r10 = clone_req.regs64.r10; | |
| 150 mem->r11 = clone_req.regs64.r11; | |
| 151 mem->r12 = clone_req.regs64.r12; | |
| 152 mem->r13 = clone_req.regs64.r13; | |
| 153 mem->r14 = clone_req.regs64.r14; | |
| 154 mem->r15 = clone_req.regs64.r15; | |
| 155 #elif defined(__i386__) | |
| 156 mem->ebp = clone_req.regs32.ebp; | |
| 157 mem->edi = clone_req.regs32.edi; | |
| 158 mem->esi = clone_req.regs32.esi; | |
| 159 mem->edx = clone_req.regs32.edx; | |
| 160 mem->ecx = clone_req.regs32.ecx; | |
| 161 mem->ebx = clone_req.regs32.ebx; | |
| 162 #else | |
| 163 #error Unsupported target platform | |
| 164 #endif | |
| 165 newMem->sequence = 0; | |
| 166 newMem->shmId = -1; | |
| 167 mem->newSecureMem = newMem; | |
| 168 mem->processFdPub = processFdPub_; | |
| 169 mem->cloneFdPub = cloneFdPub_; | |
| 170 | |
| 171 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
| 172 __NR_clone, clone_req.flags, clone_req.stack, | |
| 173 clone_req.pid, clone_req.ctid, clone_req.tls); | |
| 174 return true; | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 } // namespace | |
| OLD | NEW |