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"
10 #include <sys/prctl.h>
11 #include <sys/ptrace.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
32 using sandbox::ErrorCode
;
33 using sandbox::SandboxBPF
;
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
;
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
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
);
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
,
129 ErrorCode(ErrorCode::ERR_ALLOWED
),
130 sb
->Cond(0, ErrorCode::TP_32BIT
, ErrorCode::OP_EQUAL
,
132 ErrorCode(ErrorCode::ERR_ALLOWED
),
133 sb
->Cond(0, ErrorCode::TP_32BIT
, ErrorCode::OP_EQUAL
,
135 ErrorCode(ErrorCode::ERR_ALLOWED
),
136 sb
->Cond(0, ErrorCode::TP_32BIT
, ErrorCode::OP_EQUAL
,
138 ErrorCode(ErrorCode::ERR_ALLOWED
),
139 sb
->Trap(sandbox::CrashSIGSYS_Handler
, NULL
)))));
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
,
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
,
164 sb
->Trap(sandbox::CrashSIGSYS_Handler
, NULL
),
165 sb
->Cond(2, ErrorCode::TP_32BIT
,
166 ErrorCode::OP_HAS_ANY_BITS
,
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
));
183 bool IsGracefullyDenied(int 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__)
201 // tcmalloc calls madvise in TCMalloc_SystemRelease.
203 // EPERM instead of SIGSYS as glibc tries to open files in /proc.
204 // TODO(hamaji): Remove this when we switch to newlib.
206 // For RunSandboxSanityChecks().
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
215 #if defined(__i386__) || defined(__x86_64__)
225 void RunSandboxSanityChecks() {
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
);
236 ErrorCode
NaClNonSfiBPFSandboxPolicy::EvaluateSyscall(SandboxBPF
* sb
,
240 #if defined(__i386__) || defined(__arm__)
242 #elif defined(__x86_64__)
249 case __NR_exit_group
:
250 #if defined(__i386__) || defined(__arm__)
252 #elif defined(__x86_64__)
255 // TODO(hamaji): Allow only FUTEX_PRIVATE_FLAG.
257 // TODO(hamaji): Remove the need of gettid. Currently, this is
258 // called from PlatformThread::CurrentId().
260 case __NR_gettimeofday
:
263 // TODO(hamaji): Remove the need of pipe. Currently, this is
264 // called from base::MessagePumpLibevent::Init().
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.
278 case __ARM_NR_cacheflush
:
280 return ErrorCode(ErrorCode::ERR_ALLOWED
);
282 case __NR_clock_getres
:
283 case __NR_clock_gettime
:
284 return RestrictClockID(sb
);
287 return RestrictClone(sb
);
289 #if defined(__x86_64__)
292 #if defined(__i386__) || defined(__arm__)
295 return RestrictFcntlCommands(sb
);
297 #if defined(__x86_64__)
300 #if defined(__i386__) || defined(__arm__)
303 return RestrictMmap(sb
);
305 return RestrictMprotect(sb
);
308 return RestrictPrctl(sb
);
310 #if defined(__i386__)
311 case __NR_socketcall
:
312 return RestrictSocketcall(sb
);
314 #if defined(__x86_64__) || defined(__arm__)
318 return ErrorCode(ErrorCode::ERR_ALLOWED
);
319 case __NR_socketpair
:
320 return RestrictSocketpair(sb
);
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.
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
)
345 RunSandboxSanityChecks();
349 } // namespace nonsfi