Support for unpacked ARM packed relocations.
[chromium-blink-merge.git] / components / nacl / loader / nonsfi / nonsfi_sandbox.cc
blobff17c62cb170453fd4daf456e00b6999ab926731
1 // Copyright 2014 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.
5 #include "components/nacl/loader/nonsfi/nonsfi_sandbox.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <linux/net.h>
10 #include <sys/prctl.h>
11 #include <sys/ptrace.h>
12 #include <sys/mman.h>
13 #include <sys/socket.h>
14 #include <sys/syscall.h>
16 #include "base/basictypes.h"
17 #include "base/logging.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "content/public/common/sandbox_init.h"
21 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
22 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
23 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
24 #include "sandbox/linux/seccomp-bpf/trap.h"
25 #include "sandbox/linux/services/linux_syscalls.h"
27 #if defined(__arm__) && !defined(MAP_STACK)
28 // Chrome OS Daisy (ARM) build environment has old headers.
29 #define MAP_STACK 0x20000
30 #endif
32 using sandbox::ErrorCode;
33 using sandbox::SandboxBPF;
35 namespace nacl {
36 namespace nonsfi {
37 namespace {
39 ErrorCode RestrictFcntlCommands(SandboxBPF* sb) {
40 ErrorCode::ArgType mask_long_type;
41 if (sizeof(long) == 8) {
42 mask_long_type = ErrorCode::TP_64BIT;
43 } else if (sizeof(long) == 4) {
44 mask_long_type = ErrorCode::TP_32BIT;
45 } else {
46 NOTREACHED();
48 // We allow following cases:
49 // 1. F_SETFD + FD_CLOEXEC: libevent's epoll_init uses this.
50 // 2. F_GETFL: Used by SetNonBlocking in
51 // message_pump_libevent.cc and Channel::ChannelImpl::CreatePipe
52 // in ipc_channel_posix.cc. Note that the latter does not work
53 // with EPERM.
54 // 3. F_SETFL: Used by evutil_make_socket_nonblocking in
55 // libevent and SetNonBlocking. As the latter mix O_NONBLOCK to
56 // the return value of F_GETFL, so we need to allow O_ACCMODE in
57 // addition to O_NONBLOCK.
58 const unsigned long denied_mask = ~(O_ACCMODE | O_NONBLOCK);
59 return sb->Cond(1, ErrorCode::TP_32BIT,
60 ErrorCode::OP_EQUAL, F_SETFD,
61 sb->Cond(2, mask_long_type,
62 ErrorCode::OP_EQUAL, FD_CLOEXEC,
63 ErrorCode(ErrorCode::ERR_ALLOWED),
64 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL)),
65 sb->Cond(1, ErrorCode::TP_32BIT,
66 ErrorCode::OP_EQUAL, F_GETFL,
67 ErrorCode(ErrorCode::ERR_ALLOWED),
68 sb->Cond(1, ErrorCode::TP_32BIT,
69 ErrorCode::OP_EQUAL, F_SETFL,
70 sb->Cond(2, mask_long_type,
71 ErrorCode::OP_HAS_ANY_BITS, denied_mask,
72 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL),
73 ErrorCode(ErrorCode::ERR_ALLOWED)),
74 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL))));
77 ErrorCode RestrictClockID(SandboxBPF* sb) {
78 // We allow accessing only CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID,
79 // CLOCK_REALTIME, and CLOCK_THREAD_CPUTIME_ID. In particular, this disallows
80 // access to arbitrary per-{process,thread} CPU-time clock IDs (such as those
81 // returned by {clock,pthread}_getcpuclockid), which can leak information
82 // about the state of the host OS.
83 COMPILE_ASSERT(4 == sizeof(clockid_t), clockid_is_not_32bit);
84 ErrorCode result = sb->Cond(0, ErrorCode::TP_32BIT,
85 ErrorCode::OP_EQUAL, CLOCK_MONOTONIC,
86 ErrorCode(ErrorCode::ERR_ALLOWED),
87 sb->Cond(0, ErrorCode::TP_32BIT,
88 ErrorCode::OP_EQUAL, CLOCK_PROCESS_CPUTIME_ID,
89 ErrorCode(ErrorCode::ERR_ALLOWED),
90 sb->Cond(0, ErrorCode::TP_32BIT,
91 ErrorCode::OP_EQUAL, CLOCK_REALTIME,
92 ErrorCode(ErrorCode::ERR_ALLOWED),
93 sb->Cond(0, ErrorCode::TP_32BIT,
94 ErrorCode::OP_EQUAL, CLOCK_THREAD_CPUTIME_ID,
95 ErrorCode(ErrorCode::ERR_ALLOWED),
96 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL)))));
97 #if defined(OS_CHROMEOS)
98 // Allow the special clock for Chrome OS used by Chrome tracing.
99 result = sb->Cond(0, ErrorCode::TP_32BIT,
100 ErrorCode::OP_EQUAL, base::TimeTicks::kClockSystemTrace,
101 ErrorCode(ErrorCode::ERR_ALLOWED), result);
102 #endif
103 return result;
106 ErrorCode RestrictClone(SandboxBPF* sb) {
107 // We allow clone only for new thread creation.
108 return sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
109 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
110 CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS |
111 CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID,
112 ErrorCode(ErrorCode::ERR_ALLOWED),
113 sb->Trap(sandbox::SIGSYSCloneFailure, NULL));
116 ErrorCode RestrictPrctl(SandboxBPF* sb) {
117 // base::PlatformThread::SetName() uses PR_SET_NAME so we return
118 // EPERM for it. Otherwise, we will raise SIGSYS.
119 return sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
120 PR_SET_NAME, ErrorCode(EPERM),
121 sb->Trap(sandbox::SIGSYSPrctlFailure, NULL));
124 #if defined(__i386__)
125 ErrorCode RestrictSocketcall(SandboxBPF* sb) {
126 // We only allow socketpair, sendmsg, and recvmsg.
127 return sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
128 SYS_SOCKETPAIR,
129 ErrorCode(ErrorCode::ERR_ALLOWED),
130 sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
131 SYS_SENDMSG,
132 ErrorCode(ErrorCode::ERR_ALLOWED),
133 sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
134 SYS_RECVMSG,
135 ErrorCode(ErrorCode::ERR_ALLOWED),
136 sb->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
137 SYS_SHUTDOWN,
138 ErrorCode(ErrorCode::ERR_ALLOWED),
139 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL)))));
141 #endif
143 ErrorCode RestrictMprotect(SandboxBPF* sb) {
144 // TODO(jln, keescook, drewry): Limit the use of mprotect by adding
145 // some features to linux kernel.
146 const uint32_t denied_mask = ~(PROT_READ | PROT_WRITE | PROT_EXEC);
147 return sb->Cond(2, ErrorCode::TP_32BIT,
148 ErrorCode::OP_HAS_ANY_BITS,
149 denied_mask,
150 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL),
151 ErrorCode(ErrorCode::ERR_ALLOWED));
154 ErrorCode RestrictMmap(SandboxBPF* sb) {
155 const uint32_t denied_flag_mask = ~(MAP_SHARED | MAP_PRIVATE |
156 MAP_ANONYMOUS | MAP_STACK | MAP_FIXED);
157 // When PROT_EXEC is specified, IRT mmap of Non-SFI NaCl helper
158 // calls mmap without PROT_EXEC and then adds PROT_EXEC by mprotect,
159 // so we do not need to allow PROT_EXEC in mmap.
160 const uint32_t denied_prot_mask = ~(PROT_READ | PROT_WRITE);
161 return sb->Cond(3, ErrorCode::TP_32BIT,
162 ErrorCode::OP_HAS_ANY_BITS,
163 denied_flag_mask,
164 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL),
165 sb->Cond(2, ErrorCode::TP_32BIT,
166 ErrorCode::OP_HAS_ANY_BITS,
167 denied_prot_mask,
168 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL),
169 ErrorCode(ErrorCode::ERR_ALLOWED)));
172 #if defined(__x86_64__) || defined(__arm__)
173 ErrorCode RestrictSocketpair(SandboxBPF* sb) {
174 // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen.
175 COMPILE_ASSERT(AF_UNIX == PF_UNIX, af_unix_pf_unix_different);
176 return sb->Cond(0, ErrorCode::TP_32BIT,
177 ErrorCode::OP_EQUAL, AF_UNIX,
178 ErrorCode(ErrorCode::ERR_ALLOWED),
179 sb->Trap(sandbox::CrashSIGSYS_Handler, NULL));
181 #endif
183 bool IsGracefullyDenied(int sysno) {
184 switch (sysno) {
185 // libevent tries this first and then falls back to poll if
186 // epoll_create fails.
187 case __NR_epoll_create:
188 // third_party/libevent uses them, but we can just return -1 from
189 // them as it is just checking getuid() != geteuid() and
190 // getgid() != getegid()
191 #if defined(__i386__) || defined(__arm__)
192 case __NR_getegid32:
193 case __NR_geteuid32:
194 case __NR_getgid32:
195 case __NR_getuid32:
196 #endif
197 case __NR_getegid:
198 case __NR_geteuid:
199 case __NR_getgid:
200 case __NR_getuid:
201 // tcmalloc calls madvise in TCMalloc_SystemRelease.
202 case __NR_madvise:
203 // EPERM instead of SIGSYS as glibc tries to open files in /proc.
204 // TODO(hamaji): Remove this when we switch to newlib.
205 case __NR_open:
206 // For RunSandboxSanityChecks().
207 case __NR_ptrace:
208 // glibc uses this for its pthread implementation. If we return
209 // EPERM for this, glibc will stop using this.
210 // TODO(hamaji): newlib does not use this. Make this SIGTRAP once
211 // we have switched to newlib.
212 case __NR_set_robust_list:
213 // This is obsolete in ARM EABI, but x86 glibc indirectly calls
214 // this in sysconf.
215 #if defined(__i386__) || defined(__x86_64__)
216 case __NR_time:
217 #endif
218 return true;
220 default:
221 return false;
225 void RunSandboxSanityChecks() {
226 errno = 0;
227 // Make a ptrace request with an invalid PID.
228 long ptrace_ret = ptrace(PTRACE_PEEKUSER, -1 /* pid */, NULL, NULL);
229 CHECK_EQ(-1, ptrace_ret);
230 // Without the sandbox on, this ptrace call would ESRCH instead.
231 CHECK_EQ(EPERM, errno);
234 } // namespace
236 ErrorCode NaClNonSfiBPFSandboxPolicy::EvaluateSyscall(SandboxBPF* sb,
237 int sysno) const {
238 switch (sysno) {
239 // Allowed syscalls.
240 #if defined(__i386__) || defined(__arm__)
241 case __NR__llseek:
242 #elif defined(__x86_64__)
243 case __NR_lseek:
244 #endif
245 case __NR_close:
246 case __NR_dup:
247 case __NR_dup2:
248 case __NR_exit:
249 case __NR_exit_group:
250 #if defined(__i386__) || defined(__arm__)
251 case __NR_fstat64:
252 #elif defined(__x86_64__)
253 case __NR_fstat:
254 #endif
255 // TODO(hamaji): Allow only FUTEX_PRIVATE_FLAG.
256 case __NR_futex:
257 // TODO(hamaji): Remove the need of gettid. Currently, this is
258 // called from PlatformThread::CurrentId().
259 case __NR_gettid:
260 case __NR_gettimeofday:
261 case __NR_munmap:
262 case __NR_nanosleep:
263 // TODO(hamaji): Remove the need of pipe. Currently, this is
264 // called from base::MessagePumpLibevent::Init().
265 case __NR_pipe:
266 case __NR_poll:
267 case __NR_pread64:
268 case __NR_pwrite64:
269 case __NR_read:
270 case __NR_restart_syscall:
271 case __NR_sched_yield:
272 // __NR_times needed as clock() is called by CommandBufferHelper, which is
273 // used by NaCl applications that use Pepper's 3D interfaces.
274 // See crbug.com/264856 for details.
275 case __NR_times:
276 case __NR_write:
277 #if defined(__arm__)
278 case __ARM_NR_cacheflush:
279 #endif
280 return ErrorCode(ErrorCode::ERR_ALLOWED);
282 case __NR_clock_getres:
283 case __NR_clock_gettime:
284 return RestrictClockID(sb);
286 case __NR_clone:
287 return RestrictClone(sb);
289 #if defined(__x86_64__)
290 case __NR_fcntl:
291 #endif
292 #if defined(__i386__) || defined(__arm__)
293 case __NR_fcntl64:
294 #endif
295 return RestrictFcntlCommands(sb);
297 #if defined(__x86_64__)
298 case __NR_mmap:
299 #endif
300 #if defined(__i386__) || defined(__arm__)
301 case __NR_mmap2:
302 #endif
303 return RestrictMmap(sb);
304 case __NR_mprotect:
305 return RestrictMprotect(sb);
307 case __NR_prctl:
308 return RestrictPrctl(sb);
310 #if defined(__i386__)
311 case __NR_socketcall:
312 return RestrictSocketcall(sb);
313 #endif
314 #if defined(__x86_64__) || defined(__arm__)
315 case __NR_recvmsg:
316 case __NR_sendmsg:
317 case __NR_shutdown:
318 return ErrorCode(ErrorCode::ERR_ALLOWED);
319 case __NR_socketpair:
320 return RestrictSocketpair(sb);
321 #endif
323 case __NR_brk:
324 // The behavior of brk on Linux is different from other system
325 // calls. It does not return errno but the current break on
326 // failure. glibc thinks brk failed if the return value of brk
327 // is less than the requested address (i.e., brk(addr) < addr).
328 // So, glibc thinks brk succeeded if we return -EPERM and we
329 // need to return zero instead.
330 return ErrorCode(0);
332 default:
333 if (IsGracefullyDenied(sysno))
334 return ErrorCode(EPERM);
335 return sb->Trap(sandbox::CrashSIGSYS_Handler, NULL);
339 bool InitializeBPFSandbox() {
340 bool sandbox_is_initialized = content::InitializeSandbox(
341 scoped_ptr<sandbox::SandboxBPFPolicy>(
342 new nacl::nonsfi::NaClNonSfiBPFSandboxPolicy()));
343 if (!sandbox_is_initialized)
344 return false;
345 RunSandboxSanityChecks();
346 return true;
349 } // namespace nonsfi
350 } // namespace nacl