Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / sandbox / linux / seccomp-bpf / syscall.cc
index 64c0b8e..b0a41b0 100644 (file)
@@ -8,11 +8,21 @@
 #include <errno.h>
 
 #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
@@ -49,10 +59,10 @@ asm(// We need to be able to tell the kernel exactly where we made a
     // 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"
@@ -67,10 +77,10 @@ asm(// We need to be able to tell the kernel exactly where we made a
     "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"
@@ -170,18 +180,70 @@ asm(// We need to be able to tell the kernel exactly where we made a
 #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,
                        intptr_t p2,
                        intptr_t p3,
                        intptr_t p4,
-                       intptr_t p5) {
+                       intptr_t p5,
+                       intptr_t p6,
+                       intptr_t p7) {
   // We rely on "intptr_t" to be the exact size as a "void *". This is
   // typically true, but just in case, we add a check. The language
   // specification allows platforms some leeway in cases, where
@@ -192,7 +254,17 @@ intptr_t Syscall::Call(int nr,
   COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t),
                  pointer_types_and_intptr_must_be_exactly_the_same_size);
 
+  // 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,
@@ -259,10 +331,64 @@ intptr_t Syscall::Call(int nr,
         );
     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