From 4b438323d68840453b5ef826c3997568e2e0e8c7 Mon Sep 17 00:00:00 2001 From: Matthew Denton 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 > > > > Reviewed-by: Robert Sesek > > > > 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 > > > Owners-Override: Victor Vianna > > > Commit-Queue: Rubber Stamper > > > Bot-Commit: Rubber Stamper > > > 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 > > Commit-Queue: Matthew Denton > > Reviewed-by: Robert Sesek > > 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 > Commit-Queue: Matthew Denton > Commit-Queue: Kinuko Yasuda > Reviewed-by: Matthew Denton > Reviewed-by: Kinuko Yasuda > Owners-Override: Kinuko Yasuda > 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 Commit-Queue: Matthew Denton 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 #include #include #include @@ -14,11 +15,13 @@ #include #include +#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 +#include #include #include #include #include -#include +#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(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(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( + 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(sb->st_dev)); + EXPECT_NE(0u, static_cast(sb->st_ino)); + EXPECT_NE(0u, static_cast(sb->st_mode)); + EXPECT_NE(0u, static_cast(sb->st_blksize)); + EXPECT_NE(0u, static_cast(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(sb->st_atime_)); + EXPECT_LT(1500000000u, static_cast(sb->st_mtime_)); + EXPECT_LT(1500000000u, static_cast(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(&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(&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(sb.st_atime)); - EXPECT_LT(1500000000u, static_cast(sb.st_mtime)); - EXPECT_LT(1500000000u, static_cast(sb.st_ctime)); + EXPECT_LT(1500000000u, static_cast(sb.st_atime_)); + EXPECT_LT(1500000000u, static_cast(sb.st_mtime_)); + EXPECT_LT(1500000000u, static_cast(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 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(MapPagesOrDie(total_pages)); + char* mmap_addr = static_cast(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(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(read_from), write_size, false); - char* write_to = static_cast(MapPagesOrDie(1)); + char* write_to = static_cast(TestUtils::MapPagesOrDie(1)); base::ScopedFD parent_signal_fd; const std::vector empty_fd_vec; @@ -278,8 +266,8 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicWrite) { } SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) { - char* write_to = static_cast(MapPagesOrDie(1)); - MprotectLastPageOrDie(write_to, 1); + char* write_to = static_cast(TestUtils::MapPagesOrDie(1)); + TestUtils::MprotectLastPageOrDie(write_to, 1); base::ScopedFD parent_signal_fd; const std::vector empty_fd_vec; @@ -295,11 +283,11 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) { } SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) { - char* read_from = static_cast(MapPagesOrDie(2)); + char* read_from = static_cast(TestUtils::MapPagesOrDie(2)); const size_t write_size = base::GetPageSize(); FillBufferWithPath(static_cast(read_from), write_size, false); - char* write_to = static_cast(MapPagesOrDie(2)); - MprotectLastPageOrDie(write_to, 2); + char* write_to = static_cast(TestUtils::MapPagesOrDie(2)); + TestUtils::MprotectLastPageOrDie(write_to, 2); write_to += base::GetPageSize() / 2; base::ScopedFD parent_signal_fd; const std::vector empty_fd_vec; @@ -314,7 +302,7 @@ SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) { } SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteChildExited) { - char* addr = static_cast(MapPagesOrDie(1)); + char* addr = static_cast(TestUtils::MapPagesOrDie(1)); FillBufferWithPath(static_cast(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(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(args.args[3]) & AT_SYMLINK_NOFOLLOW); - if (arch64) { + if (stat64) { return Stat64(reinterpret_cast(args.args[1]), follow_links, - reinterpret_cast(args.args[2])); + reinterpret_cast(args.args[2])); } return Stat(reinterpret_cast(args.args[1]), follow_links, - reinterpret_cast(args.args[2])); + reinterpret_cast(args.args[2])); +} + +int SyscallDispatcher::PerformUnlinkat(const arch_seccomp_data& args) { + if (static_cast(args.args[0]) != AT_FDCWD) + return -EPERM; + + int flags = static_cast(args.args[2]); + + if (flags == AT_REMOVEDIR) { + return Rmdir(reinterpret_cast(args.args[1])); + } + + if (flags != 0) + return -EPERM; + + return Unlink(reinterpret_cast(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(args.args[0]), true, - reinterpret_cast(args.args[1])); + reinterpret_cast(args.args[1])); #endif #if defined(__NR_stat64) case __NR_stat64: return Stat64(reinterpret_cast(args.args[0]), true, - reinterpret_cast(args.args[1])); + reinterpret_cast(args.args[1])); #endif #if defined(__NR_lstat) case __NR_lstat: // See https://crbug.com/847096 BROKER_UNPOISON_STRING(reinterpret_cast(args.args[0])); return Stat(reinterpret_cast(args.args[0]), false, - reinterpret_cast(args.args[1])); + reinterpret_cast(args.args[1])); #endif #if defined(__NR_lstat64) case __NR_lstat64: // See https://crbug.com/847096 BROKER_UNPOISON_STRING(reinterpret_cast(args.args[0])); return Stat64(reinterpret_cast(args.args[0]), false, - reinterpret_cast(args.args[1])); -#endif -#if defined(__NR_fstatat) - case __NR_fstatat: - return PerformStatat(args, /*arch64=*/false); + reinterpret_cast(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(args.args[0])); #endif #if defined(__NR_unlinkat) - case __NR_unlinkat: { - if (static_cast(args.args[0]) != AT_FDCWD) - return -EPERM; - - int flags = static_cast(args.args[2]); - - if (flags == AT_REMOVEDIR) { - return Rmdir(reinterpret_cast(args.args[1])); - } - - if (flags != 0) - return -EPERM; - - return Unlink(reinterpret_cast(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 #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 + +#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 +#include #include #include #include #include #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;