chromium/chromium-92.0.4515.107-linux-sandbox-syscall-broker-use-struct-kernel_stat.patch

1385 lines
52 KiB
Diff
Raw Permalink Normal View History

From 4b438323d68840453b5ef826c3997568e2e0e8c7 Mon Sep 17 00:00:00 2001
From: Matthew Denton <mpdenton@chromium.org>
Date: Mon, 19 Jul 2021 14:03:13 +0000
Subject: [PATCH] Reland "Reland "Linux sandbox syscall broker: use struct
kernel_stat""
This reverts commit ff277a52ece0b216617d770f201ed66955fe70b9.
Reason for revert: reland
The fix included in the reland is that fstatat64() needs to be
allowed in the broker process's seccomp policy.
This CL also includes some extra tests that the kernel_stat structures
match the layout the kernel expects.
Bug: 1164975, 1199431
Test: trogdor Chromebook successfully boots and allows login.
Original change's description:
> Revert "Reland "Linux sandbox syscall broker: use struct kernel_stat""
>
> This reverts commit cffbc4432af79f720ae3c75dff380b853701bd64.
>
> Reason for revert: https://bugs.chromium.org/p/chromium/issues/detail?id=1199431
>
> Original change's description:
> > Reland "Linux sandbox syscall broker: use struct kernel_stat"
> >
> > This reverts commit 23030dc650cdfa22631f25bef937905f27f06a2c.
> >
> > Original change's description:
> > > Revert "Linux sandbox syscall broker: use struct kernel_stat"
> > >
> > > This reverts commit 784b0fcd8a3ca6bcd3acb9cfd624ec9cbbac2789.
> > >
> > > Reason for revert: Causing failure in
> > > Step "sandbox_linux_unittests" failing on builder "Linux ChromiumOS MSan Tests"
> > > See crbug.com/1198480
> > >
> > > Original change's description:
> > > > Linux sandbox syscall broker: use struct kernel_stat
> > > >
> > > > The struct stat used in libc is different (in size and field ordering)
> > > > from the structure assumed by the Linux kernel. So, when emulating
> > > > system calls, we need to use the struct definition the kernel expects.
> > > >
> > > > This CL adds linux_stat.h that includes definitions of the different
> > > > kernel structs.
> > > >
> > > > Change-Id: I53cad35c2251dff0f6b7ea77528cfa58ef3cab4a
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2780876
> > > > Commit-Queue: Matthew Denton <mpdenton@chromium.org>
> > > > Reviewed-by: Robert Sesek <rsesek@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#871767}
> > >
> > > Change-Id: Icbec38f2103c8424dec79ab1870b97c3e83f9361
> > > No-Presubmit: true
> > > No-Tree-Checks: true
> > > No-Try: true
> > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821812
> > > Auto-Submit: Victor Vianna <victorvianna@google.com>
> > > Owners-Override: Victor Vianna <victorvianna@google.com>
> > > Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> > > Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> > > Cr-Commit-Position: refs/heads/master@{#871882}
> >
> > Change-Id: I1f39bb5242961474def594ff7dbea52009f2cee4
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2824115
> > Auto-Submit: Matthew Denton <mpdenton@chromium.org>
> > Commit-Queue: Matthew Denton <mpdenton@chromium.org>
> > Reviewed-by: Robert Sesek <rsesek@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#872812}
>
> Fixed: 1199431
> Change-Id: Iebfc0c48201bf22ff9c54d8d5c8a43d26a880098
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2830459
> Auto-Submit: Kyle Horimoto <khorimoto@chromium.org>
> Commit-Queue: Matthew Denton <mpdenton@chromium.org>
> Commit-Queue: Kinuko Yasuda <kinuko@chromium.org>
> Reviewed-by: Matthew Denton <mpdenton@chromium.org>
> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
> Owners-Override: Kinuko Yasuda <kinuko@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#873173}
Change-Id: Ibe6a485070f33489aaa157b51b908c2d23d174d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2848936
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Matthew Denton <mpdenton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#902981}
---
sandbox/linux/BUILD.gn | 1 +
.../seccomp_broker_process_unittest.cc | 40 +++-
sandbox/linux/seccomp-bpf-helpers/DEPS | 1 -
...scall_parameters_restrictions_unittests.cc | 4 -
sandbox/linux/services/syscall_wrappers.cc | 50 ++++-
sandbox/linux/services/syscall_wrappers.h | 15 ++
.../services/syscall_wrappers_unittest.cc | 129 +++++++++++-
sandbox/linux/syscall_broker/DEPS | 3 +-
sandbox/linux/syscall_broker/broker_client.cc | 4 +-
sandbox/linux/syscall_broker/broker_client.h | 4 +-
sandbox/linux/syscall_broker/broker_host.cc | 23 ++-
.../syscall_broker/broker_process_unittest.cc | 74 +++----
.../remote_syscall_arg_handler_unittest.cc | 36 ++--
.../syscall_broker/syscall_dispatcher.cc | 67 ++++---
.../linux/syscall_broker/syscall_dispatcher.h | 27 ++-
sandbox/linux/system_headers/linux_stat.h | 188 ++++++++++++++++++
sandbox/linux/system_headers/linux_time.h | 26 +++
sandbox/linux/tests/test_utils.cc | 15 ++
sandbox/linux/tests/test_utils.h | 2 +
.../policy/linux/bpf_broker_policy_linux.cc | 4 +-
20 files changed, 595 insertions(+), 118 deletions(-)
create mode 100644 sandbox/linux/system_headers/linux_stat.h
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 2f778dd0bc..ccbbc91716 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -443,6 +443,7 @@ source_set("sandbox_services_headers") {
"system_headers/linux_ptrace.h",
"system_headers/linux_seccomp.h",
"system_headers/linux_signal.h",
+ "system_headers/linux_stat.h",
"system_headers/linux_syscalls.h",
"system_headers/linux_time.h",
"system_headers/linux_ucontext.h",
diff --git a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
index 9da9c68911..8a941983b1 100644
--- a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
+++ b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -34,6 +34,7 @@
#include "sandbox/linux/syscall_broker/broker_file_permission.h"
#include "sandbox/linux/syscall_broker/broker_process.h"
#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_stat.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
#include "sandbox/linux/tests/scoped_temporary_file.h"
#include "sandbox/linux/tests/test_utils.h"
@@ -202,6 +203,26 @@ namespace {
// not accept this as a valid error number. E.g. bionic accepts up to 255, glibc
// and musl up to 4096.
const int kFakeErrnoSentinel = 254;
+
+void ConvertKernelStatToLibcStat(default_stat_struct& in_stat,
+ struct stat& out_stat) {
+ out_stat.st_dev = in_stat.st_dev;
+ out_stat.st_ino = in_stat.st_ino;
+ out_stat.st_mode = in_stat.st_mode;
+ out_stat.st_nlink = in_stat.st_nlink;
+ out_stat.st_uid = in_stat.st_uid;
+ out_stat.st_gid = in_stat.st_gid;
+ out_stat.st_rdev = in_stat.st_rdev;
+ out_stat.st_size = in_stat.st_size;
+ out_stat.st_blksize = in_stat.st_blksize;
+ out_stat.st_blocks = in_stat.st_blocks;
+ out_stat.st_atim.tv_sec = in_stat.st_atime_;
+ out_stat.st_atim.tv_nsec = in_stat.st_atime_nsec_;
+ out_stat.st_mtim.tv_sec = in_stat.st_mtime_;
+ out_stat.st_mtim.tv_nsec = in_stat.st_mtime_nsec_;
+ out_stat.st_ctim.tv_sec = in_stat.st_ctime_;
+ out_stat.st_ctim.tv_nsec = in_stat.st_ctime_nsec_;
+}
} // namespace
// There are a variety of ways to make syscalls in a sandboxed process. One is
@@ -217,6 +238,10 @@ class Syscaller {
virtual int Open(const char* filepath, int flags) = 0;
virtual int Access(const char* filepath, int mode) = 0;
+ // NOTE: we use struct stat instead of default_stat_struct, to make the libc
+ // syscaller simpler. Copying from default_stat_struct (the structure returned
+ // from a stat sycall) to struct stat (the structure exposed by a libc to its
+ // users) is simpler than going in the opposite direction.
virtual int Stat(const char* filepath,
bool follow_links,
struct stat* statbuf) = 0;
@@ -243,8 +268,12 @@ class IPCSyscaller : public Syscaller {
int Stat(const char* filepath,
bool follow_links,
struct stat* statbuf) override {
- return broker_->GetBrokerClientSignalBased()->Stat(filepath, follow_links,
- statbuf);
+ default_stat_struct buf;
+ int ret = broker_->GetBrokerClientSignalBased()->DefaultStatForTesting(
+ filepath, follow_links, &buf);
+ if (ret >= 0)
+ ConvertKernelStatToLibcStat(buf, *statbuf);
+ return ret;
}
int Rename(const char* oldpath, const char* newpath) override {
@@ -300,10 +329,13 @@ class DirectSyscaller : public Syscaller {
int Stat(const char* filepath,
bool follow_links,
struct stat* statbuf) override {
- int ret = follow_links ? syscall(__NR_stat, filepath, statbuf)
- : syscall(__NR_lstat, filepath, statbuf);
+ struct kernel_stat buf;
+ int ret = syscall(__NR_newfstatat, AT_FDCWD, filepath, &buf,
+ follow_links ? 0 : AT_SYMLINK_NOFOLLOW);
if (ret < 0)
return -errno;
+
+ ConvertKernelStatToLibcStat(buf, *statbuf);
return ret;
}
diff --git a/sandbox/linux/seccomp-bpf-helpers/DEPS b/sandbox/linux/seccomp-bpf-helpers/DEPS
index 4419fd1da3..95d1bb6cbb 100644
--- a/sandbox/linux/seccomp-bpf-helpers/DEPS
+++ b/sandbox/linux/seccomp-bpf-helpers/DEPS
@@ -3,5 +3,4 @@ include_rules = [
"+sandbox/linux/seccomp-bpf",
"+sandbox/linux/services",
"+sandbox/linux/system_headers",
- "+third_party/lss/linux_syscall_support.h",
]
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
index 903e702eab..76c393032c 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -37,10 +37,6 @@
#include "sandbox/linux/system_headers/linux_time.h"
#include "sandbox/linux/tests/unit_tests.h"
-#if !defined(OS_ANDROID)
-#include "third_party/lss/linux_syscall_support.h" // for MAKE_PROCESS_CPUCLOCK
-#endif
-
namespace sandbox {
namespace {
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc
index fcfd2aa129..3bec18a14e 100644
--- a/sandbox/linux/services/syscall_wrappers.cc
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -4,6 +4,7 @@
#include "sandbox/linux/services/syscall_wrappers.h"
+#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <setjmp.h>
@@ -14,11 +15,13 @@
#include <unistd.h>
#include <cstring>
+#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "sandbox/linux/system_headers/capability.h"
#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/system_headers/linux_stat.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
namespace sandbox {
@@ -217,7 +220,7 @@ asm(
#undef STR
#undef XSTR
-#endif
+#endif // defined(ARCH_CPU_X86_FAMILY)
int sys_sigaction(int signum,
const struct sigaction* act,
@@ -241,7 +244,7 @@ int sys_sigaction(int signum,
#error "Unsupported architecture."
#endif
}
-#endif
+#endif // defined(ARCH_CPU_X86_FAMILY)
}
LinuxSigAction linux_oldact = {};
@@ -259,6 +262,47 @@ int sys_sigaction(int signum,
return result;
}
-#endif // defined(MEMORY_SANITIZER)
+#endif // !defined(OS_NACL_NONSFI)
+
+int sys_stat(const char* path, struct kernel_stat* stat_buf) {
+ int res;
+#if !defined(__NR_stat)
+ res = syscall(__NR_newfstatat, AT_FDCWD, path, stat_buf, 0);
+#else
+ res = syscall(__NR_stat, path, stat_buf);
+#endif
+ if (res == 0)
+ MSAN_UNPOISON(stat_buf, sizeof(*stat_buf));
+ return res;
+}
+
+int sys_lstat(const char* path, struct kernel_stat* stat_buf) {
+ int res;
+#if !defined(__NR_lstat)
+ res = syscall(__NR_newfstatat, AT_FDCWD, path, stat_buf, AT_SYMLINK_NOFOLLOW);
+#else
+ res = syscall(__NR_lstat, path, stat_buf);
+#endif
+ if (res == 0)
+ MSAN_UNPOISON(stat_buf, sizeof(*stat_buf));
+ return res;
+}
+
+int sys_fstatat64(int dirfd,
+ const char* pathname,
+ struct kernel_stat64* stat_buf,
+ int flags) {
+#if defined(__NR_fstatat64)
+ int res = syscall(__NR_fstatat64, dirfd, pathname, stat_buf, flags);
+ if (res == 0)
+ MSAN_UNPOISON(stat_buf, sizeof(*stat_buf));
+ return res;
+#else // defined(__NR_fstatat64)
+ // We should not reach here on 64-bit systems, as the *stat*64() are only
+ // necessary on 32-bit.
+ RAW_CHECK(false);
+ return -ENOSYS;
+#endif
+}
} // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h
index 1975bfbd88..b55340e4a2 100644
--- a/sandbox/linux/services/syscall_wrappers.h
+++ b/sandbox/linux/services/syscall_wrappers.h
@@ -17,6 +17,8 @@ struct sock_fprog;
struct rlimit64;
struct cap_hdr;
struct cap_data;
+struct kernel_stat;
+struct kernel_stat64;
namespace sandbox {
@@ -84,6 +86,19 @@ SANDBOX_EXPORT int sys_sigaction(int signum,
const struct sigaction* act,
struct sigaction* oldact);
+// Some architectures do not have stat() and lstat() syscalls. In that case,
+// these wrappers will use newfstatat(), which is available on all other
+// architectures, with the same capabilities as stat() and lstat().
+SANDBOX_EXPORT int sys_stat(const char* path, struct kernel_stat* stat_buf);
+SANDBOX_EXPORT int sys_lstat(const char* path, struct kernel_stat* stat_buf);
+
+// Takes care of unpoisoning |stat_buf| for MSAN. Check-fails if fstatat64() is
+// not a supported syscall on the current platform.
+SANDBOX_EXPORT int sys_fstatat64(int dirfd,
+ const char* pathname,
+ struct kernel_stat64* stat_buf,
+ int flags);
+
} // namespace sandbox
#endif // SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
diff --git a/sandbox/linux/services/syscall_wrappers_unittest.cc b/sandbox/linux/services/syscall_wrappers_unittest.cc
index 32820f60a8..64b9cea80f 100644
--- a/sandbox/linux/services/syscall_wrappers_unittest.cc
+++ b/sandbox/linux/services/syscall_wrappers_unittest.cc
@@ -5,15 +5,19 @@
#include "sandbox/linux/services/syscall_wrappers.h"
#include <stdint.h>
+#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <cstring>
+#include "base/logging.h"
+#include "base/memory/page_size.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/system_headers/linux_stat.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -93,6 +97,129 @@ TEST(SyscallWrappers, LinuxSigSet) {
linux_sigset);
}
+TEST(SyscallWrappers, Stat) {
+ // Create a file to stat, with 12 bytes of data.
+ ScopedTemporaryFile tmp_file;
+ EXPECT_EQ(12, write(tmp_file.fd(), "blahblahblah", 12));
+
+ // To test we have the correct stat structures for each kernel/platform, we
+ // will right-align them on a page, with a guard page after.
+ char* two_pages = static_cast<char*>(TestUtils::MapPagesOrDie(2));
+ TestUtils::MprotectLastPageOrDie(two_pages, 2);
+ char* page1_end = two_pages + base::GetPageSize();
+
+ // First, check that calling stat with |stat_buf| pointing to the last byte on
+ // a page causes EFAULT.
+ int res = sys_stat(tmp_file.full_file_name(),
+ reinterpret_cast<struct kernel_stat*>(page1_end - 1));
+ ASSERT_EQ(res, -1);
+ ASSERT_EQ(errno, EFAULT);
+
+ // Now, check that we have the correctly sized stat structure.
+ struct kernel_stat* sb = reinterpret_cast<struct kernel_stat*>(
+ page1_end - sizeof(struct kernel_stat));
+ // Memset to c's so we can check the kernel zero'd the padding...
+ memset(sb, 'c', sizeof(struct kernel_stat));
+ res = sys_stat(tmp_file.full_file_name(), sb);
+ ASSERT_EQ(res, 0);
+
+ // Following fields may never be consistent but should be non-zero.
+ // Don't trust the platform to define fields with any particular sign.
+ EXPECT_NE(0u, static_cast<unsigned int>(sb->st_dev));
+ EXPECT_NE(0u, static_cast<unsigned int>(sb->st_ino));
+ EXPECT_NE(0u, static_cast<unsigned int>(sb->st_mode));
+ EXPECT_NE(0u, static_cast<unsigned int>(sb->st_blksize));
+ EXPECT_NE(0u, static_cast<unsigned int>(sb->st_blocks));
+
+// We are the ones that made the file.
+// Note: normally gid and uid overflow on backwards-compatible 32-bit systems
+// and we end up with dummy uids and gids in place here.
+#if defined(ARCH_CPU_64_BITS)
+ EXPECT_EQ(geteuid(), sb->st_uid);
+ EXPECT_EQ(getegid(), sb->st_gid);
+#endif
+
+ // Wrote 12 bytes above which should fit in one block.
+ EXPECT_EQ(12u, sb->st_size);
+
+ // Can't go backwards in time, 1500000000 was some time ago.
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb->st_atime_));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb->st_mtime_));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb->st_ctime_));
+
+ // Checking the padding for good measure.
+#if defined(__x86_64__)
+ EXPECT_EQ(0u, sb->__pad0);
+ EXPECT_EQ(0u, sb->__unused4[0]);
+ EXPECT_EQ(0u, sb->__unused4[1]);
+ EXPECT_EQ(0u, sb->__unused4[2]);
+#elif defined(__aarch64__)
+ EXPECT_EQ(0u, sb->__pad1);
+ EXPECT_EQ(0, sb->__pad2);
+ EXPECT_EQ(0u, sb->__unused4);
+ EXPECT_EQ(0u, sb->__unused5);
+#endif
+}
+
+TEST(SyscallWrappers, LStat) {
+ // Create a file to stat, with 12 bytes of data.
+ ScopedTemporaryFile tmp_file;
+ EXPECT_EQ(12, write(tmp_file.fd(), "blahblahblah", 12));
+
+ // Also create a symlink.
+ std::string symlink_name;
+ {
+ ScopedTemporaryFile tmp_file2;
+ symlink_name = tmp_file2.full_file_name();
+ }
+ int rc = symlink(tmp_file.full_file_name(), symlink_name.c_str());
+ if (rc != 0) {
+ PLOG(ERROR) << "Couldn't symlink " << symlink_name << " to target "
+ << tmp_file.full_file_name();
+ GTEST_FAIL();
+ }
+
+ struct kernel_stat lstat_info;
+ rc = sys_lstat(symlink_name.c_str(), &lstat_info);
+ if (rc < 0 && errno == EOVERFLOW) {
+ GTEST_SKIP();
+ }
+ if (rc != 0) {
+ PLOG(ERROR) << "Couldn't sys_lstat " << symlink_name;
+ GTEST_FAIL();
+ }
+
+ struct kernel_stat stat_info;
+ rc = sys_stat(symlink_name.c_str(), &stat_info);
+ if (rc < 0 && errno == EOVERFLOW) {
+ GTEST_SKIP();
+ }
+ if (rc != 0) {
+ PLOG(ERROR) << "Couldn't sys_stat " << symlink_name;
+ GTEST_FAIL();
+ }
+
+ struct kernel_stat tmp_file_stat_info;
+ rc = sys_stat(tmp_file.full_file_name(), &tmp_file_stat_info);
+ if (rc < 0 && errno == EOVERFLOW) {
+ GTEST_SKIP();
+ }
+ if (rc != 0) {
+ PLOG(ERROR) << "Couldn't sys_stat " << tmp_file.full_file_name();
+ GTEST_FAIL();
+ }
+
+ // lstat should produce information about a symlink.
+ ASSERT_TRUE(S_ISLNK(lstat_info.st_mode));
+
+ // stat-ing symlink_name and tmp_file should produce the same inode.
+ ASSERT_EQ(stat_info.st_ino, tmp_file_stat_info.st_ino);
+
+ // lstat-ing symlink_name should give a different inode than stat-ing
+ // symlink_name.
+ ASSERT_NE(stat_info.st_ino, lstat_info.st_ino);
+}
+
} // namespace
} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/DEPS b/sandbox/linux/syscall_broker/DEPS
index c477f7d363..149c463b06 100644
--- a/sandbox/linux/syscall_broker/DEPS
+++ b/sandbox/linux/syscall_broker/DEPS
@@ -1,4 +1,5 @@
include_rules = [
- "+sandbox/linux/system_headers",
"+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/services",
+ "+sandbox/linux/system_headers",
]
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
index 6b1b5be433..e24f659fcf 100644
--- a/sandbox/linux/syscall_broker/broker_client.cc
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -166,7 +166,7 @@ int BrokerClient::Rmdir(const char* path) const {
int BrokerClient::Stat(const char* pathname,
bool follow_links,
- struct stat* sb) const {
+ struct kernel_stat* sb) const {
if (!pathname || !sb)
return -EFAULT;
@@ -181,7 +181,7 @@ int BrokerClient::Stat(const char* pathname,
int BrokerClient::Stat64(const char* pathname,
bool follow_links,
- struct stat64* sb) const {
+ struct kernel_stat64* sb) const {
if (!pathname || !sb)
return -EFAULT;
diff --git a/sandbox/linux/syscall_broker/broker_client.h b/sandbox/linux/syscall_broker/broker_client.h
index 05e14c83f2..26ca78101c 100644
--- a/sandbox/linux/syscall_broker/broker_client.h
+++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -61,10 +61,10 @@ class SANDBOX_EXPORT BrokerClient : public SyscallDispatcher {
int Rmdir(const char* path) const override;
int Stat(const char* pathname,
bool follow_links,
- struct stat* sb) const override;
+ struct kernel_stat* sb) const override;
int Stat64(const char* pathname,
bool follow_links,
- struct stat64* sb) const override;
+ struct kernel_stat64* sb) const override;
int Unlink(const char* unlink) const override;
private:
diff --git a/sandbox/linux/syscall_broker/broker_host.cc b/sandbox/linux/syscall_broker/broker_host.cc
index 1cd03a18df..1cdc01a888 100644
--- a/sandbox/linux/syscall_broker/broker_host.cc
+++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -20,9 +20,11 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_permission_list.h"
#include "sandbox/linux/syscall_broker/broker_simple_message.h"
+#include "sandbox/linux/system_headers/linux_stat.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
namespace sandbox {
@@ -193,10 +195,12 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
+
if (command_type == COMMAND_STAT) {
- struct stat sb;
- int sts =
- follow_links ? stat(file_to_access, &sb) : lstat(file_to_access, &sb);
+ struct kernel_stat sb;
+
+ int sts = follow_links ? sandbox::sys_stat(file_to_access, &sb)
+ : sandbox::sys_lstat(file_to_access, &sb);
if (sts < 0) {
RAW_CHECK(reply->AddIntToMessage(-errno));
return;
@@ -205,10 +209,12 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
RAW_CHECK(
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
} else {
+#if defined(__NR_fstatat64)
DCHECK(command_type == COMMAND_STAT64);
- struct stat64 sb;
- int sts = follow_links ? stat64(file_to_access, &sb)
- : lstat64(file_to_access, &sb);
+ struct kernel_stat64 sb;
+
+ int sts = sandbox::sys_fstatat64(AT_FDCWD, file_to_access, &sb,
+ follow_links ? 0 : AT_SYMLINK_NOFOLLOW);
if (sts < 0) {
RAW_CHECK(reply->AddIntToMessage(-errno));
return;
@@ -216,6 +222,11 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
RAW_CHECK(reply->AddIntToMessage(0));
RAW_CHECK(
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
+#else // defined(__NR_fstatat64)
+ // We should not reach here on 64-bit systems, as the *stat*64() are only
+ // necessary on 32-bit.
+ RAW_CHECK(false);
+#endif
}
}
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
index 55ba6bccb2..c65f25a78a 100644
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -811,7 +811,7 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
const char* bad_leading_path5 = "/mbogo/fictitioux";
const char* bad_leading_path6 = "/mbogo/fictitiousa";
- struct stat sb;
+ default_stat_struct sb;
{
// Actual file with permissions to see file but command not allowed.
@@ -824,7 +824,7 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
tempfile_name, follow_links, &sb));
}
@@ -840,7 +840,7 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
nonesuch_name, follow_links, &sb));
}
{
@@ -852,7 +852,7 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
tempfile_name, follow_links, &sb));
}
{
@@ -864,38 +864,39 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
- nonesuch_name, follow_links, &sb));
+ EXPECT_EQ(-ENOENT,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ nonesuch_name, follow_links, &sb));
// Gets denied all the way back to root since no create permission.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
leading_path3, follow_links, &sb));
// Not fooled by substrings.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path3, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path4, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path5, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path6, follow_links, &sb));
}
{
@@ -907,37 +908,41 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
- nonesuch_name, follow_links, &sb));
+ EXPECT_EQ(-ENOENT,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ nonesuch_name, follow_links, &sb));
// Gets ENOENT all the way back to root since it has create permission.
- EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
- leading_path1, follow_links, &sb));
- EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
- leading_path2, follow_links, &sb));
+ EXPECT_EQ(-ENOENT,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ leading_path1, follow_links, &sb));
+ EXPECT_EQ(-ENOENT,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ leading_path2, follow_links, &sb));
// But can always get the root.
- EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Stat(
- leading_path3, follow_links, &sb));
+ EXPECT_EQ(0,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ leading_path3, follow_links, &sb));
// Not fooled by substrings.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path3, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path4, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path5, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.GetBrokerClientSignalBased()->Stat(
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
bad_leading_path6, follow_links, &sb));
}
{
@@ -949,8 +954,9 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Stat(
- tempfile_name, follow_links, &sb));
+ EXPECT_EQ(0,
+ open_broker.GetBrokerClientSignalBased()->DefaultStatForTesting(
+ tempfile_name, follow_links, &sb));
// Following fields may never be consistent but should be non-zero.
// Don't trust the platform to define fields with any particular sign.
@@ -968,9 +974,9 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
EXPECT_EQ(12, sb.st_size);
// Can't go backwards in time, 1500000000 was some time ago.
- EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_atime));
- EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_mtime));
- EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_ctime));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_atime_));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_mtime_));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_ctime_));
}
}
diff --git a/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc b/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc
index fffa9bb708..f517a9867c 100644
--- a/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc
+++ b/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc
@@ -16,6 +16,7 @@
#include "base/memory/page_size.h"
#include "base/posix/unix_domain_socket.h"
#include "base/test/bind.h"
+#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -52,19 +53,6 @@ void VerifyCorrectString(std::string str, size_t size) {
}
}
-void* MapPagesOrDie(size_t num_pages) {
- void* addr = mmap(nullptr, num_pages * base::GetPageSize(),
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- PCHECK(addr);
- return addr;
-}
-
-void MprotectLastPageOrDie(char* addr, size_t num_pages) {
- size_t last_page_offset = (num_pages - 1) * base::GetPageSize();
- PCHECK(mprotect(addr + last_page_offset, base::GetPageSize(), PROT_NONE) >=
- 0);
-}
-
pid_t ForkWaitingChild(base::OnceCallback<void(int)>
after_parent_signals_callback = base::DoNothing(),
base::ScopedFD* parent_sync_fd = nullptr) {
@@ -105,13 +93,13 @@ void ReadTest(const ReadTestConfig& test_config) {
size_t total_pages = (test_config.start_at + test_config.total_size +
base::GetPageSize() - 1) /
base::GetPageSize();
- char* mmap_addr = static_cast<char*>(MapPagesOrDie(total_pages));
+ char* mmap_addr = static_cast<char*>(TestUtils::MapPagesOrDie(total_pages));
char* addr = mmap_addr + test_config.start_at;
FillBufferWithPath(addr, test_config.total_size,
test_config.include_null_byte);
if (test_config.last_page_inaccessible)
- MprotectLastPageOrDie(mmap_addr, total_pages);
+ TestUtils::MprotectLastPageOrDie(mmap_addr, total_pages);
pid_t pid = ForkWaitingChild();
munmap(mmap_addr, base::GetPageSize() * total_pages);
@@ -212,7 +200,7 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkPlus1EndingOnePastPage) {
}
SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChildExited) {
- void* addr = MapPagesOrDie(1);
+ void* addr = TestUtils::MapPagesOrDie(1);
FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
base::ScopedFD parent_sync, child_sync;
@@ -240,10 +228,10 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChildExited) {
}
SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicWrite) {
- void* read_from = MapPagesOrDie(1);
+ void* read_from = TestUtils::MapPagesOrDie(1);
const size_t write_size = base::GetPageSize();
FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
- char* write_to = static_cast<char*>(MapPagesOrDie(1));
+ char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(1));
base::ScopedFD parent_signal_fd;
const std::vector<int> empty_fd_vec;
@@ -278,8 +266,8 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicWrite) {
}
SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) {
- char* write_to = static_cast<char*>(MapPagesOrDie(1));
- MprotectLastPageOrDie(write_to, 1);
+ char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(1));
+ TestUtils::MprotectLastPageOrDie(write_to, 1);
base::ScopedFD parent_signal_fd;
const std::vector<int> empty_fd_vec;
@@ -295,11 +283,11 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) {
}
SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) {
- char* read_from = static_cast<char*>(MapPagesOrDie(2));
+ char* read_from = static_cast<char*>(TestUtils::MapPagesOrDie(2));
const size_t write_size = base::GetPageSize();
FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
- char* write_to = static_cast<char*>(MapPagesOrDie(2));
- MprotectLastPageOrDie(write_to, 2);
+ char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(2));
+ TestUtils::MprotectLastPageOrDie(write_to, 2);
write_to += base::GetPageSize() / 2;
base::ScopedFD parent_signal_fd;
const std::vector<int> empty_fd_vec;
@@ -314,7 +302,7 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) {
}
SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteChildExited) {
- char* addr = static_cast<char*>(MapPagesOrDie(1));
+ char* addr = static_cast<char*>(TestUtils::MapPagesOrDie(1));
FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
base::ScopedFD parent_sync, child_sync;
diff --git a/sandbox/linux/syscall_broker/syscall_dispatcher.cc b/sandbox/linux/syscall_broker/syscall_dispatcher.cc
index b9ee93c14a..8a42397ef8 100644
--- a/sandbox/linux/syscall_broker/syscall_dispatcher.cc
+++ b/sandbox/linux/syscall_broker/syscall_dispatcher.cc
@@ -19,8 +19,18 @@ namespace syscall_broker {
#define BROKER_UNPOISON_STRING(x)
#endif
+int SyscallDispatcher::DefaultStatForTesting(const char* pathname,
+ bool follow_links,
+ default_stat_struct* sb) {
+#if defined(__NR_fstatat64)
+ return Stat64(pathname, follow_links, sb);
+#elif defined(__NR_newfstatat)
+ return Stat(pathname, follow_links, sb);
+#endif
+}
+
int SyscallDispatcher::PerformStatat(const arch_seccomp_data& args,
- bool arch64) {
+ bool stat64) {
if (static_cast<int>(args.args[0]) != AT_FDCWD)
return -EPERM;
// Only allow the AT_SYMLINK_NOFOLLOW flag which is used by some libc
@@ -30,13 +40,29 @@ int SyscallDispatcher::PerformStatat(const arch_seccomp_data& args,
const bool follow_links =
!(static_cast<int>(args.args[3]) & AT_SYMLINK_NOFOLLOW);
- if (arch64) {
+ if (stat64) {
return Stat64(reinterpret_cast<const char*>(args.args[1]), follow_links,
- reinterpret_cast<struct stat64*>(args.args[2]));
+ reinterpret_cast<struct kernel_stat64*>(args.args[2]));
}
return Stat(reinterpret_cast<const char*>(args.args[1]), follow_links,
- reinterpret_cast<struct stat*>(args.args[2]));
+ reinterpret_cast<struct kernel_stat*>(args.args[2]));
+}
+
+int SyscallDispatcher::PerformUnlinkat(const arch_seccomp_data& args) {
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+
+ int flags = static_cast<int>(args.args[2]);
+
+ if (flags == AT_REMOVEDIR) {
+ return Rmdir(reinterpret_cast<const char*>(args.args[1]));
+ }
+
+ if (flags != 0)
+ return -EPERM;
+
+ return Unlink(reinterpret_cast<const char*>(args.args[1]));
}
int SyscallDispatcher::DispatchSyscall(const arch_seccomp_data& args) {
@@ -127,59 +153,42 @@ int SyscallDispatcher::DispatchSyscall(const arch_seccomp_data& args) {
#if defined(__NR_stat)
case __NR_stat:
return Stat(reinterpret_cast<const char*>(args.args[0]), true,
- reinterpret_cast<struct stat*>(args.args[1]));
+ reinterpret_cast<struct kernel_stat*>(args.args[1]));
#endif
#if defined(__NR_stat64)
case __NR_stat64:
return Stat64(reinterpret_cast<const char*>(args.args[0]), true,
- reinterpret_cast<struct stat64*>(args.args[1]));
+ reinterpret_cast<struct kernel_stat64*>(args.args[1]));
#endif
#if defined(__NR_lstat)
case __NR_lstat:
// See https://crbug.com/847096
BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
return Stat(reinterpret_cast<const char*>(args.args[0]), false,
- reinterpret_cast<struct stat*>(args.args[1]));
+ reinterpret_cast<struct kernel_stat*>(args.args[1]));
#endif
#if defined(__NR_lstat64)
case __NR_lstat64:
// See https://crbug.com/847096
BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
return Stat64(reinterpret_cast<const char*>(args.args[0]), false,
- reinterpret_cast<struct stat64*>(args.args[1]));
-#endif
-#if defined(__NR_fstatat)
- case __NR_fstatat:
- return PerformStatat(args, /*arch64=*/false);
+ reinterpret_cast<struct kernel_stat64*>(args.args[1]));
#endif
#if defined(__NR_fstatat64)
case __NR_fstatat64:
- return PerformStatat(args, /*arch64=*/true);
+ return PerformStatat(args, /*stat64=*/true);
#endif
#if defined(__NR_newfstatat)
case __NR_newfstatat:
- return PerformStatat(args, /*arch64=*/false);
+ return PerformStatat(args, /*stat64=*/false);
#endif
#if defined(__NR_unlink)
case __NR_unlink:
return Unlink(reinterpret_cast<const char*>(args.args[0]));
#endif
#if defined(__NR_unlinkat)
- case __NR_unlinkat: {
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
-
- int flags = static_cast<int>(args.args[2]);
-
- if (flags == AT_REMOVEDIR) {
- return Rmdir(reinterpret_cast<const char*>(args.args[1]));
- }
-
- if (flags != 0)
- return -EPERM;
-
- return Unlink(reinterpret_cast<const char*>(args.args[1]));
- }
+ case __NR_unlinkat:
+ return PerformUnlinkat(args);
#endif // defined(__NR_unlinkat)
default:
RAW_CHECK(false);
diff --git a/sandbox/linux/syscall_broker/syscall_dispatcher.h b/sandbox/linux/syscall_broker/syscall_dispatcher.h
index d8b8874ad9..1d6653caf3 100644
--- a/sandbox/linux/syscall_broker/syscall_dispatcher.h
+++ b/sandbox/linux/syscall_broker/syscall_dispatcher.h
@@ -9,13 +9,15 @@
#include <cstddef>
#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_stat.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
namespace syscall_broker {
// An abstract class that defines all the system calls we perform for the
// sandboxed process.
-class SyscallDispatcher {
+class SANDBOX_EXPORT SyscallDispatcher {
public:
// Emulates access()/faccessat().
// X_OK will always return an error in practice since the broker process
@@ -40,19 +42,34 @@ class SyscallDispatcher {
virtual int Rmdir(const char* path) const = 0;
// Emulates stat()/stat64()/lstat()/lstat64()/fstatat()/newfstatat().
+ // Stat64 is only available on 32-bit systems.
virtual int Stat(const char* pathname,
bool follow_links,
- struct stat* sb) const = 0;
+ struct kernel_stat* sb) const = 0;
virtual int Stat64(const char* pathname,
bool follow_links,
- struct stat64* sb) const = 0;
+ struct kernel_stat64* sb) const = 0;
// Emulates unlink()/unlinkat().
virtual int Unlink(const char* unlink) const = 0;
+ // Different architectures use a different syscall from the stat family by
+ // default in glibc. E.g. 32-bit systems use *stat*64() and fill out struct
+ // kernel_stat64, whereas 64-bit systems use *stat*() and fill out struct
+ // kernel_stat. Some tests want to call the SyscallDispatcher directly, and
+ // should be using the default stat in order to test against glibc.
+ int DefaultStatForTesting(const char* pathname,
+ bool follow_links,
+ default_stat_struct* sb);
+
// Validates the args passed to a *statat*() syscall and performs the syscall
- // using Stat() or Stat64().
- int PerformStatat(const arch_seccomp_data& args, bool arch64);
+ // using Stat(), or on 32-bit systems it uses Stat64() for the *statat64()
+ // syscalls.
+ int PerformStatat(const arch_seccomp_data& args, bool stat64);
+
+ // Validates the args passed to an unlinkat() syscall and performs the syscall
+ // using either Unlink() or Rmdir().
+ int PerformUnlinkat(const arch_seccomp_data& args);
// Reads the syscall number and arguments, imposes some policy (e.g. the *at()
// system calls must only allow AT_FDCWD as the first argument), and
diff --git a/sandbox/linux/system_headers/linux_stat.h b/sandbox/linux/system_headers/linux_stat.h
new file mode 100644
index 0000000000..35788eb22a
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_stat.h
@@ -0,0 +1,188 @@
+// Copyright 2021 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.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_STAT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_STAT_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#if defined(ARCH_CPU_MIPS_FAMILY)
+#if defined(ARCH_CPU_64_BITS)
+struct kernel_stat {
+#else
+struct kernel_stat64 {
+#endif
+ unsigned st_dev;
+ unsigned __pad0[3];
+ unsigned long long st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned st_rdev;
+ unsigned __pad1[3];
+ long long st_size;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned st_blksize;
+ unsigned __pad2;
+ unsigned long long st_blocks;
+};
+#else
+struct kernel_stat64 {
+ unsigned long long st_dev;
+ unsigned char __pad0[4];
+ unsigned __st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned long long st_rdev;
+ unsigned char __pad3[4];
+ long long st_size;
+ unsigned st_blksize;
+ unsigned long long st_blocks;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned long long st_ino;
+};
+#endif
+
+#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
+struct kernel_stat {
+ /* The kernel headers suggest that st_dev and st_rdev should be 32bit
+ * quantities encoding 12bit major and 20bit minor numbers in an interleaved
+ * format. In reality, we do not see useful data in the top bits. So,
+ * we'll leave the padding in here, until we find a better solution.
+ */
+ unsigned short st_dev;
+ short pad1;
+ unsigned st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ short pad2;
+ unsigned st_size;
+ unsigned st_blksize;
+ unsigned st_blocks;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned __unused4;
+ unsigned __unused5;
+};
+#elif defined(__x86_64__)
+struct kernel_stat {
+ uint64_t st_dev;
+ uint64_t st_ino;
+ uint64_t st_nlink;
+ unsigned st_mode;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned __pad0;
+ uint64_t st_rdev;
+ int64_t st_size;
+ int64_t st_blksize;
+ int64_t st_blocks;
+ uint64_t st_atime_;
+ uint64_t st_atime_nsec_;
+ uint64_t st_mtime_;
+ uint64_t st_mtime_nsec_;
+ uint64_t st_ctime_;
+ uint64_t st_ctime_nsec_;
+ int64_t __unused4[3];
+};
+#elif (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
+struct kernel_stat {
+ unsigned st_dev;
+ int st_pad1[3];
+ unsigned st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned st_rdev;
+ int st_pad2[2];
+ long st_size;
+ int st_pad3;
+ long st_atime_;
+ long st_atime_nsec_;
+ long st_mtime_;
+ long st_mtime_nsec_;
+ long st_ctime_;
+ long st_ctime_nsec_;
+ int st_blksize;
+ int st_blocks;
+ int st_pad4[14];
+};
+#elif defined(__aarch64__)
+struct kernel_stat {
+ unsigned long st_dev;
+ unsigned long st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned long st_rdev;
+ unsigned long __pad1;
+ long st_size;
+ int st_blksize;
+ int __pad2;
+ long st_blocks;
+ long st_atime_;
+ unsigned long st_atime_nsec_;
+ long st_mtime_;
+ unsigned long st_mtime_nsec_;
+ long st_ctime_;
+ unsigned long st_ctime_nsec_;
+ unsigned int __unused4;
+ unsigned int __unused5;
+};
+#endif
+
+// On 32-bit systems, we default to the 64-bit stat struct like libc
+// implementations do. Otherwise we default to the normal stat struct which is
+// already 64-bit.
+// These defines make it easy to call the right syscall to fill out a 64-bit
+// stat struct, which is the default in libc implementations but requires
+// different syscall names on 32 and 64-bit platforms.
+#if defined(__NR_fstatat64)
+
+namespace sandbox {
+using default_stat_struct = struct kernel_stat64;
+} // namespace sandbox
+
+#define __NR_fstatat_default __NR_fstatat64
+#define __NR_fstat_default __NR_fstat64
+
+#elif defined(__NR_newfstatat)
+
+namespace sandbox {
+using default_stat_struct = struct kernel_stat;
+} // namespace sandbox
+
+#define __NR_fstatat_default __NR_newfstatat
+#define __NR_fstat_default __NR_fstat
+
+#else
+#error "one of fstatat64 and newfstatat must be defined"
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_STAT_H_
diff --git a/sandbox/linux/system_headers/linux_time.h b/sandbox/linux/system_headers/linux_time.h
index 780f24dddd..f18c806611 100644
--- a/sandbox/linux/system_headers/linux_time.h
+++ b/sandbox/linux/system_headers/linux_time.h
@@ -11,6 +11,32 @@
#define CPUCLOCK_CLOCK_MASK 3
#endif
+#if !defined(CPUCLOCK_PROF)
+#define CPUCLOCK_PROF 0
+#endif
+
+#if !defined(CPUCLOCK_VIRT)
+#define CPUCLOCK_VIRT 1
+#endif
+
+#if !defined(CPUCLOCK_SCHED)
+#define CPUCLOCK_SCHED 2
+#endif
+
+#if !defined(CPUCLOCK_PERTHREAD_MASK)
+#define CPUCLOCK_PERTHREAD_MASK 4
+#endif
+
+#if !defined(MAKE_PROCESS_CPUCLOCK)
+#define MAKE_PROCESS_CPUCLOCK(pid, clock) \
+ ((int)(~(unsigned)(pid) << 3) | (int)(clock))
+#endif
+
+#if !defined(MAKE_THREAD_CPUCLOCK)
+#define MAKE_THREAD_CPUCLOCK(tid, clock) \
+ ((int)(~(unsigned)(tid) << 3) | (int)((clock) | CPUCLOCK_PERTHREAD_MASK))
+#endif
+
#if !defined(CLOCKFD)
#define CLOCKFD 3
#endif
diff --git a/sandbox/linux/tests/test_utils.cc b/sandbox/linux/tests/test_utils.cc
index 847c20b20c..cf6041a4b4 100644
--- a/sandbox/linux/tests/test_utils.cc
+++ b/sandbox/linux/tests/test_utils.cc
@@ -5,12 +5,14 @@
#include "sandbox/linux/tests/test_utils.h"
#include <errno.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "base/check_op.h"
+#include "base/memory/page_size.h"
#include "base/posix/eintr_wrapper.h"
namespace sandbox {
@@ -39,4 +41,17 @@ void TestUtils::HandlePostForkReturn(pid_t pid) {
}
}
+void* TestUtils::MapPagesOrDie(size_t num_pages) {
+ void* addr = mmap(nullptr, num_pages * base::GetPageSize(),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ PCHECK(addr);
+ return addr;
+}
+
+void TestUtils::MprotectLastPageOrDie(char* addr, size_t num_pages) {
+ size_t last_page_offset = (num_pages - 1) * base::GetPageSize();
+ PCHECK(mprotect(addr + last_page_offset, base::GetPageSize(), PROT_NONE) >=
+ 0);
+}
+
} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.h b/sandbox/linux/tests/test_utils.h
index 7cf9749fe4..43b028b1e3 100644
--- a/sandbox/linux/tests/test_utils.h
+++ b/sandbox/linux/tests/test_utils.h
@@ -19,6 +19,8 @@ class TestUtils {
// makes sure that if fork() succeeded the child exits
// and the parent waits for it.
static void HandlePostForkReturn(pid_t pid);
+ static void* MapPagesOrDie(size_t num_pages);
+ static void MprotectLastPageOrDie(char* addr, size_t num_pages);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
diff --git a/sandbox/policy/linux/bpf_broker_policy_linux.cc b/sandbox/policy/linux/bpf_broker_policy_linux.cc
index 2963bb9ca8..6dc8c0581b 100644
--- a/sandbox/policy/linux/bpf_broker_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_broker_policy_linux.cc
@@ -93,8 +93,8 @@ ResultExpr BrokerProcessPolicy::EvaluateSyscall(int sysno) const {
return Allow();
break;
#endif
-#if defined(__NR_fstatat)
- case __NR_fstatat:
+#if defined(__NR_fstatat64)
+ case __NR_fstatat64:
if (allowed_command_set_.test(syscall_broker::COMMAND_STAT))
return Allow();
break;