Skip to content

Commit 282ba30

Browse files
rickyzCommit bot
authored andcommitted
Linux sandbox: Allow restricting sched_* on other processes.
Adds a RestrictSchedTarget parameter restriction which only allows sched_* syscalls if the pid argument is the sandboxed process's pid or if the pid is 0, which means the current thread. glibc's pthread implementation sometimes calls these syscalls with pid equal to the current tid. On these calls, the policy triggers a SIGSYS, and the SIGSYS handler reruns the syscall with a pid argument of 0. [email protected] BUG=413855 Review URL: https://codereview.chromium.org/590213003 Cr-Commit-Position: refs/heads/master@{#297059}
1 parent b1f98f2 commit 282ba30

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed

sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66

77
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
88

9+
#include <sys/syscall.h>
910
#include <unistd.h>
1011

1112
#include "base/basictypes.h"
13+
#include "base/logging.h"
1214
#include "base/posix/eintr_wrapper.h"
1315
#include "build/build_config.h"
1416
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
17+
#include "sandbox/linux/seccomp-bpf/syscall.h"
18+
#include "sandbox/linux/services/linux_syscalls.h"
1519

1620
#if defined(__mips__)
1721
// __NR_Linux, is defined in <asm/unistd.h>.
@@ -206,6 +210,40 @@ intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
206210
_exit(1);
207211
}
208212

213+
intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
214+
void* aux) {
215+
switch (args.nr) {
216+
case __NR_sched_getaffinity:
217+
case __NR_sched_getattr:
218+
case __NR_sched_getparam:
219+
case __NR_sched_getscheduler:
220+
case __NR_sched_rr_get_interval:
221+
case __NR_sched_setaffinity:
222+
case __NR_sched_setattr:
223+
case __NR_sched_setparam:
224+
case __NR_sched_setscheduler:
225+
const pid_t tid = syscall(__NR_gettid);
226+
// The first argument is the pid. If is our thread id, then replace it
227+
// with 0, which is equivalent and allowed by the policy.
228+
if (args.args[0] == static_cast<uint64_t>(tid)) {
229+
return Syscall::Call(args.nr,
230+
0,
231+
static_cast<intptr_t>(args.args[1]),
232+
static_cast<intptr_t>(args.args[2]),
233+
static_cast<intptr_t>(args.args[3]),
234+
static_cast<intptr_t>(args.args[4]),
235+
static_cast<intptr_t>(args.args[5]));
236+
}
237+
break;
238+
}
239+
240+
CrashSIGSYS_Handler(args, aux);
241+
242+
// Should never be reached.
243+
RAW_CHECK(false);
244+
return -ENOSYS;
245+
}
246+
209247
bpf_dsl::ResultExpr CrashSIGSYS() {
210248
return bpf_dsl::Trap(CrashSIGSYS_Handler, NULL);
211249
}
@@ -230,6 +268,10 @@ bpf_dsl::ResultExpr CrashSIGSYSFutex() {
230268
return bpf_dsl::Trap(SIGSYSFutexFailure, NULL);
231269
}
232270

271+
bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
272+
return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
273+
}
274+
233275
const char* GetErrorMessageContentForTests() {
234276
return SECCOMP_MESSAGE_COMMON_CONTENT;
235277
}

sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ SANDBOX_EXPORT intptr_t
4747
// argument.
4848
SANDBOX_EXPORT intptr_t
4949
SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
50+
// If the syscall is not being called on the current tid, crashes in the same
51+
// way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the
52+
// syscall with the pid argument set to 0 (which for these calls means the
53+
// current thread). The following syscalls are supported:
54+
//
55+
// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
56+
// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
57+
// sched_setparam(), sched_setscheduler()
58+
SANDBOX_EXPORT intptr_t
59+
SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux);
5060

5161
// Variants of the above functions for use with bpf_dsl.
5262
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS();
@@ -55,6 +65,7 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPrctl();
5565
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl();
5666
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
5767
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
68+
SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
5869

5970
// Following four functions return substrings of error messages used
6071
// in the above four functions. They are useful in death tests.

sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
3030
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
3131
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
32+
#include "sandbox/linux/services/linux_syscalls.h"
3233

3334
#if defined(OS_ANDROID)
3435

@@ -263,4 +264,26 @@ ResultExpr RestrictClockID() {
263264
Allow()).Else(CrashSIGSYS());
264265
}
265266

