// 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 { #ifndef IPC_PRIVATE #define IPC_PRIVATE 0 #endif #ifndef IPC_RMID #define IPC_RMID 0 #endif #ifndef IPC_64 #define IPC_64 256 #endif #if defined(__NR_shmget) void* Sandbox::sandbox_shmat(int shmid, const void* shmaddr, int shmflg) { long long tm; Debug::syscall(&tm, __NR_shmat, "Executing handler"); struct { int sysnum; long long cookie; ShmAt shmat_req; } __attribute__((packed)) request; request.sysnum = __NR_shmat; request.cookie = cookie(); request.shmat_req.shmid = shmid; request.shmat_req.shmaddr = shmaddr; request.shmat_req.shmflg = shmflg; 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 shmat() request [sandbox]"); } Debug::elapsed(tm, __NR_shmat); return reinterpret_cast(rc); } int Sandbox::sandbox_shmctl(int shmid, int cmd, void* buf) { long long tm; Debug::syscall(&tm, __NR_shmctl, "Executing handler"); struct { int sysnum; long long cookie; ShmCtl shmctl_req; } __attribute__((packed)) request; request.sysnum = __NR_shmctl; request.cookie = cookie(); request.shmctl_req.shmid = shmid; request.shmctl_req.cmd = cmd; request.shmctl_req.buf = buf; 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 shmctl() request [sandbox]"); } Debug::elapsed(tm, __NR_shmctl); return static_cast(rc); } int Sandbox::sandbox_shmdt(const void* shmaddr) { long long tm; Debug::syscall(&tm, __NR_shmdt, "Executing handler"); struct { int sysnum; long long cookie; ShmDt shmdt_req; } __attribute__((packed)) request; request.sysnum = __NR_shmdt; request.cookie = cookie(); request.shmdt_req.shmaddr = shmaddr; 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 shmdt() request [sandbox]"); } Debug::elapsed(tm, __NR_shmdt); return static_cast(rc); } int Sandbox::sandbox_shmget(int key, size_t size, int shmflg) { long long tm; Debug::syscall(&tm, __NR_shmget, "Executing handler"); struct { int sysnum; long long cookie; ShmGet shmget_req; } __attribute__((packed)) request; request.sysnum = __NR_shmget; request.cookie = cookie(); request.shmget_req.key = key; request.shmget_req.size = size; request.shmget_req.shmflg = shmflg; 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 shmget() request [sandbox]"); } Debug::elapsed(tm, __NR_shmget); return static_cast(rc); } bool Sandbox::process_shmat(int parentMapsFd, int sandboxFd, int threadFdPub, int threadFd, SecureMem::Args* mem) { // Read request ShmAt shmat_req; SysCalls sys; if (read(sys, sandboxFd, &shmat_req, sizeof(shmat_req)) != sizeof(shmat_req)) { die("Failed to read parameters for shmat() [process]"); } // We only allow attaching to the shm identifier that was returned by // the most recent call to shmget(IPC_PRIVATE) if (shmat_req.shmaddr || shmat_req.shmflg || shmat_req.shmid != mem->shmId) { mem->shmId = -1; SecureMem::abandonSystemCall(threadFd, -EINVAL); return false; } mem->shmId = -1; SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_shmat, shmat_req.shmid, shmat_req.shmaddr, shmat_req.shmflg); return true; } bool Sandbox::process_shmctl(int parentMapsFd, int sandboxFd, int threadFdPub, int threadFd, SecureMem::Args* mem) { // Read request ShmCtl shmctl_req; SysCalls sys; if (read(sys, sandboxFd, &shmctl_req, sizeof(shmctl_req)) != sizeof(shmctl_req)) { die("Failed to read parameters for shmctl() [process]"); } // The only shmctl() operation that we need to support is removal. This // operation is generally safe. if ((shmctl_req.cmd & ~(IPC_64 | IPC_RMID)) || shmctl_req.buf) { mem->shmId = -1; SecureMem::abandonSystemCall(threadFd, -EINVAL); return false; } mem->shmId = -1; SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_shmctl, shmctl_req.shmid, shmctl_req.cmd, shmctl_req.buf); return true; } bool Sandbox::process_shmdt(int parentMapsFd, int sandboxFd, int threadFdPub, int threadFd, SecureMem::Args* mem) { // Read request ShmDt shmdt_req; SysCalls sys; if (read(sys, sandboxFd, &shmdt_req, sizeof(shmdt_req)) != sizeof(shmdt_req)) { die("Failed to read parameters for shmdt() [process]"); } // Detaching shared memory segments it generally safe, but just in case // of a kernel bug, we make sure that the address does not fall into any // of the reserved memory regions. ProtectedMap::const_iterator iter = protectedMap_.lower_bound( (void *)shmdt_req.shmaddr); if (iter != protectedMap_.begin()) { --iter; } for (; iter != protectedMap_.end() && iter->first <= shmdt_req.shmaddr; ++iter){ if (shmdt_req.shmaddr < reinterpret_cast( reinterpret_cast(iter->first) + iter->second) && shmdt_req.shmaddr >= iter->first) { mem->shmId = -1; SecureMem::abandonSystemCall(threadFd, -EINVAL); return false; } } mem->shmId = -1; SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_shmdt, shmdt_req.shmaddr); return true; } bool Sandbox::process_shmget(int parentMapsFd, int sandboxFd, int threadFdPub, int threadFd, SecureMem::Args* mem) { // Read request ShmGet shmget_req; SysCalls sys; if (read(sys, sandboxFd, &shmget_req, sizeof(shmget_req)) != sizeof(shmget_req)) { die("Failed to read parameters for shmget() [process]"); } // We do not want to allow the sandboxed application to access arbitrary // shared memory regions. We only allow it to access regions that it // created itself. if (shmget_req.key != IPC_PRIVATE || shmget_req.shmflg & ~0777) { mem->shmId = -1; SecureMem::abandonSystemCall(threadFd, -EINVAL); return false; } mem->shmId = -1; SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_shmget, shmget_req.key, shmget_req.size, shmget_req.shmflg); return true; } #endif #if defined(__NR_ipc) #ifndef SHMAT #define SHMAT 21 #endif #ifndef SHMDT #define SHMDT 22 #endif #ifndef SHMGET #define SHMGET 23 #endif #ifndef SHMCTL #define SHMCTL 24 #endif int Sandbox::sandbox_ipc(unsigned call, int first, int second, int third, void* ptr, long fifth) { long long tm; Debug::syscall(&tm, __NR_ipc, "Executing handler", call); struct { int sysnum; long long cookie; IPC ipc_req; } __attribute__((packed)) request; request.sysnum = __NR_ipc; request.cookie = cookie(); request.ipc_req.call = call; request.ipc_req.first = first; request.ipc_req.second = second; request.ipc_req.third = third; request.ipc_req.ptr = ptr; request.ipc_req.fifth = fifth; 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 ipc() request [sandbox]"); } Debug::elapsed(tm, __NR_ipc, call); return static_cast(rc); } bool Sandbox::process_ipc(int parentMapsFd, int sandboxFd, int threadFdPub, int threadFd, SecureMem::Args* mem) { // Read request IPC ipc_req; SysCalls sys; if (read(sys, sandboxFd, &ipc_req, sizeof(ipc_req)) != sizeof(ipc_req)) { die("Failed to read parameters for ipc() [process]"); } // We do not support all of the SysV IPC calls. In fact, we only support // the minimum feature set necessary for Chrome's renderers to share memory // with the X server. switch (ipc_req.call) { case SHMAT: { // We only allow attaching to the shm identifier that was returned by // the most recent call to shmget(IPC_PRIVATE) if (ipc_req.ptr || ipc_req.second || ipc_req.first != mem->shmId) { goto deny; } accept: mem->shmId = -1; SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_ipc, ipc_req.call, ipc_req.first, ipc_req.second, ipc_req.third, ipc_req.ptr, ipc_req.fifth); return true; } case SHMCTL: // The only shmctl() operation that we need to support is removal. This // operation is generally safe. if ((ipc_req.second & ~(IPC_64 | IPC_RMID)) || ipc_req.ptr) { goto deny; } else { goto accept; } case SHMDT: { // Detaching shared memory segments it generally safe, but just in case // of a kernel bug, we make sure that the address does not fall into any // of the reserved memory regions. ProtectedMap::const_iterator iter = protectedMap_.lower_bound( (void *)ipc_req.ptr); if (iter != protectedMap_.begin()) { --iter; } for (; iter != protectedMap_.end() && iter->first <=ipc_req.ptr; ++iter){ if (ipc_req.ptr < reinterpret_cast( reinterpret_cast(iter->first) + iter->second) && ipc_req.ptr >= iter->first) { goto deny; } } goto accept; } case SHMGET: // We do not want to allow the sandboxed application to access arbitrary // shared memory regions. We only allow it to access regions that it // created itself. if (ipc_req.first != IPC_PRIVATE || ipc_req.third & ~0777) { goto deny; } else { goto accept; } default: // Other than SysV shared memory, we do not actually need to support any // other SysV IPC calls. deny: mem->shmId = -1; SecureMem::abandonSystemCall(threadFd, -EINVAL); return false; } } #endif } // namespace