/
clone.cc
163 lines (153 loc) · 6.62 KB
/
clone.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debug.h"
#include "sandbox_impl.h"
namespace playground {
long Sandbox::sandbox__clone(int flags, char* stack, int* pid, void* arg4,
void* arg5, void *wrapper_sp) {
long long tm;
Debug::syscall(&tm, __NR_clone, "Executing handler");
struct {
struct RequestHeader header;
Clone clone_req;
} __attribute__((packed)) request;
request.clone_req.flags = flags;
request.clone_req.stack = stack;
request.clone_req.pid = pid;
request.clone_req.arg4 = arg4;
request.clone_req.arg5 = arg5;
// TODO(markus): Passing stack == 0 currently does not do the same thing
// that the kernel would do without the sandbox. This is just going to
// cause a crash. We should detect this case, and replace the stack pointer
// with the correct value, instead.
// This is complicated by the fact that we will temporarily be executing
// both threads from the same stack. Some synchronization will be necessary.
// Fortunately, this complication also explains why hardly anybody ever
// does this.
// See trusted_thread.cc for more information.
long rc;
if (stack == 0) {
rc = -EINVAL;
} else {
// In order to unblock the signal mask in the newly created thread and
// after entering Seccomp mode, we have to call sigreturn(). But that
// requires access to a proper stack frame describing a valid signal.
// We trigger a signal now and make sure the stack frame ends up on the
// new stack. Our segv() handler (in sandbox.cc) does that for us.
// See trusted_thread.cc for more details on how threads get created.
//
// In general we rely on the kernel for generating the signal stack
// frame, as the exact binary format has been extended several times over
// the course of the kernel's development. Fortunately, the kernel
// developers treat the initial part of the stack frame as a stable part
// of the ABI. So, we can rely on fixed, well-defined offsets for accessing
// register values and for accessing the signal mask.
#if defined(__x86_64__)
// Red zone compensation. The instrumented system call will remove 128
// bytes from the thread's stack prior to returning to the original
// call site.
stack -= 128;
request.clone_req.stack = stack;
void *dummy;
asm volatile("mov %%rsp, %%rcx\n"
"mov %3, %%rsp\n"
"int $0\n"
"mov %%rcx, %%rsp\n"
: "=a"(request.clone_req.stack), "=&c"(dummy)
: "a"(__NR_clone + 0xF000), "m"(request.clone_req.stack)
: "memory");
#elif defined(__i386__)
void *dummy;
asm volatile("mov %%esp, %%ecx\n"
"mov %3, %%esp\n"
"int $0\n"
"mov %%ecx, %%esp\n"
: "=a"(request.clone_req.stack), "=&c"(dummy)
: "a"(__NR_clone + 0xF000), "m"(request.clone_req.stack)
: "memory");
#else
#error Unsupported target platform
#endif
// We have created a signal frame that contains the correct values
// of FP registers and segment registers, but we need to update it
// with:
// * The values of general purpose registers, as saved by
// syscallEntryPoint.
// * The return address, which is also saved by syscallEntryPoint.
// Note that we do not return through defaultSystemCallHandler()
// and the syscallEntryPoint code in the new thread. We jump
// out of these, straight back to where syscallEntryPoint was
// originally called.
// * The new stack address for the new thread.
// * The return value from the syscall (0 in the new thread).
struct CloneStackFrame *regs = (struct CloneStackFrame *) wrapper_sp;
#if defined(__x86_64__)
struct ucontext *uc = (struct ucontext *) request.clone_req.stack;
uc->uc_mcontext.gregs[REG_R8] = (long) regs->r8;
uc->uc_mcontext.gregs[REG_R9] = (long) regs->r9;
uc->uc_mcontext.gregs[REG_R10] = (long) regs->r10;
uc->uc_mcontext.gregs[REG_R11] = (long) regs->r11;
uc->uc_mcontext.gregs[REG_R12] = (long) regs->r12;
uc->uc_mcontext.gregs[REG_R13] = (long) regs->r13;
uc->uc_mcontext.gregs[REG_R14] = (long) regs->r14;
uc->uc_mcontext.gregs[REG_R15] = (long) regs->r15;
uc->uc_mcontext.gregs[REG_RDI] = (long) regs->rdi;
uc->uc_mcontext.gregs[REG_RSI] = (long) regs->rsi;
uc->uc_mcontext.gregs[REG_RBP] = (long) regs->rbp;
uc->uc_mcontext.gregs[REG_RBX] = (long) regs->rbx;
uc->uc_mcontext.gregs[REG_RDX] = (long) regs->rdx;
uc->uc_mcontext.gregs[REG_RCX] = (long) regs->rcx;
uc->uc_mcontext.gregs[REG_RAX] = 0; // Result of clone()
uc->uc_mcontext.gregs[REG_RIP] = (long) regs->ret;
uc->uc_mcontext.gregs[REG_RSP] = (long) stack;
#elif defined(__i386__)
struct sigcontext *sc = (struct sigcontext *) request.clone_req.stack;
sc->edi = (long) regs->edi;
sc->esi = (long) regs->esi;
sc->ebp = (long) regs->ebp;
sc->ebx = (long) regs->ebx;
sc->edx = (long) regs->edx;
sc->ecx = (long) regs->ecx;
sc->eax = 0; // Result of clone()
sc->eip = (long) regs->ret;
sc->esp = (long) stack;
#else
#error Unsupported target platform
#endif
rc = forwardSyscall(__NR_clone, &request.header, sizeof(request));
}
Debug::elapsed(tm, __NR_clone);
return rc;
}
bool Sandbox::process_clone(const SecureMem::SyscallRequestInfo* info) {
// Read request
Clone clone_req;
SysCalls sys;
if (read(sys, info->trustedProcessFd, &clone_req, sizeof(clone_req)) !=
sizeof(clone_req)) {
die("Failed to read parameters for clone() [process]");
}
// TODO(markus): add policy restricting parameters for clone
if ((clone_req.flags & ~CLONE_DETACHED) != (CLONE_VM|CLONE_FS|CLONE_FILES|
CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID)) {
SecureMem::abandonSystemCall(*info, -EPERM);
return false;
} else {
SecureMem::Args* newMem = getNewSecureMem();
if (!newMem) {
SecureMem::abandonSystemCall(*info, -ENOMEM);
return false;
} else {
newMem->sequence = 0;
newMem->shmId = -1;
SecureMem::sendSystemCall(*info, SecureMem::SEND_UNLOCKED,
clone_req.flags, clone_req.stack,
clone_req.pid, clone_req.arg4, clone_req.arg5,
(void*)NULL, newMem);
return true;
}
}
}
} // namespace