267+
ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) {
268+
switch (sysno) {
269+
case __NR_sched_getaffinity:
270+
case __NR_sched_getattr:
271+
case __NR_sched_getparam:
272+
case __NR_sched_getscheduler:
273+
case __NR_sched_rr_get_interval:
274+
case __NR_sched_setaffinity:
275+
case __NR_sched_setattr:
276+
case __NR_sched_setparam:
277+
case __NR_sched_setscheduler: {
278+
const Arg<pid_t> pid(0);
279+
return If(pid == 0 || pid == target_pid, Allow())
280+
.Else(RewriteSchedSIGSYS());
281+
}
282+
default:
283+
NOTREACHED();
284+
return CrashSIGSYS();
285+
}
286+
}
287+
288+
266289
} // namespace sandbox.

sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ bpf_dsl::ResultExpr RestrictGetSetpriority(pid_t target_pid);
7575
// On Chrome OS, base::TimeTicks::kClockSystemTrace is also allowed.
7676
SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictClockID();
7777

78+
// Restricts |pid| for sched_* syscalls which take a pid as the first argument.
79+
// We only allow calling these syscalls if the pid argument is equal to the pid
80+
// of the sandboxed process or 0 (indicating the current thread). The following
81+
// syscalls are supported:
82+
//
83+
// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
84+
// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
85+
// sched_setparam(), sched_setscheduler()
86+
SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSchedTarget(pid_t target_pid,
87+
int sysno);
88+
7889
} // namespace sandbox.
7990

8091
#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_

sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@
44

55
#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
66

7+
#include <errno.h>
8+
#include <sched.h>
9+
#include <sys/syscall.h>
710
#include <time.h>
11+
#include <unistd.h>
812

13+
#include "base/bind.h"
14+
#include "base/synchronization/waitable_event.h"
915
#include "base/sys_info.h"
16+
#include "base/threading/thread.h"
1017
#include "base/time/time.h"
1118
#include "build/build_config.h"
1219
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
@@ -136,6 +143,72 @@ BPF_DEATH_TEST_C(ParameterRestrictions,
136143
}
137144
#endif // !defined(OS_ANDROID)
138145

146+
class RestrictSchedPolicy : public SandboxBPFDSLPolicy {
147+
public:
148+
RestrictSchedPolicy() {}
149+
virtual ~RestrictSchedPolicy() {}
150+
151+
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
152+
switch (sysno) {
153+
case __NR_sched_getparam:
154+
return RestrictSchedTarget(getpid(), sysno);
155+
default:
156+
return Allow();
157+
}
158+
}
159+
};
160+
161+
void CheckSchedGetParam(pid_t pid, struct sched_param* param) {
162+
BPF_ASSERT_EQ(0, sched_getparam(pid, param));
163+
}
164+
165+
void SchedGetParamThread(base::WaitableEvent* thread_run) {
166+
const pid_t pid = getpid();
167+
const pid_t tid = syscall(__NR_gettid);
168+
BPF_ASSERT_NE(pid, tid);
169+
170+
struct sched_param current_pid_param;
171+
CheckSchedGetParam(pid, &current_pid_param);
172+
173+
struct sched_param zero_param;
174+
CheckSchedGetParam(0, &zero_param);
175+
176+
struct sched_param tid_param;
177+
CheckSchedGetParam(tid, &tid_param);
178+
179+
BPF_ASSERT_EQ(zero_param.sched_priority, tid_param.sched_priority);
180+
181+
// Verify that the SIGSYS handler sets errno properly.
182+
errno = 0;
183+
BPF_ASSERT_EQ(-1, sched_getparam(tid, NULL));
184+
BPF_ASSERT_EQ(EINVAL, errno);
185+
186+
thread_run->Signal();
187+
}
188+
189+
BPF_TEST_C(ParameterRestrictions,
190+
sched_getparam_allowed,
191+
RestrictSchedPolicy) {
192+
base::WaitableEvent thread_run(true, false);
193+
// Run the actual test in a new thread so that the current pid and tid are
194+
// different.
195+
base::Thread getparam_thread("sched_getparam_thread");
196+
BPF_ASSERT(getparam_thread.Start());
197+
getparam_thread.message_loop()->PostTask(
198+
FROM_HERE, base::Bind(&SchedGetParamThread, &thread_run));
199+
BPF_ASSERT(thread_run.TimedWait(base::TimeDelta::FromMilliseconds(5000)));
200+
getparam_thread.Stop();
201+
}
202+
203+
BPF_DEATH_TEST_C(ParameterRestrictions,
204+
sched_getparam_crash_non_zero,
205+
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
206+
RestrictSchedPolicy) {
207+
const pid_t kInitPID = 1;
208+
struct sched_param param;
209+
sched_getparam(kInitPID, &param);
210+
}
211+
139212
} // namespace
140213

141214
} // namespace sandbox

0 commit comments

Comments
 (0)