summaryrefslogtreecommitdiffstats
path: root/sandbox/linux/seccomp/clone.cc
blob: 28a358491c8466c69de8870830bc22cf4540ce18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "debug.h"
#include "sandbox_impl.h"

namespace playground {

int Sandbox::sandbox_clone(int flags, void* stack, int* pid, int* ctid,
                           void* tls, void *wrapper_sp) {
  Debug::syscall(__NR_clone, "Executing handler");
  struct {
    int       sysnum;
    long long cookie;
    Clone     clone_req;
  } __attribute__((packed)) request;
  request.sysnum               = __NR_clone;
  request.cookie               = cookie();
  request.clone_req.flags      = flags;
  request.clone_req.stack      = stack;
  request.clone_req.pid        = pid;
  request.clone_req.ctid       = ctid;
  request.clone_req.tls        = tls;

  // Pass along the address on the stack where syscallWrapper() stored the
  // original CPU registers. These registers will be restored in the newly
  // created thread prior to returning from the wrapped system call.
  #if defined(__x86_64__)
  memcpy(&request.clone_req.regs64, wrapper_sp,
         sizeof(request.clone_req.regs64) + sizeof(void *));
  #elif defined(__i386__)
  memcpy(&request.clone_req.regs32, wrapper_sp,
         sizeof(request.clone_req.regs32) + sizeof(void *));
  #else
  #error Unsupported target platform
  #endif

  long rc;
  SysCalls sys;
  if (write(sys, processFdPub(), &request, sizeof(request)) !=
      sizeof(request) ||
      read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) {
    die("Failed to forward clone() request [sandbox]");
  }
  return static_cast<int>(rc);
}

bool Sandbox::process_clone(int parentMapsFd, int sandboxFd, int threadFdPub,
                            int threadFd, SecureMem::Args* mem) {
  // Read request
  Clone clone_req;
  SysCalls sys;
  if (read(sys, sandboxFd, &clone_req, sizeof(clone_req)) !=sizeof(clone_req)){
    die("Failed to read parameters for clone() [process]");
  }

  // TODO(markus): add policy restricting parameters for clone
  if ((clone_req.flags & ~CLONE_DETACHED) != (CLONE_VM|CLONE_FS|CLONE_FILES|
      CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
      CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID)) {
    SecureMem::abandonSystemCall(threadFd, -EPERM);
    return false;
  } else {
    SecureMem::Args* newMem = getSecureMem();
    if (!newMem) {
      SecureMem::abandonSystemCall(threadFd, -ENOMEM);
      return false;
    } else {
      // clone() has unusual semantics. We don't want to return back into the
      // trusted thread, but instead we need to continue execution at the IP
      // where we got called initially.
      SecureMem::lockSystemCall(parentMapsFd, mem);
      mem->ret              = clone_req.ret;
      #if defined(__x86_64__)
      mem->rbp              = clone_req.regs64.rbp;
      mem->rbx              = clone_req.regs64.rbx;
      mem->rcx              = clone_req.regs64.rcx;
      mem->rdx              = clone_req.regs64.rdx;
      mem->rsi              = clone_req.regs64.rsi;
      mem->rdi              = clone_req.regs64.rdi;
      mem->r8               = clone_req.regs64.r8;
      mem->r9               = clone_req.regs64.r9;
      mem->r10              = clone_req.regs64.r10;
      mem->r11              = clone_req.regs64.r11;
      mem->r12              = clone_req.regs64.r12;
      mem->r13              = clone_req.regs64.r13;
      mem->r14              = clone_req.regs64.r14;
      mem->r15              = clone_req.regs64.r15;
      #elif defined(__i386__)
      mem->ebp              = clone_req.regs32.ebp;
      mem->edi              = clone_req.regs32.edi;
      mem->esi              = clone_req.regs32.esi;
      mem->edx              = clone_req.regs32.edx;
      mem->ecx              = clone_req.regs32.ecx;
      mem->ebx              = clone_req.regs32.ebx;
      #else
      #error Unsupported target platform
      #endif
      newMem->sequence      = 0;
      newMem->shmId         = -1;
      mem->newSecureMem     = newMem;
      mem->processFdPub     = processFdPub_;
      mem->cloneFdPub       = cloneFdPub_;

      SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem,
                                __NR_clone, clone_req.flags, clone_req.stack,
                                clone_req.pid, clone_req.ctid, clone_req.tls);
      return true;
    }
  }
}

} // namespace