Merge from Chromium at DEPS revision 288042
This commit was generated by merge_to_master.py.
Change-Id: I583602ff16d735199f1810565c9296e970ce2854
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
index bd18412..eaa9ff3 100644
--- a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -134,6 +134,16 @@
BPF_ASSERT(ENOSYS == errno);
}
+const char kHelloMessage[] = "Hello";
+
+BPF_DEATH_TEST_C(BPFTest,
+ BPFDeathTestWithInlineTest,
+ DEATH_MESSAGE(kHelloMessage),
+ EnosysPtracePolicy) {
+ LOG(ERROR) << kHelloMessage;
+ _exit(1);
+}
+
} // namespace
} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
index 2e51381..2cc001c 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -41,9 +41,14 @@
// indicate success, but it won't actually run the system call.
// This is very different from return ERR_ALLOWED.
ERR_MIN_ERRNO = 0,
+#if defined(__mips__)
+ // MIPS only supports errno up to 1133
+ ERR_MAX_ERRNO = 1133,
+#else
// TODO(markus): Android only supports errno up to 255
// (crbug.com/181647).
ERR_MAX_ERRNO = 4095,
+#endif
};
// While BPF filter programs always operate on 32bit quantities, the kernel
diff --git a/sandbox/linux/seccomp-bpf/linux_seccomp.h b/sandbox/linux/seccomp-bpf/linux_seccomp.h
index b3d7a55..1716655 100644
--- a/sandbox/linux/seccomp-bpf/linux_seccomp.h
+++ b/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -23,6 +23,10 @@
#if !defined(__BIONIC__) || defined(__x86_64__)
#include <sys/types.h> // Fix for gcc 4.7, make sure __uint16_t is defined.
#include <sys/user.h>
+#if defined(__mips__)
+// sys/user.h in eglibc misses size_t definition
+#include <stddef.h>
+#endif
#endif
// For audit.h
@@ -35,6 +39,9 @@
#ifndef EM_X86_64
#define EM_X86_64 62
#endif
+#ifndef EM_MIPS
+#define EM_MIPS 8
+#endif
#ifndef __AUDIT_ARCH_64BIT
#define __AUDIT_ARCH_64BIT 0x80000000
@@ -51,6 +58,9 @@
#ifndef AUDIT_ARCH_X86_64
#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
#endif
+#ifndef AUDIT_ARCH_MIPSEL
+#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
+#endif
// For prctl.h
#ifndef PR_SET_SECCOMP
@@ -287,6 +297,63 @@
#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4
#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5
+#elif defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+#define MIN_SYSCALL __NR_O32_Linux
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls)
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL
+#define SYSCALL_EIGHT_ARGS
+// MIPS sigcontext_t is different from i386/x86_64 and ARM.
+// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg])
+// Based on MIPS o32 ABI syscall convention.
+// On MIPS, when indirect syscall is being made (syscall(__NR_foo)),
+// real identificator (__NR_foo) is not in v0, but in a0
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7)
+// Only the first 4 arguments of syscall are in registers.
+// The rest are on the stack.
+#define SECCOMP_STACKPARM(_ctx, n) (((long *)SECCOMP_REG(_ctx, 29))[(n)])
+#define SECCOMP_PARM5(_ctx) SECCOMP_STACKPARM(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_STACKPARM(_ctx, 5)
+#define SECCOMP_PARM7(_ctx) SECCOMP_STACKPARM(_ctx, 6)
+#define SECCOMP_PARM8(_ctx) SECCOMP_STACKPARM(_ctx, 7)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+// On Mips we don't have structures like user_regs or user_regs_struct in
+// sys/user.h that we could use, so we just define regs_struct directly.
+struct regs_struct {
+ unsigned long long regs[32];
+};
+
+#define REG_a3 regs[7]
+#define REG_a2 regs[6]
+#define REG_a1 regs[5]
+#define REG_a0 regs[4]
+#define REG_v1 regs[3]
+#define REG_v0 regs[2]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3
+
#else
#error Unsupported target platform
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 95fcbc6..c25d6cf 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -13,6 +13,7 @@
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
+#include <sys/socket.h>
#if defined(ANDROID)
// Work-around for buggy headers in Android's NDK
@@ -246,11 +247,8 @@
ErrorCode ErrnoTestPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
-#if defined(ANDROID)
case __NR_dup3: // dup2 is a wrapper of dup3 in android
-#else
case __NR_dup2:
-#endif
// Pretend that dup2() worked, but don't actually do anything.
return ErrorCode(0);
case __NR_setuid:
@@ -662,7 +660,7 @@
// more complex uses of signals where our use of the SIGBUS mask is not
// 100% transparent. This is expected behavior.
int fds[2];
- BPF_ASSERT(pipe(fds) == 0);
+ BPF_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
bus_handler_fd_ = fds[1];
struct sigaction sa = {};
sa.sa_sigaction = SigBusHandler;
@@ -760,15 +758,13 @@
BPF_ASSERT(aux);
BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
switch (args.nr) {
-#if defined(ANDROID)
case __NR_faccessat: // access is a wrapper of faccessat in android
+ BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
-#else
case __NR_access:
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
-#endif
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
@@ -792,11 +788,8 @@
}
switch (sysno) {
-#if defined(ANDROID)
case __NR_faccessat:
-#else
case __NR_access:
-#endif
case __NR_open:
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
@@ -874,28 +867,23 @@
// We deliberately return unusual errno values upon failure, so that we
// can uniquely test for these values. In a "real" policy, you would want
// to return more traditional values.
+ int flags_argument_position = -1;
switch (sysno) {
-#if defined(ANDROID)
- case __NR_openat: // open is a wrapper of openat in android
- // Allow opening files for reading, but don't allow writing.
- COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
- return sandbox->Cond(2,
- ErrorCode::TP_32BIT,
- ErrorCode::OP_HAS_ANY_BITS,
- O_ACCMODE /* 0x3 */,
- ErrorCode(EROFS),
- ErrorCode(ErrorCode::ERR_ALLOWED));
-#else
case __NR_open:
+ case __NR_openat: // open can be a wrapper for openat(2).
+ if (sysno == __NR_open) {
+ flags_argument_position = 1;
+ } else if (sysno == __NR_openat) {
+ flags_argument_position = 2;
+ }
// Allow opening files for reading, but don't allow writing.
COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
- return sandbox->Cond(1,
+ return sandbox->Cond(flags_argument_position,
ErrorCode::TP_32BIT,
ErrorCode::OP_HAS_ANY_BITS,
O_ACCMODE /* 0x3 */,
ErrorCode(EROFS),
ErrorCode(ErrorCode::ERR_ALLOWED));
-#endif
case __NR_prctl:
// Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
// disallow everything else.
@@ -1990,6 +1978,13 @@
return;
#endif
+#if defined(__mips__)
+ // TODO: Figure out how to support specificity of handling indirect syscalls
+ // in this test and enable it.
+ printf("This test is currently disabled on MIPS.");
+ return;
+#endif
+
pid_t pid = fork();
BPF_ASSERT_NE(-1, pid);
if (pid == 0) {
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
index 0a028b7..b0a41b0 100644
--- a/sandbox/linux/seccomp-bpf/syscall.cc
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -9,11 +9,20 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
namespace sandbox {
namespace {
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY)
+// Number that's not currently used by any Linux kernel ABIs.
+const int kInvalidSyscallNumber = 0x351d3;
+#else
+#error Unrecognized architecture
+#endif
+
asm(// We need to be able to tell the kernel exactly where we made a
// system call. The C++ compiler likes to sometimes clone or
// inline code, which would inadvertently end up duplicating
@@ -50,10 +59,10 @@
// that are used internally (e.g. %ebx for position-independent
// code, and %ebp for the frame pointer), and as we need to keep at
// least a few registers available for the register allocator.
- "1:push %esi; .cfi_adjust_cfa_offset 4\n"
- "push %edi; .cfi_adjust_cfa_offset 4\n"
- "push %ebx; .cfi_adjust_cfa_offset 4\n"
- "push %ebp; .cfi_adjust_cfa_offset 4\n"
+ "1:push %esi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset esi, 0\n"
+ "push %edi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset edi, 0\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebx, 0\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebp, 0\n"
// Copy entries from the array holding the arguments into the
// correct CPU registers.
"movl 0(%edi), %ebx\n"
@@ -68,10 +77,10 @@
"2:"
// Restore any clobbered registers that we didn't declare to the
// compiler.
- "pop %ebp; .cfi_adjust_cfa_offset -4\n"
- "pop %ebx; .cfi_adjust_cfa_offset -4\n"
- "pop %edi; .cfi_adjust_cfa_offset -4\n"
- "pop %esi; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebp; .cfi_restore ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_restore ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_restore edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_restore esi; .cfi_adjust_cfa_offset -4\n"
"ret\n"
".cfi_endproc\n"
"9:.size SyscallAsm, 9b-SyscallAsm\n"
@@ -171,11 +180,61 @@
#endif
".fnend\n"
"9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__mips__)
+ ".text\n"
+ ".align 4\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 40, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ "addiu $sp, $sp, -40\n"
+ "sw $ra, 36($sp)\n"
+ // Check if "v0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ "la $v0, 2f\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS first four arguments go to registers a0 - a3 and any
+ // argument after that goes to stack. We can go ahead and directly
+ // copy the entries from the arguments array into the appropriate
+ // CPU registers and on the stack.
+ "1:lw $a3, 28($a0)\n"
+ "lw $a2, 24($a0)\n"
+ "lw $a1, 20($a0)\n"
+ "lw $t0, 16($a0)\n"
+ "sw $a3, 28($sp)\n"
+ "sw $a2, 24($sp)\n"
+ "sw $a1, 20($sp)\n"
+ "sw $t0, 16($sp)\n"
+ "lw $a3, 12($a0)\n"
+ "lw $a2, 8($a0)\n"
+ "lw $a1, 4($a0)\n"
+ "lw $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:lw $ra, 36($sp)\n"
+ "jr $ra\n"
+ " addiu $sp, $sp, 40\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
#endif
); // asm
} // namespace
+intptr_t Syscall::InvalidCall() {
+ // Explicitly pass eight zero arguments just in case.
+ return Call(kInvalidSyscallNumber, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
intptr_t Syscall::Call(int nr,
intptr_t p0,
intptr_t p1,
@@ -197,11 +256,15 @@
// TODO(nedeljko): Enable use of more than six parameters on architectures
// where that makes sense.
+#if defined(__mips__)
+ const intptr_t args[8] = {p0, p1, p2, p3, p4, p5, p6, p7};
+#else
DCHECK_EQ(p6, 0) << " Support for syscalls with more than six arguments not "
"added for this architecture";
DCHECK_EQ(p7, 0) << " Support for syscalls with more than six arguments not "
"added for this architecture";
const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
+#endif // defined(__mips__)
// Invoke our file-scope assembly code. The constraints have been picked
// carefully to match what the rest of the assembly code expects in input,
@@ -268,10 +331,64 @@
);
ret = inout;
}
+#elif defined(__mips__)
+ int err_status;
+ intptr_t ret = Syscall::SandboxSyscallRaw(nr, args, &err_status);
+
+ if (err_status) {
+ // On error, MIPS returns errno from syscall instead of -errno.
+ // The purpose of this negation is for SandboxSyscall() to behave
+ // more like it would on other architectures.
+ ret = -ret;
+ }
#else
#error "Unimplemented architecture"
#endif
return ret;
}
+void Syscall::PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx) {
+#if defined(__mips__)
+ // Mips ABI states that on error a3 CPU register has non zero value and if
+ // there is no error, it should be zero.
+ if (ret_val <= -1 && ret_val >= -4095) {
+ // |ret_val| followes the Syscall::Call() convention of being -errno on
+ // errors. In order to write correct value to return register this sign
+ // needs to be changed back.
+ ret_val = -ret_val;
+ SECCOMP_PARM4(ctx) = 1;
+ } else
+ SECCOMP_PARM4(ctx) = 0;
+#endif
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(ret_val);
+}
+
+#if defined(__mips__)
+intptr_t Syscall::SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_ret) {
+ register intptr_t ret __asm__("v0") = nr;
+ // a3 register becomes non zero on error.
+ register intptr_t err_stat __asm__("a3") = 0;
+ {
+ register const intptr_t* data __asm__("a0") = args;
+ asm volatile(
+ "la $t9, SyscallAsm\n"
+ "jalr $t9\n"
+ " nop\n"
+ : "=r"(ret), "=r"(err_stat)
+ : "0"(ret),
+ "r"(data)
+ // a2 is in the clober list so inline assembly can not change its
+ // value.
+ : "memory", "ra", "t9", "a2");
+ }
+
+ // Set an error status so it can be used outside of this function
+ *err_ret = err_stat;
+
+ return ret;
+}
+#endif // defined(__mips__)
+
} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
index 10a1253..3686df5 100644
--- a/sandbox/linux/seccomp-bpf/syscall.h
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -5,17 +5,30 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+#include <signal.h>
#include <stdint.h>
#include "base/macros.h"
#include "sandbox/sandbox_export.h"
+// Android's signal.h doesn't define ucontext etc.
+#if defined(OS_ANDROID)
+#include "sandbox/linux/services/android_ucontext.h"
+#endif
+
namespace sandbox {
// This purely static class can be used to perform system calls with some
// low-level control.
class SANDBOX_EXPORT Syscall {
public:
+ // InvalidCall() invokes Call() with a platform-appropriate syscall
+ // number that is guaranteed to not be implemented (i.e., normally
+ // returns -ENOSYS).
+ // This is primarily meant to be useful for writing sandbox policy
+ // unit tests.
+ static intptr_t InvalidCall();
+
// System calls can take up to six parameters (up to eight on some
// architectures). Traditionally, glibc
// implements this property by using variadic argument lists. This works, but
@@ -112,6 +125,11 @@
return Call(nr, 0, 0, 0, 0, 0, 0, 0, 0);
}
+ // Set the registers in |ctx| to match what they would be after a system call
+ // returning |ret_val|. |ret_val| must follow the Syscall::Call() convention
+ // of being -errno on errors.
+ static void PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx);
+
private:
// This performs system call |nr| with the arguments p0 to p7 from a constant
// userland address, which is for instance observable by seccomp-bpf filters.
@@ -129,6 +147,21 @@
intptr_t p6,
intptr_t p7);
+#if defined(__mips__)
+ // This function basically does on MIPS what SandboxSyscall() is doing on
+ // other architectures. However, because of specificity of MIPS regarding
+ // handling syscall errors, SandboxSyscall() is made as a wrapper for this
+ // function in order for SandboxSyscall() to behave more like on other
+ // architectures on places where return value from SandboxSyscall() is used
+ // directly (like in most tests).
+ // The syscall "nr" is called with arguments that are set in an array on which
+ // pointer "args" points to and an information weather there is an error or no
+ // is returned to SandboxSyscall() by err_stat.
+ static intptr_t SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_stat);
+#endif // defined(__mips__)
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
};
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
index 89cc1cb..d1c383b 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -16,14 +16,25 @@
uint32_t val;
do {
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+ // |num_| has been initialized to 4000, which we assume is also MIN_SYSCALL.
+ // This is true for Mips O32 ABI.
+ COMPILE_ASSERT(MIN_SYSCALL == __NR_Linux, min_syscall_should_be_4000);
+#else
// |num_| has been initialized to 0, which we assume is also MIN_SYSCALL.
// This true for supported architectures (Intel and ARM EABI).
COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero);
+#endif
val = num_;
+ // The syscall iterator always starts at zero.
+ // If zero is not a valid system call, iterator first returns MIN_SYSCALL -1
+ // before continuing to iterate.
+ if (num_ == 0 && MIN_SYSCALL != num_) {
+ num_ = MIN_SYSCALL - 1;
// First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
// on Intel architectures, but leaves room for private syscalls on ARM.
- if (num_ <= MAX_PUBLIC_SYSCALL) {
+ } else if (num_ <= MAX_PUBLIC_SYSCALL) {
if (invalid_only_ && num_ < MAX_PUBLIC_SYSCALL) {
num_ = MAX_PUBLIC_SYSCALL;
} else {
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
index 08a857a..6d553c8 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
@@ -29,7 +29,25 @@
}
}
-SANDBOX_TEST(SyscallIterator, PublicSyscallRange) {
+#if defined(__mips__)
+SANDBOX_TEST(SyscallIterator, PublicSyscallRangeMIPS) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+ SANDBOX_ASSERT(next == 0);
+
+ // Since on MIPS MIN_SYSCALL != 0 we need to move iterator to valid range.
+ next = iter.Next();
+ SANDBOX_ASSERT(next == MIN_SYSCALL - 1);
+
+ // The iterator should cover the public syscall range
+ // MIN_SYSCALL..MAX_PUBLIC_SYSCALL, without skipping syscalls.
+ for (uint32_t last = next; next < MAX_PUBLIC_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+}
+#else
+SANDBOX_TEST(SyscallIterator, PublicSyscallRangeIntelArm) {
SyscallIterator iter(false);
uint32_t next = iter.Next();
@@ -44,6 +62,7 @@
}
SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
}
+#endif // defined(__mips__)
#if defined(__arm__)
SANDBOX_TEST(SyscallIterator, ARMPrivateSyscallRange) {
@@ -103,7 +122,27 @@
}
}
-SANDBOX_TEST(SyscallIterator, InvalidOnly) {
+#if defined(__mips__)
+SANDBOX_TEST(SyscallIterator, InvalidOnlyMIPS) {
+ bool invalid_only = true;
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+ SANDBOX_ASSERT(next == 0);
+ // For Mips O32 ABI we're assuming MIN_SYSCALL == 4000.
+ SANDBOX_ASSERT(MIN_SYSCALL == 4000);
+
+ // Since on MIPS MIN_SYSCALL != 0, we need to move iterator to valid range
+ // The iterator should skip until the last invalid syscall in this range.
+ next = iter.Next();
+ SANDBOX_ASSERT(next == MIN_SYSCALL - 1);
+ next = iter.Next();
+ // First next invalid syscall should then be |MAX_PUBLIC_SYSCALL + 1|.
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+}
+
+#else
+
+SANDBOX_TEST(SyscallIterator, InvalidOnlyIntelArm) {
bool invalid_only = true;
SyscallIterator iter(invalid_only);
uint32_t next = iter.Next();
@@ -128,8 +167,9 @@
next = iter.Next();
}
SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
-#endif
+#endif // defined(__arm__)
}
+#endif // defined(__mips__)
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
index 4e142f4..38f31e0 100644
--- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -33,6 +33,10 @@
const int kMMapNr = __NR_mmap;
#endif
+TEST(Syscall, InvalidCallReturnsENOSYS) {
+ EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
+}
+
TEST(Syscall, WellKnownEntryPoint) {
// Test that Syscall::Call(-1) is handled specially. Don't do this on ARM,
// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
@@ -54,6 +58,9 @@
#else
EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
#endif
+#elif defined(__mips__)
+ // Opcode for MIPS sycall is in the lower 16-bits
+ EXPECT_EQ(0x0cu, (((uint32_t*)Syscall::Call(-1))[-1]) & 0x0000FFFF);
#else
#warning Incomplete test case; need port for target platform
#endif
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
index 4c42111..ce906fc 100644
--- a/sandbox/linux/seccomp-bpf/trap.cc
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -150,10 +150,19 @@
struct arch_sigsys sigsys;
memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+#if defined(__mips__)
+ // When indirect syscall (syscall(__NR_foo, ...)) is made on Mips, the
+ // number in register SECCOMP_SYSCALL(ctx) is always __NR_syscall and the
+ // real number of a syscall (__NR_foo) is in SECCOMP_PARM1(ctx)
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) &&
+ sigsys.nr != static_cast<int>(SECCOMP_PARM1(ctx));
+#else
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx));
+#endif
+
// Some more sanity checks.
if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
- sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
- sigsys.arch != SECCOMP_ARCH) {
+ sigsys_nr_is_bad || sigsys.arch != SECCOMP_ARCH) {
// TODO(markus):
// SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
// safe and can lead to bugs. We should eventually implement a different
@@ -168,13 +177,28 @@
if (sigsys.nr == __NR_clone) {
RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
}
- rc = Syscall::Call(sigsys.nr,
+#if defined(__mips__)
+ // Mips supports up to eight arguments for syscall.
+ // However, seccomp bpf can filter only up to six arguments, so using eight
+ // arguments has sense only when using UnsafeTrap() handler.
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx),
+ SECCOMP_PARM7(ctx),
+ SECCOMP_PARM8(ctx));
+#else
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
SECCOMP_PARM1(ctx),
SECCOMP_PARM2(ctx),
SECCOMP_PARM3(ctx),
SECCOMP_PARM4(ctx),
SECCOMP_PARM5(ctx),
SECCOMP_PARM6(ctx));
+#endif // defined(__mips__)
} else {
const ErrorCode& err = trap_array_[info->si_errno - 1];
if (!err.safe_) {
@@ -185,7 +209,9 @@
// is what we are showing to TrapFnc callbacks that the system call
// evaluator registered with the sandbox.
struct arch_seccomp_data data = {
- sigsys.nr, SECCOMP_ARCH, reinterpret_cast<uint64_t>(sigsys.ip),
+ static_cast<int>(SECCOMP_SYSCALL(ctx)),
+ SECCOMP_ARCH,
+ reinterpret_cast<uint64_t>(sigsys.ip),
{static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
@@ -201,7 +227,7 @@
// Update the CPU register that stores the return code of the system call
// that we just handled, and restore "errno" to the value that it had
// before entering the signal handler.
- SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
+ Syscall::PutValueInUcontext(rc, ctx);
errno = old_errno;
return;