#include "config.h"
#include "OpenSyscall.h"
#if ENABLE(SECCOMP_FILTERS)
#include "ArgumentCoders.h"
#include "SyscallPolicy.h"
#include <errno.h>
#include <fcntl.h>
#include <seccomp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wtf/text/WTFString.h>
namespace WebKit {
COMPILE_ASSERT(!O_RDONLY, O_RDONLY);
COMPILE_ASSERT(O_WRONLY == 1, O_WRONLY);
COMPILE_ASSERT(O_RDWR == 2, O_RDWR);
std::unique_ptr<Syscall> OpenSyscall::createFromOpenatContext(mcontext_t* context)
{
auto open = std::make_unique<OpenSyscall>(nullptr);
open->setFlags(context->gregs[REG_ARG2]);
open->setMode(context->gregs[REG_ARG3]);
open->setContext(context);
int fd = context->gregs[REG_ARG0];
char* path = reinterpret_cast<char*>(context->gregs[REG_ARG1]);
if (path[0] == '/') {
open->setPath(path);
return WTF::move(open);
}
struct stat pathStat;
if (fstat(fd, &pathStat) == -1) {
context->gregs[REG_SYSCALL] = -errno;
return nullptr;
}
if (!S_ISDIR(pathStat.st_mode)) {
context->gregs[REG_SYSCALL] = -ENOTDIR;
return nullptr;
}
char fdLinkPath[32];
snprintf(fdLinkPath, sizeof(fdLinkPath), "/proc/self/fd/%d", fd);
char fdPath[PATH_MAX];
ssize_t size = readlink(fdLinkPath, fdPath, sizeof(fdPath) - 1);
if (size == -1) {
context->gregs[REG_SYSCALL] = -errno;
return nullptr;
}
if (size + strlen(path) + 2 > PATH_MAX) {
context->gregs[REG_SYSCALL] = -ENAMETOOLONG;
return nullptr;
}
sprintf(&fdPath[size], "/%s", path);
open->setPath(fdPath);
return WTF::move(open);
}
std::unique_ptr<Syscall> OpenSyscall::createFromCreatContext(mcontext_t* context)
{
auto open = std::make_unique<OpenSyscall>(nullptr);
open->setPath(CString(reinterpret_cast<char*>(context->gregs[REG_ARG0])));
open->setFlags(O_CREAT | O_WRONLY | O_TRUNC);
open->setMode(context->gregs[REG_ARG1]);
open->setContext(context);
return WTF::move(open);
}
OpenSyscall::OpenSyscall(mcontext_t* context)
: Syscall(__NR_open, context)
, m_flags(0)
, m_mode(0)
{
if (!context)
return;
m_path = CString(reinterpret_cast<char*>(context->gregs[REG_ARG0]));
m_flags = context->gregs[REG_ARG1];
m_mode = context->gregs[REG_ARG2];
}
void OpenSyscall::setResult(const SyscallResult* result)
{
ASSERT(context() && result->type() == type());
const OpenSyscallResult* openResult = static_cast<const OpenSyscallResult*>(result);
if (openResult->fd() >= 0)
context()->gregs[REG_SYSCALL] = dup(openResult->fd());
else
context()->gregs[REG_SYSCALL] = -openResult->errorNumber();
}
std::unique_ptr<SyscallResult> OpenSyscall::execute(const SyscallPolicy& policy)
{
if (!strncmp("/proc/self/", m_path.data(), 11)) {
String resolvedSelfPath = ASCIILiteral("/proc/") + String::number(getppid()) + &m_path.data()[10];
m_path = resolvedSelfPath.utf8().data();
}
SyscallPolicy::Permission permission = SyscallPolicy::NotAllowed;
if (m_flags & O_RDWR)
permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::ReadAndWrite);
else if (m_flags & O_WRONLY)
permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Write);
else
permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Read);
if (m_flags & O_CREAT || m_flags & O_EXCL)
permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Write);
if (!policy.hasPermissionForPath(m_path.data(), permission))
return std::make_unique<OpenSyscallResult>(-1, EACCES);
int fd = open(m_path.data(), m_flags, m_mode);
int errorNumber = fd == -1 ? errno : 0;
return std::make_unique<OpenSyscallResult>(fd, errorNumber);
}
void OpenSyscall::encode(IPC::ArgumentEncoder& encoder) const
{
encoder << type();
encoder << m_path;
encoder << m_flags;
encoder << m_mode;
}
bool OpenSyscall::decode(IPC::ArgumentDecoder* decoder)
{
if (!decoder->decode(m_path))
return false;
if (!decoder->decode(m_flags))
return false;
return decoder->decode(m_mode);
}
OpenSyscallResult::OpenSyscallResult(int fd, int errorNumber)
: SyscallResult(__NR_open)
, m_fd(fd)
, m_errorNumber(errorNumber)
{
}
OpenSyscallResult::~OpenSyscallResult()
{
if (m_fd >= 0)
close(m_fd);
}
void OpenSyscallResult::encode(IPC::ArgumentEncoder& encoder) const
{
encoder << type();
if (m_fd >= 0) {
IPC::Attachment attachment(m_fd);
encoder.addAttachment(attachment);
}
encoder << m_errorNumber;
}
bool OpenSyscallResult::decode(IPC::ArgumentDecoder* decoder, int fd)
{
if (fd >= 0)
m_fd = fd;
return decoder->decode(m_errorNumber);
}
}
#endif // ENABLE(SECCOMP_FILTERS)