diff options
Diffstat (limited to 'chromium/sandbox')
98 files changed, 2402 insertions, 380 deletions
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn index 2f778dd0bca..ccbbc91716e 100644 --- a/chromium/sandbox/linux/BUILD.gn +++ b/chromium/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/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc index 82a40696b87..846dc7a83d1 100644 --- a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc +++ b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc @@ -103,8 +103,8 @@ class IfThenResultExprImpl : public internal::ResultExprImpl { class ConstBoolExprImpl : public internal::BoolExprImpl { public: - ConstBoolExprImpl(bool value) : value_(value) {} - ~ConstBoolExprImpl() override {} + explicit ConstBoolExprImpl(bool value) : value_(value) {} + ~ConstBoolExprImpl() override = default; CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, diff --git a/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc b/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc index 2ba3bc9908f..19c0cf631db 100644 --- a/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc +++ b/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc @@ -12,8 +12,9 @@ #include <limits> #include <ostream> +#include "base/bits.h" #include "base/check_op.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" #include "sandbox/linux/bpf_dsl/codegen.h" @@ -51,11 +52,6 @@ const int kSyscallsRequiredForUnsafeTraps[] = { #endif }; -bool HasExactlyOneBit(uint64_t x) { - // Common trick; e.g., see http://stackoverflow.com/a/108329. - return x != 0 && (x & (x - 1)) == 0; -} - ResultExpr DefaultPanic(const char* error) { return Kill(); } @@ -404,7 +400,7 @@ CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno, // For (arg & x) == x where x is a single-bit value, emit: // LDW [idx] // JSET mask, passed, failed - if (mask == value && HasExactlyOneBit(mask)) { + if (mask == value && base::bits::IsPowerOfTwo(mask)) { return gen_.MakeInstruction( BPF_LD + BPF_W + BPF_ABS, idx, diff --git a/chromium/sandbox/linux/bpf_dsl/syscall_set_unittest.cc b/chromium/sandbox/linux/bpf_dsl/syscall_set_unittest.cc index 942ac745a3c..00c475ea990 100644 --- a/chromium/sandbox/linux/bpf_dsl/syscall_set_unittest.cc +++ b/chromium/sandbox/linux/bpf_dsl/syscall_set_unittest.cc @@ -7,7 +7,7 @@ #include <stddef.h> #include <stdint.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h" #include "sandbox/linux/tests/unit_tests.h" diff --git a/chromium/sandbox/linux/bpf_dsl/test_trap_registry_unittest.cc b/chromium/sandbox/linux/bpf_dsl/test_trap_registry_unittest.cc index 6f0f71f5fee..05bca9edee8 100644 --- a/chromium/sandbox/linux/bpf_dsl/test_trap_registry_unittest.cc +++ b/chromium/sandbox/linux/bpf_dsl/test_trap_registry_unittest.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace sandbox { diff --git a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc index 9da9c689114..6d6132a542f 100644 --- a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc +++ b/chromium/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; } @@ -1212,11 +1244,11 @@ class StatFileNoCommandDelegate final : public StatFileDelegate { }; TEST(BrokerProcessIntegrationTest, StatFileNoCommandFollowLinks) { - RunAllBrokerTests<StatFileNoCommandDelegate<true>>(); + RunAllBrokerTests<StatFileNoCommandDelegate</*follow_links=*/true>>(); } TEST(BrokerProcessIntegrationTest, StatFileNoCommandNoFollowLinks) { - RunAllBrokerTests<StatFileNoCommandDelegate<false>>(); + RunAllBrokerTests<StatFileNoCommandDelegate</*follow_links=*/false>>(); } // Allows the STAT command without any file permissions. @@ -1242,11 +1274,11 @@ class StatFilesNoPermissionDelegate final : public StatFileDelegate { }; TEST(BrokerProcessIntegrationTest, StatFilesNoPermissionFollowLinks) { - RunAllBrokerTests<StatFilesNoPermissionDelegate<true>>(); + RunAllBrokerTests<StatFilesNoPermissionDelegate</*follow_links=*/true>>(); } TEST(BrokerProcessIntegrationTest, StatFilesNoPermissionNoFollowLinks) { - RunAllBrokerTests<StatFilesNoPermissionDelegate<false>>(); + RunAllBrokerTests<StatFilesNoPermissionDelegate</*follow_links=*/false>>(); } // Nonexistent file with permissions to see file. @@ -1291,12 +1323,14 @@ class StatNonexistentFileWithPermissionsDelegate final TEST(BrokerProcessIntegrationTest, StatNonexistentFileWithPermissionsFollowLinks) { - RunAllBrokerTests<StatNonexistentFileWithPermissionsDelegate<true>>(); + RunAllBrokerTests< + StatNonexistentFileWithPermissionsDelegate</*follow_links=*/true>>(); } TEST(BrokerProcessIntegrationTest, StatNonexistentFileWithPermissionsNoFollowLinks) { - RunAllBrokerTests<StatNonexistentFileWithPermissionsDelegate<false>>(); + RunAllBrokerTests< + StatNonexistentFileWithPermissionsDelegate</*follow_links=*/false>>(); } // Nonexistent file with permissions to create file. @@ -1340,12 +1374,14 @@ class StatNonexistentFileWithCreatePermissionsDelegate final TEST(BrokerProcessIntegrationTest, StatNonexistentFileWithCreatePermissionsFollowLinks) { - RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate<true>>(); + RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate< + /*follow_links=*/true>>(); } TEST(BrokerProcessIntegrationTest, StatNonexistentFileWithCreatePermissionsNoFollowLinks) { - RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate<false>>(); + RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate< + /*follow_links=*/false>>(); } // Actual file with permissions to see file. @@ -1387,11 +1423,11 @@ class StatFileWithPermissionsDelegate final : public StatFileDelegate { }; TEST(BrokerProcessIntegrationTest, StatFileWithPermissionsFollowLinks) { - RunAllBrokerTests<StatFileWithPermissionsDelegate<true>>(); + RunAllBrokerTests<StatFileWithPermissionsDelegate</*follow_links=*/true>>(); } TEST(BrokerProcessIntegrationTest, StatFileWithPermissionsNoFollowLinks) { - RunAllBrokerTests<StatFileWithPermissionsDelegate<false>>(); + RunAllBrokerTests<StatFileWithPermissionsDelegate</*follow_links=*/false>>(); } class RenameTestDelegate : public BrokerTestDelegate { @@ -1421,6 +1457,16 @@ class RenameTestDelegate : public BrokerTestDelegate { } protected: + void ExpectRenamed() { + EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); + EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); + } + + void ExpectNotRenamed() { + EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); + EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); + } + std::string oldpath_; std::string newpath_; }; @@ -1443,13 +1489,7 @@ class RenameNoCommandDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (true) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectNotRenamed(); RenameTestDelegate::ParentTearDown(); } }; @@ -1474,13 +1514,7 @@ class RenameNoPermNewDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (true) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectNotRenamed(); RenameTestDelegate::ParentTearDown(); } }; @@ -1505,13 +1539,7 @@ class RenameNoPermOldDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (true) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectNotRenamed(); RenameTestDelegate::ParentTearDown(); } }; @@ -1538,13 +1566,7 @@ class RenameReadPermNewDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (true) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectNotRenamed(); RenameTestDelegate::ParentTearDown(); } }; @@ -1571,13 +1593,7 @@ class RenameReadPermOldDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (true) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectNotRenamed(); RenameTestDelegate::ParentTearDown(); } }; @@ -1603,13 +1619,7 @@ class RenameWritePermsBothDelegate final : public RenameTestDelegate { } void ParentTearDown() override { - if (false) { - EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) < 0); - } else { - EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0); - EXPECT_TRUE(access(newpath_.c_str(), 0) == 0); - } + ExpectRenamed(); RenameTestDelegate::ParentTearDown(); } }; diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS b/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS index 4419fd1da34..95d1bb6cbba 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS +++ b/chromium/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/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc index 05c39f0f564..049e921694e 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc @@ -20,6 +20,7 @@ #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/services/syscall_wrappers.h" +#include "sandbox/linux/system_headers/linux_stat.h" #include "sandbox/linux/system_headers/linux_syscalls.h" #if !defined(SO_PEEK_OFF) @@ -170,6 +171,15 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno, return Allow(); #endif + // V8 uses PKU (a.k.a. MPK / PKEY) for protecting code spaces. + if (sysno == __NR_pkey_alloc) { + return RestrictPkeyAllocFlags(); + } + + if (sysno == __NR_pkey_free) { + return Allow(); + } + if (SyscallSets::IsClockApi(sysno)) { return RestrictClockID(); } @@ -178,6 +188,12 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno, return RestrictCloneToThreadsAndEPERMFork(); } + // clone3 takes a pointer argument which we cannot examine, so return ENOSYS + // to force the libc to use clone. See https://crbug.com/1213452. + if (sysno == __NR_clone3) { + return Error(ENOSYS); + } + if (sysno == __NR_fcntl) return RestrictFcntlCommands(); @@ -253,8 +269,11 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno, return RestrictMmapFlags(); #endif - if (sysno == __NR_mprotect) + if (sysno == __NR_mprotect || sysno == __NR_pkey_mprotect) { + // pkey_mprotect is identical to mprotect except for the additional (last) + // parameter, which can be ignored here. return RestrictMprotectFlags(); + } if (sysno == __NR_prctl) return RestrictPrctl(); @@ -286,6 +305,24 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno, return Allow(); } + // The fstatat syscalls are file system syscalls, which will be denied below + // with fs_denied_errno. However some allowed fstat syscalls are rewritten by + // libc implementations to fstatat syscalls, and we need to rewrite them back. + if (sysno == __NR_fstatat_default) { + return RewriteFstatatSIGSYS(fs_denied_errno); + } + + // The statx syscall is a filesystem syscall, which will be denied below with + // fs_denied_errno. However, on some platforms, glibc will default to statx + // for normal stat-family calls. Unfortunately there's no way to rewrite statx + // to something safe using a signal handler. Returning ENOSYS will cause glibc + // to fallback to old stat paths. + if (sysno == __NR_statx) { + const Arg<int> mask(3); + return If(mask == STATX_BASIC_STATS, Error(ENOSYS)) + .Else(Error(fs_denied_errno)); + } + if (SyscallSets::IsFileSystem(sysno) || SyscallSets::IsCurrentDirectory(sysno)) { return Error(fs_denied_errno); diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc index 68c29b564bb..57d307e09d3 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc @@ -51,7 +51,8 @@ namespace sandbox { namespace { -// This also tests that read(), write() and fstat() are allowed. +// This also tests that read(), write(), fstat(), and fstatat(.., "", .., +// AT_EMPTY_PATH) are allowed. void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) { BPF_ASSERT_LE(0, read_end.get()); BPF_ASSERT_LE(0, write_end.get()); @@ -60,6 +61,20 @@ void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) { BPF_ASSERT_EQ(0, sys_ret); BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode)); + sys_ret = fstatat(read_end.get(), "", &stat_buf, AT_EMPTY_PATH); + BPF_ASSERT_EQ(0, sys_ret); + BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode)); + + // Make sure fstatat with anything other than an empty string is denied. + sys_ret = fstatat(read_end.get(), "/", &stat_buf, AT_EMPTY_PATH); + BPF_ASSERT_EQ(sys_ret, -1); + BPF_ASSERT_EQ(EPERM, errno); + + // Make sure fstatat without AT_EMPTY_PATH is denied. + sys_ret = fstatat(read_end.get(), "", &stat_buf, 0); + BPF_ASSERT_EQ(sys_ret, -1); + BPF_ASSERT_EQ(EPERM, errno); + const ssize_t kTestTransferSize = 4; static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'}; ssize_t transfered = 0; diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc index 76eb32493f5..71068a04527 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc @@ -6,6 +6,7 @@ #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" +#include <fcntl.h> #include <stddef.h> #include <stdint.h> #include <string.h> @@ -13,15 +14,16 @@ #include <unistd.h> #include "base/check.h" +#include "base/cxx17_backports.h" #include "base/debug/crash_logging.h" #include "base/posix/eintr_wrapper.h" -#include "base/stl_util.h" #include "build/build_config.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/services/syscall_wrappers.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" #if defined(__mips__) @@ -355,6 +357,24 @@ intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args, return -ENOSYS; } +intptr_t SIGSYSFstatatHandler(const struct arch_seccomp_data& args, + void* fs_denied_errno) { + if (args.nr == __NR_fstatat_default) { + if (*reinterpret_cast<const char*>(args.args[1]) == '\0' && + args.args[3] == static_cast<uint64_t>(AT_EMPTY_PATH)) { + return syscall(__NR_fstat_default, static_cast<int>(args.args[0]), + reinterpret_cast<default_stat_struct*>(args.args[2])); + } + return -reinterpret_cast<intptr_t>(fs_denied_errno); + } + + CrashSIGSYS_Handler(args, fs_denied_errno); + + // Should never be reached. + RAW_CHECK(false); + return -ENOSYS; +} + bpf_dsl::ResultExpr CrashSIGSYS() { return bpf_dsl::Trap(CrashSIGSYS_Handler, NULL); } @@ -387,6 +407,11 @@ bpf_dsl::ResultExpr RewriteSchedSIGSYS() { return bpf_dsl::Trap(SIGSYSSchedHandler, NULL); } +bpf_dsl::ResultExpr RewriteFstatatSIGSYS(int fs_denied_errno) { + return bpf_dsl::Trap(SIGSYSFstatatHandler, + reinterpret_cast<void*>(fs_denied_errno)); +} + void AllocateCrashKeys() { #if !defined(OS_NACL_NONSFI) if (seccomp_crash_key) diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h index 7a958b93b27..8cd735ce157 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h @@ -62,6 +62,19 @@ SANDBOX_EXPORT intptr_t SIGSYSPtraceFailure(const arch_seccomp_data& args, // sched_setparam(), sched_setscheduler() SANDBOX_EXPORT intptr_t SIGSYSSchedHandler(const arch_seccomp_data& args, void* aux); +// If the fstatat() syscall is functionally equivalent to an fstat() syscall, +// then rewrite the syscall to the equivalent fstat() syscall which can be +// adequately sandboxed. +// If the fstatat() is not functionally equivalent to an fstat() syscall, we +// fail with -fs_denied_errno. +// If the syscall is not an fstatat() at all, crash in the same way as +// CrashSIGSYS_Handler. +// This is necessary because glibc and musl have started rewriting fstat(fd, +// stat_buf) as fstatat(fd, "", stat_buf, AT_EMPTY_PATH). We rewrite the latter +// back to the former, which is actually sandboxable. +SANDBOX_EXPORT intptr_t +SIGSYSFstatatHandler(const struct arch_seccomp_data& args, + void* fs_denied_errno); // Variants of the above functions for use with bpf_dsl. SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS(); @@ -72,6 +85,7 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill(); SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex(); SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPtrace(); SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS(); +SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteFstatatSIGSYS(int fs_denied_errno); // Allocates a crash key so that Seccomp information can be recorded. void AllocateCrashKeys(); diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index cc0e91b203c..a8f860fcf08 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc @@ -64,6 +64,14 @@ #if defined(__mips__) && !defined(MAP_STACK) #define MAP_STACK 0x40000 #endif + +// Temporary definitions for Arm's Memory Tagging Extension (MTE) and Branch +// Target Identification (BTI). +#if defined(ARCH_CPU_ARM64) +#define PROT_MTE 0x20 +#define PROT_BTI 0x10 +#endif + namespace { inline bool IsArchitectureX86_64() { @@ -229,7 +237,15 @@ ResultExpr RestrictMprotectFlags() { // "denied" mask because of the negation operator. // Significantly, we don't permit weird undocumented flags such as // PROT_GROWSDOWN. - const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC; +#if defined(ARCH_CPU_ARM64) + // Allows PROT_MTE and PROT_BTI (as explained higher up) on only Arm + // platforms. + const uint64_t kArchSpecificFlags = PROT_MTE | PROT_BTI; +#else + const uint64_t kArchSpecificFlags = 0; +#endif + const uint64_t kAllowedMask = + PROT_READ | PROT_WRITE | PROT_EXEC | kArchSpecificFlags; const Arg<int> prot(2); return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()); } @@ -446,4 +462,9 @@ ResultExpr RestrictPtrace() { } #endif // defined(OS_NACL_NONSFI) +ResultExpr RestrictPkeyAllocFlags() { + const Arg<int> flags(0); + return If(flags == 0, Allow()).Else(CrashSIGSYS()); +} + } // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h index ba4289f05be..348970b39e7 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h @@ -39,6 +39,7 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMmapFlags(); // Restrict the prot argument in mprotect(2). // Only allow: PROT_READ | PROT_WRITE | PROT_EXEC. +// PROT_BTI | PROT_MTE is additionally allowed on 64-bit Arm. SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMprotectFlags(); // Restrict fcntl(2) cmd argument to: @@ -113,6 +114,9 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimitToGetrlimit(pid_t target_pid); // reporting. See https://crbug.com/933418 for details. SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPtrace(); +// Restrict the flags argument for pkey_alloc. It's specified to always be 0. +SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPkeyAllocFlags(); + } // namespace sandbox. #endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc index 903e702eab1..76c393032c1 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc +++ b/chromium/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/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc index 96c9f490e28..8227dc18546 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc @@ -171,6 +171,7 @@ bool SyscallSets::IsFileSystem(int sysno) { (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) case __NR_statfs64: #endif + case __NR_statx: // EPERM not a valid errno. case __NR_symlinkat: case __NR_truncate: #if defined(__i386__) || defined(__arm__) || \ diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index e8fd90cde6c..d8e451d3e65 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -18,6 +18,7 @@ #include "base/macros.h" #include "base/notreached.h" #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/bpf_dsl/codegen.h" #include "sandbox/linux/bpf_dsl/policy.h" @@ -171,7 +172,6 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level, bool enable_ibpb) { if (!supports_tsync) { SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " "filters for a threadgroup"); - return false; } } diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc index 2bc6619a3e9..39774c46802 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc @@ -16,9 +16,9 @@ #include <vector> +#include "base/cxx17_backports.h" #include "base/memory/page_size.h" #include "base/posix/eintr_wrapper.h" -#include "base/stl_util.h" #include "build/build_config.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/bpf_dsl/policy.h" diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.cc b/chromium/sandbox/linux/seccomp-bpf/trap.cc index f5b86a73ac7..cef1cddc0c3 100644 --- a/chromium/sandbox/linux/seccomp-bpf/trap.cc +++ b/chromium/sandbox/linux/seccomp-bpf/trap.cc @@ -280,8 +280,6 @@ uint16_t Trap::Add(TrapFnc fnc, const void* aux, bool safe) { SANDBOX_DIE( "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING " "is enabled"); - - return 0; } // Each unique pair of TrapFnc and auxiliary data make up a distinct instance diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc index d7b5d8c4413..67025a7dde1 100644 --- a/chromium/sandbox/linux/services/credentials.cc +++ b/chromium/sandbox/linux/services/credentials.cc @@ -18,12 +18,12 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/process/launch.h" -#include "base/stl_util.h" #include "build/build_config.h" #include "sandbox/linux/services/namespace_utils.h" #include "sandbox/linux/services/proc_util.h" diff --git a/chromium/sandbox/linux/services/libc_interceptor.cc b/chromium/sandbox/linux/services/libc_interceptor.cc index fd4ff9156a5..c08a450f070 100644 --- a/chromium/sandbox/linux/services/libc_interceptor.cc +++ b/chromium/sandbox/linux/services/libc_interceptor.cc @@ -23,11 +23,14 @@ #include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/metrics/histogram_functions.h" #include "base/pickle.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/global_descriptors.h" #include "base/posix/unix_domain_socket.h" #include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "base/timer/elapsed_timer.h" namespace sandbox { @@ -119,6 +122,8 @@ void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, char* timezone_out, size_t timezone_out_len) { + base::ElapsedTimer timer; + base::Pickle request; request.WriteInt(METHOD_LOCALTIME); request.WriteString( @@ -137,6 +142,11 @@ void ProxyLocaltimeCallToBrowser(time_t input, if (!ReadTimeStruct(&iter, output, timezone_out, timezone_out_len)) { memset(output, 0, sizeof(struct tm)); } + + base::UmaHistogramCustomMicrosecondsTimes( + "Linux.ProxyLocaltimeCallToBrowserUs", timer.Elapsed(), + base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(1), + /*buckets=*/50); } // The other side of this call is ProxyLocaltimeCallToBrowser(). diff --git a/chromium/sandbox/linux/services/namespace_sandbox.cc b/chromium/sandbox/linux/services/namespace_sandbox.cc index d5d0f68e274..3ae22651576 100644 --- a/chromium/sandbox/linux/services/namespace_sandbox.cc +++ b/chromium/sandbox/linux/services/namespace_sandbox.cc @@ -17,12 +17,12 @@ #include "base/check_op.h" #include "base/command_line.h" +#include "base/cxx17_backports.h" #include "base/environment.h" #include "base/files/scoped_file.h" #include "base/posix/eintr_wrapper.h" #include "base/process/launch.h" #include "base/process/process.h" -#include "base/stl_util.h" #include "build/build_config.h" #include "sandbox/linux/services/credentials.h" #include "sandbox/linux/services/namespace_utils.h" diff --git a/chromium/sandbox/linux/services/syscall_wrappers.cc b/chromium/sandbox/linux/services/syscall_wrappers.cc index fcfd2aa129d..3bec18a14e9 100644 --- a/chromium/sandbox/linux/services/syscall_wrappers.cc +++ b/chromium/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/chromium/sandbox/linux/services/syscall_wrappers.h b/chromium/sandbox/linux/services/syscall_wrappers.h index 1975bfbd88a..b55340e4a26 100644 --- a/chromium/sandbox/linux/services/syscall_wrappers.h +++ b/chromium/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/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc b/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc index 32820f60a8c..723ff3b672e 100644 --- a/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc +++ b/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc @@ -4,16 +4,21 @@ #include "sandbox/linux/services/syscall_wrappers.h" +#include <fcntl.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 +98,184 @@ 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); + if (res < 0 && errno == EOVERFLOW) { + GTEST_SKIP(); + } + 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 +} + +#if defined(__NR_fstatat64) +TEST(SyscallWrappers, Stat64) { + static_assert(sizeof(struct kernel_stat64) == sizeof(default_stat_struct), + "This test only works on systems where the default_stat_struct " + "is kernel_stat64"); + // 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_fstatat64(AT_FDCWD, tmp_file.full_file_name(), + reinterpret_cast<struct kernel_stat64*>(page1_end - 1), 0); + ASSERT_EQ(res, -1); + ASSERT_EQ(errno, EFAULT); + + // Now, check that we have the correctly sized stat structure. + struct kernel_stat64* sb = reinterpret_cast<struct kernel_stat64*>( + page1_end - sizeof(struct kernel_stat64)); + memset(sb, 0, sizeof(struct kernel_stat64)); + res = sys_fstatat64(AT_FDCWD, tmp_file.full_file_name(), sb, 0); + 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. + EXPECT_EQ(geteuid(), sb->st_uid); + EXPECT_EQ(getegid(), sb->st_gid); + + // Wrote 12 bytes above which should fit in one block. + 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_)); +} +#endif // defined(__NR_fstatat64) + +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/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc index f88c5077c6d..7022a65e035 100644 --- a/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc +++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc @@ -15,6 +15,7 @@ #include <utility> #include "base/command_line.h" +#include "base/cxx17_backports.h" #include "base/environment.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -24,7 +25,6 @@ #include "base/posix/eintr_wrapper.h" #include "base/process/launch.h" #include "base/process/process_metrics.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "sandbox/linux/suid/common/sandbox.h" #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" diff --git a/chromium/sandbox/linux/suid/sandbox.c b/chromium/sandbox/linux/suid/sandbox.c index 5ee1689e234..2bedcb34f2d 100644 --- a/chromium/sandbox/linux/suid/sandbox.c +++ b/chromium/sandbox/linux/suid/sandbox.c @@ -480,6 +480,4 @@ int main(int argc, char** argv) { execv(argv[1], &argv[1]); FatalError("execv failed"); - - return 1; } diff --git a/chromium/sandbox/linux/syscall_broker/DEPS b/chromium/sandbox/linux/syscall_broker/DEPS index c477f7d3639..149c463b068 100644 --- a/chromium/sandbox/linux/syscall_broker/DEPS +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc index 6b1b5be4338..e24f659fcf8 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_client.cc +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/broker_client.h b/chromium/sandbox/linux/syscall_broker/broker_client.h index 05e14c83f20..26ca78101c7 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_client.h +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/broker_host.cc b/chromium/sandbox/linux/syscall_broker/broker_host.cc index 1cd03a18df8..1cdc01a888f 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_host.cc +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc index c2176eb785e..e9dad37485a 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc @@ -113,44 +113,49 @@ bool BrokerProcess::IsSyscallAllowed(int sysno) const { } bool BrokerProcess::IsSyscallBrokerable(int sysno, bool fast_check) const { + // The syscalls unavailable on aarch64 are all blocked by Android's default + // seccomp policy, even on non-aarch64 architectures. I.e., the syscalls XX() + // with a corresponding XXat() versions are typically unavailable in aarch64 + // and are default disabled in Android. So, we should refuse to broker them + // to be consistent with the platform's restrictions. switch (sysno) { -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_access: #endif case __NR_faccessat: return !fast_check || allowed_command_set_.test(COMMAND_ACCESS); -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_mkdir: #endif case __NR_mkdirat: return !fast_check || allowed_command_set_.test(COMMAND_MKDIR); -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_open: #endif case __NR_openat: return !fast_check || allowed_command_set_.test(COMMAND_OPEN); -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_readlink: #endif case __NR_readlinkat: return !fast_check || allowed_command_set_.test(COMMAND_READLINK); -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_rename: #endif case __NR_renameat: case __NR_renameat2: return !fast_check || allowed_command_set_.test(COMMAND_RENAME); -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_rmdir: return !fast_check || allowed_command_set_.test(COMMAND_RMDIR); #endif -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_stat: case __NR_lstat: #endif @@ -175,7 +180,7 @@ bool BrokerProcess::IsSyscallBrokerable(int sysno, bool fast_check) const { return !fast_check || allowed_command_set_.test(COMMAND_STAT); #endif -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(OS_ANDROID) case __NR_unlink: return !fast_check || allowed_command_set_.test(COMMAND_UNLINK); #endif diff --git a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc index 08096b95aa3..f0db08d84e0 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc @@ -23,12 +23,12 @@ #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" +#include "base/cxx17_backports.h" #include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/unix_domain_socket.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "sandbox/linux/syscall_broker/broker_client.h" @@ -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_)); } } @@ -1590,52 +1596,52 @@ TEST(BrokerProcess, IsSyscallAllowed) { const base::flat_map<BrokerCommand, base::flat_set<int>> kSysnosForCommand = { {COMMAND_ACCESS, {__NR_faccessat, -#if defined(__NR_access) +#if defined(__NR_access) && !defined(OS_ANDROID) __NR_access #endif }}, {COMMAND_MKDIR, {__NR_mkdirat, -#if defined(__NR_mkdir) +#if defined(__NR_mkdir) && !defined(OS_ANDROID) __NR_mkdir #endif }}, {COMMAND_OPEN, {__NR_openat, -#if defined(__NR_open) +#if defined(__NR_open) && !defined(OS_ANDROID) __NR_open #endif }}, {COMMAND_READLINK, {__NR_readlinkat, -#if defined(__NR_readlink) +#if defined(__NR_readlink) && !defined(OS_ANDROID) __NR_readlink #endif }}, {COMMAND_RENAME, {__NR_renameat, -#if defined(__NR_rename) +#if defined(__NR_rename) && !defined(OS_ANDROID) __NR_rename #endif }}, {COMMAND_UNLINK, {__NR_unlinkat, -#if defined(__NR_unlink) +#if defined(__NR_unlink) && !defined(OS_ANDROID) __NR_unlink #endif }}, {COMMAND_RMDIR, {__NR_unlinkat, -#if defined(__NR_rmdir) +#if defined(__NR_rmdir) && !defined(OS_ANDROID) __NR_rmdir #endif }}, {COMMAND_STAT, { -#if defined(__NR_stat) +#if defined(__NR_stat) && !defined(OS_ANDROID) __NR_stat, #endif -#if defined(__NR_lstat) +#if defined(__NR_lstat) && !defined(OS_ANDROID) __NR_lstat, #endif #if defined(__NR_fstatat) diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc index d64cba22281..c3aca7d522d 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc @@ -61,7 +61,7 @@ class ExpectedResultDataValue : public ExpectedResultValue { class ExpectedResultIntValue : public ExpectedResultValue { public: - ExpectedResultIntValue(int value); + explicit ExpectedResultIntValue(int value); bool NextMessagePieceMatches(BrokerSimpleMessage* message) override; size_t Size() override; diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc index 1ba5890daff..256586e141b 100644 --- a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc +++ b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc @@ -138,7 +138,7 @@ RemoteProcessIOResult ReadFilePathFromRemoteProcess(pid_t pid, namespace internal { uintptr_t NumBytesLeftInPage(uintptr_t addr) { - const uintptr_t page_end = base::bits::Align(addr + 1, base::GetPageSize()); + const uintptr_t page_end = base::bits::AlignUp(addr + 1, base::GetPageSize()); return page_end - addr; } } // namespace internal diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc index fffa9bb7082..f517a9867c5 100644 --- a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc index b9ee93c14ac..8a42397ef87 100644 --- a/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc +++ b/chromium/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/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h index d8b8874ad9c..1d6653caf3b 100644 --- a/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h +++ b/chromium/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/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h b/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h index a242c18c842..ab86b36353c 100644 --- a/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h @@ -1119,4 +1119,100 @@ #define __NR_rseq 293 #endif +#if !defined(__NR_kexec_file_load) +#define __NR_kexec_file_load 294 +#endif + +#if !defined(__NR_pidfd_send_signal) +#define __NR_pidfd_send_signal 424 +#endif + +#if !defined(__NR_io_uring_setup) +#define __NR_io_uring_setup 425 +#endif + +#if !defined(__NR_io_uring_enter) +#define __NR_io_uring_enter 426 +#endif + +#if !defined(__NR_io_uring_register) +#define __NR_io_uring_register 427 +#endif + +#if !defined(__NR_open_tree) +#define __NR_open_tree 428 +#endif + +#if !defined(__NR_move_mount) +#define __NR_move_mount 429 +#endif + +#if !defined(__NR_fsopen) +#define __NR_fsopen 430 +#endif + +#if !defined(__NR_fsconfig) +#define __NR_fsconfig 431 +#endif + +#if !defined(__NR_fsmount) +#define __NR_fsmount 432 +#endif + +#if !defined(__NR_fspick) +#define __NR_fspick 433 +#endif + +#if !defined(__NR_pidfd_open) +#define __NR_pidfd_open 434 +#endif + +#if !defined(__NR_clone3) +#define __NR_clone3 435 +#endif + +#if !defined(__NR_close_range) +#define __NR_close_range 436 +#endif + +#if !defined(__NR_openat2) +#define __NR_openat2 437 +#endif + +#if !defined(__NR_pidfd_getfd) +#define __NR_pidfd_getfd 438 +#endif + +#if !defined(__NR_faccessat2) +#define __NR_faccessat2 439 +#endif + +#if !defined(__NR_process_madvise) +#define __NR_process_madvise 440 +#endif + +#if !defined(__NR_epoll_pwait2) +#define __NR_epoll_pwait2 441 +#endif + +#if !defined(__NR_mount_setattr) +#define __NR_mount_setattr 442 +#endif + +#if !defined(__NR_quotactl_path) +#define __NR_quotactl_path 443 +#endif + +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset 444 +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule 445 +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self 446 +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h b/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h index 85da6f41c66..9c44368a8ee 100644 --- a/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h @@ -1605,6 +1605,18 @@ #define __NR_mount_setattr (__NR_SYSCALL_BASE + 442) #endif +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset (__NR_SYSCALL_BASE + 444) +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule (__NR_SYSCALL_BASE + 445) +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self (__NR_SYSCALL_BASE + 446) +#endif + // ARM private syscalls. #if !defined(__ARM_NR_BASE) #define __ARM_NR_BASE (__NR_SYSCALL_BASE + 0xF0000) diff --git a/chromium/sandbox/linux/system_headers/linux_stat.h b/chromium/sandbox/linux/system_headers/linux_stat.h new file mode 100644 index 00000000000..e697dd6777e --- /dev/null +++ b/chromium/sandbox/linux/system_headers/linux_stat.h @@ -0,0 +1,196 @@ +// 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 + +#if !defined(AT_EMPTY_PATH) +#define AT_EMPTY_PATH 0x1000 +#endif + +#if !defined(STATX_BASIC_STATS) +#define STATX_BASIC_STATS 0x000007ffU +#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/chromium/sandbox/linux/system_headers/linux_time.h b/chromium/sandbox/linux/system_headers/linux_time.h index 780f24dddd9..f18c806611f 100644 --- a/chromium/sandbox/linux/system_headers/linux_time.h +++ b/chromium/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/chromium/sandbox/linux/system_headers/mips64_linux_syscalls.h b/chromium/sandbox/linux/system_headers/mips64_linux_syscalls.h index ec75815a842..ae7cb48f57c 100644 --- a/chromium/sandbox/linux/system_headers/mips64_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/mips64_linux_syscalls.h @@ -1271,4 +1271,148 @@ #define __NR_memfd_create (__NR_Linux + 314) #endif +#if !defined(__NR_bpf) +#define __NR_bpf (__NR_Linux + 315) +#endif + +#if !defined(__NR_execveat) +#define __NR_execveat (__NR_Linux + 316) +#endif + +#if !defined(__NR_userfaultfd) +#define __NR_userfaultfd (__NR_Linux + 317) +#endif + +#if !defined(__NR_membarrier) +#define __NR_membarrier (__NR_Linux + 318) +#endif + +#if !defined(__NR_mlock2) +#define __NR_mlock2 (__NR_Linux + 319) +#endif + +#if !defined(__NR_copy_file_range) +#define __NR_copy_file_range (__NR_Linux + 320) +#endif + +#if !defined(__NR_preadv2) +#define __NR_preadv2 (__NR_Linux + 321) +#endif + +#if !defined(__NR_pwritev2) +#define __NR_pwritev2 (__NR_Linux + 322) +#endif + +#if !defined(__NR_pkey_mprotect) +#define __NR_pkey_mprotect (__NR_Linux + 323) +#endif + +#if !defined(__NR_pkey_alloc) +#define __NR_pkey_alloc (__NR_Linux + 324) +#endif + +#if !defined(__NR_pkey_free) +#define __NR_pkey_free (__NR_Linux + 325) +#endif + +#if !defined(__NR_statx) +#define __NR_statx (__NR_Linux + 326) +#endif + +#if !defined(__NR_rseq) +#define __NR_rseq (__NR_Linux + 327) +#endif + +#if !defined(__NR_io_pgetevents) +#define __NR_io_pgetevents (__NR_Linux + 328) +#endif + +#if !defined(__NR_pidfd_send_signal) +#define __NR_pidfd_send_signal (__NR_Linux + 424) +#endif + +#if !defined(__NR_io_uring_setup) +#define __NR_io_uring_setup (__NR_Linux + 425) +#endif + +#if !defined(__NR_io_uring_enter) +#define __NR_io_uring_enter (__NR_Linux + 426) +#endif + +#if !defined(__NR_io_uring_register) +#define __NR_io_uring_register (__NR_Linux + 427) +#endif + +#if !defined(__NR_open_tree) +#define __NR_open_tree (__NR_Linux + 428) +#endif + +#if !defined(__NR_move_mount) +#define __NR_move_mount (__NR_Linux + 429) +#endif + +#if !defined(__NR_fsopen) +#define __NR_fsopen (__NR_Linux + 430) +#endif + +#if !defined(__NR_fsconfig) +#define __NR_fsconfig (__NR_Linux + 431) +#endif + +#if !defined(__NR_fsmount) +#define __NR_fsmount (__NR_Linux + 432) +#endif + +#if !defined(__NR_fspick) +#define __NR_fspick (__NR_Linux + 433) +#endif + +#if !defined(__NR_pidfd_open) +#define __NR_pidfd_open (__NR_Linux + 434) +#endif + +#if !defined(__NR_clone3) +#define __NR_clone3 (__NR_Linux + 435) +#endif + +#if !defined(__NR_close_range) +#define __NR_close_range (__NR_Linux + 436) +#endif + +#if !defined(__NR_openat2) +#define __NR_openat2 (__NR_Linux + 437) +#endif + +#if !defined(__NR_pidfd_getfd) +#define __NR_pidfd_getfd (__NR_Linux + 438) +#endif + +#if !defined(__NR_faccessat2) +#define __NR_faccessat2 (__NR_Linux + 439) +#endif + +#if !defined(__NR_process_madvise) +#define __NR_process_madvise (__NR_Linux + 440) +#endif + +#if !defined(__NR_epoll_pwait2) +#define __NR_epoll_pwait2 (__NR_Linux + 441) +#endif + +#if !defined(__NR_mount_setattr) +#define __NR_mount_setattr (__NR_Linux + 442) +#endif + +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset (__NR_Linux + 444) +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule (__NR_Linux + 445) +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self (__NR_Linux + 446) +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS64_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/linux/system_headers/mips_linux_syscalls.h b/chromium/sandbox/linux/system_headers/mips_linux_syscalls.h index 50d9ea11bfa..093778288bb 100644 --- a/chromium/sandbox/linux/system_headers/mips_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/mips_linux_syscalls.h @@ -1685,4 +1685,16 @@ #define __NR_mount_setattr (__NR_Linux + 442) #endif +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset (__NR_Linux + 444) +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule (__NR_Linux + 445) +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self (__NR_Linux + 446) +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h b/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h index 1720edb1810..2c81a930138 100644 --- a/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h @@ -1738,5 +1738,17 @@ #define __NR_mount_setattr 442 #endif +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset 444 +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule 445 +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self 446 +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h b/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h index b0ae0a2edf6..e618c6237b0 100644 --- a/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h @@ -1350,5 +1350,93 @@ #define __NR_rseq 334 #endif +#if !defined(__NR_pidfd_send_signal) +#define __NR_pidfd_send_signal 424 +#endif + +#if !defined(__NR_io_uring_setup) +#define __NR_io_uring_setup 425 +#endif + +#if !defined(__NR_io_uring_enter) +#define __NR_io_uring_enter 426 +#endif + +#if !defined(__NR_io_uring_register) +#define __NR_io_uring_register 427 +#endif + +#if !defined(__NR_open_tree) +#define __NR_open_tree 428 +#endif + +#if !defined(__NR_move_mount) +#define __NR_move_mount 429 +#endif + +#if !defined(__NR_fsopen) +#define __NR_fsopen 430 +#endif + +#if !defined(__NR_fsconfig) +#define __NR_fsconfig 431 +#endif + +#if !defined(__NR_fsmount) +#define __NR_fsmount 432 +#endif + +#if !defined(__NR_fspick) +#define __NR_fspick 433 +#endif + +#if !defined(__NR_pidfd_open) +#define __NR_pidfd_open 434 +#endif + +#if !defined(__NR_clone3) +#define __NR_clone3 435 +#endif + +#if !defined(__NR_close_range) +#define __NR_close_range 436 +#endif + +#if !defined(__NR_openat2) +#define __NR_openat2 437 +#endif + +#if !defined(__NR_pidfd_getfd) +#define __NR_pidfd_getfd 438 +#endif + +#if !defined(__NR_faccessat2) +#define __NR_faccessat2 439 +#endif + +#if !defined(__NR_process_madvise) +#define __NR_process_madvise 440 +#endif + +#if !defined(__NR_epoll_pwait2) +#define __NR_epoll_pwait2 441 +#endif + +#if !defined(__NR_mount_setattr) +#define __NR_mount_setattr 442 +#endif + +#if !defined(__NR_landlock_create_ruleset) +#define __NR_landlock_create_ruleset 444 +#endif + +#if !defined(__NR_landlock_add_rule) +#define __NR_landlock_add_rule 445 +#endif + +#if !defined(__NR_landlock_restrict_self) +#define __NR_landlock_restrict_self 446 +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/mac/seatbelt_extension_unittest.cc b/chromium/sandbox/mac/seatbelt_extension_unittest.cc index 2a746886742..ef1d7ceecb1 100644 --- a/chromium/sandbox/mac/seatbelt_extension_unittest.cc +++ b/chromium/sandbox/mac/seatbelt_extension_unittest.cc @@ -7,11 +7,11 @@ #include <unistd.h> #include "base/command_line.h" +#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" -#include "base/stl_util.h" #include "base/test/multiprocess_test.h" #include "base/test/test_timeouts.h" #include "sandbox/mac/sandbox_compiler.h" diff --git a/chromium/sandbox/policy/BUILD.gn b/chromium/sandbox/policy/BUILD.gn index 7dfa7764de3..69828e5bb9f 100644 --- a/chromium/sandbox/policy/BUILD.gn +++ b/chromium/sandbox/policy/BUILD.gn @@ -10,6 +10,7 @@ import("//chromeos/assistant/assistant.gni") import("//printing/buildflags/buildflags.gni") import("//testing/test.gni") +# Most consumers of sandbox::policy should depend on this target. component("policy") { sources = [ "export.h", @@ -30,6 +31,7 @@ component("policy") { "//build:chromeos_buildflags", "//printing/buildflags", "//sandbox:common", + "//sandbox/policy/mojom", ] public_deps = [] if (is_linux || is_chromeos) { @@ -92,7 +94,7 @@ component("policy") { ] deps += [ "//chromeos/assistant:buildflags" ] - if (enable_libassistant_sandbox) { + if (enable_cros_libassistant) { sources += [ "linux/bpf_libassistant_policy_linux.cc", "linux/bpf_libassistant_policy_linux.h", diff --git a/chromium/sandbox/policy/features.cc b/chromium/sandbox/policy/features.cc index 8ba5f501bda..4922dad3810 100644 --- a/chromium/sandbox/policy/features.cc +++ b/chromium/sandbox/policy/features.cc @@ -4,6 +4,7 @@ #include "sandbox/policy/features.h" +#include "base/win/windows_version.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -24,10 +25,10 @@ const base::Feature kNetworkServiceSandbox{"NetworkServiceSandbox", const base::Feature kWinSboxDisableKtmComponent{ "WinSboxDisableKtmComponent", base::FEATURE_ENABLED_BY_DEFAULT}; -// Emergency "off switch" for new Windows sandbox security mitigation, +// Experiment for Windows sandbox security mitigation, // sandbox::MITIGATION_EXTENSION_POINT_DISABLE. const base::Feature kWinSboxDisableExtensionPoints{ - "WinSboxDisableExtensionPoint", base::FEATURE_ENABLED_BY_DEFAULT}; + "WinSboxDisableExtensionPoint", base::FEATURE_DISABLED_BY_DEFAULT}; // Enables GPU AppContainer sandbox on Windows. const base::Feature kGpuAppContainer{"GpuAppContainer", @@ -36,10 +37,6 @@ const base::Feature kGpuAppContainer{"GpuAppContainer", // Enables GPU Low Privilege AppContainer when combined with kGpuAppContainer. const base::Feature kGpuLPAC{"GpuLPAC", base::FEATURE_ENABLED_BY_DEFAULT}; -// Use LPAC for network sandbox instead of restricted token. Relies on -// NetworkServiceSandbox being also enabled. -const base::Feature kNetworkServiceSandboxLPAC{ - "NetworkServiceSandboxLPAC", base::FEATURE_DISABLED_BY_DEFAULT}; #endif // defined(OS_WIN) #if !defined(OS_ANDROID) @@ -61,6 +58,23 @@ const base::Feature kForceSpectreVariant2Mitigation{ "ForceSpectreVariant2Mitigation", base::FEATURE_DISABLED_BY_DEFAULT}; #endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if defined(OS_WIN) +bool IsNetworkServiceSandboxLPACEnabled() { + // Use LPAC for network sandbox instead of restricted token. Relies on + // NetworkServiceSandbox being also enabled. + const base::Feature kNetworkServiceSandboxLPAC{ + "NetworkServiceSandboxLPAC", base::FEATURE_DISABLED_BY_DEFAULT}; + + // Since some APIs used for LPAC are unsupported below Windows 10, place a + // check here in a central place. + if (base::win::GetVersion() < base::win::Version::WIN10) + return false; + + return base::FeatureList::IsEnabled(kNetworkServiceSandbox) && + base::FeatureList::IsEnabled(kNetworkServiceSandboxLPAC); +} +#endif // defined(OS_WIN) + } // namespace features } // namespace policy } // namespace sandbox diff --git a/chromium/sandbox/policy/features.h b/chromium/sandbox/policy/features.h index 6de7bbd6742..86b7d433e92 100644 --- a/chromium/sandbox/policy/features.h +++ b/chromium/sandbox/policy/features.h @@ -26,7 +26,6 @@ SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableKtmComponent; SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableExtensionPoints; SANDBOX_POLICY_EXPORT extern const base::Feature kGpuAppContainer; SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC; -SANDBOX_POLICY_EXPORT extern const base::Feature kNetworkServiceSandboxLPAC; #endif // defined(OS_WIN) #if !defined(OS_ANDROID) @@ -39,6 +38,10 @@ SANDBOX_POLICY_EXPORT extern const base::Feature kForceSpectreVariant2Mitigation; #endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if defined(OS_WIN) +// Returns whether Network Service Sandbox is enabled in LPAC mode. +SANDBOX_POLICY_EXPORT bool IsNetworkServiceSandboxLPACEnabled(); +#endif } // namespace features } // namespace policy } // namespace sandbox diff --git a/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc index e105eee8c06..c08356d7830 100644 --- a/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc +++ b/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc @@ -13,6 +13,7 @@ #include <fuchsia/fonts/cpp/fidl.h> #include <fuchsia/intl/cpp/fidl.h> #include <fuchsia/logger/cpp/fidl.h> +#include <fuchsia/media/cpp/fidl.h> #include <fuchsia/mediacodec/cpp/fidl.h> #include <fuchsia/memorypressure/cpp/fidl.h> #include <fuchsia/net/cpp/fidl.h> @@ -66,6 +67,8 @@ struct SandboxConfig { constexpr SandboxConfig kGpuConfig = { base::make_span((const char* const[]){ + // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead. + fuchsia::media::ProfileProvider::Name_, fuchsia::sysmem::Allocator::Name_, "fuchsia.vulkan.loader.Loader", fuchsia::ui::scenic::Scenic::Name_, @@ -76,6 +79,7 @@ constexpr SandboxConfig kGpuConfig = { constexpr SandboxConfig kNetworkConfig = { base::make_span((const char* const[]){ fuchsia::net::NameLookup::Name_, + "fuchsia.net.name.Lookup", fuchsia::net::interfaces::State::Name_, "fuchsia.posix.socket.Provider", }), @@ -85,6 +89,8 @@ constexpr SandboxConfig kNetworkConfig = { constexpr SandboxConfig kRendererConfig = { base::make_span((const char* const[]){ fuchsia::fonts::Provider::Name_, + // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead. + fuchsia::media::ProfileProvider::Name_, fuchsia::mediacodec::CodecFactory::Name_, fuchsia::memorypressure::Provider::Name_, fuchsia::sysmem::Allocator::Name_, @@ -227,20 +233,20 @@ void SandboxPolicyFuchsia::UpdateLaunchOptionsForSandbox( options->paths_to_clone.push_back(base::FilePath("/config/ssl")); if (config->features & kProvideVulkanResources) { - // /dev/class/gpu and /config/vulkan/icd.d are to used configure and - // access the GPU. - options->paths_to_clone.push_back(base::FilePath("/dev/class/gpu")); - const auto vulkan_icd_path = base::FilePath("/config/vulkan/icd.d"); - if (base::PathExists(vulkan_icd_path)) - options->paths_to_clone.push_back(vulkan_icd_path); - - // The following devices are used for Fuchsia Emulator. - options->paths_to_clone.insert( - options->paths_to_clone.end(), - {base::FilePath("/dev/class/goldfish-address-space"), - base::FilePath("/dev/class/goldfish-control"), - base::FilePath("/dev/class/goldfish-pipe"), - base::FilePath("/dev/class/goldfish-sync")}); + static const char* const kPathsToCloneForVulkan[] = { + // Used configure and access the GPU. + "/dev/class/gpu", "/config/vulkan/icd.d", + // Used for Fuchsia Emulator. + "/dev/class/goldfish-address-space", "/dev/class/goldfish-control", + "/dev/class/goldfish-pipe", "/dev/class/goldfish-sync"}; + for (const char* path_str : kPathsToCloneForVulkan) { + base::FilePath path(path_str); + // Vulkan paths aren't needed with newer Fuchsia versions, so they may not + // be available. + if (base::PathExists(path)) { + options->paths_to_clone.push_back(path); + } + } } // If the process needs access to any services then transfer the diff --git a/chromium/sandbox/policy/linux/bpf_broker_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_broker_policy_linux.cc index 2963bb9ca86..6dc8c0581b4 100644 --- a/chromium/sandbox/policy/linux/bpf_broker_policy_linux.cc +++ b/chromium/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; diff --git a/chromium/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc index 76e310533b6..124b4c32fab 100644 --- a/chromium/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc +++ b/chromium/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc @@ -53,6 +53,14 @@ ResultExpr CrosArmGpuProcessPolicy::EvaluateSyscall(int sysno) const { case __NR_sched_setscheduler: case __NR_sysinfo: case __NR_uname: + // We need fstatfs for supporting a local glibc patch + // which hooks into dlopen(), LD_PRELOAD, and --preload. + // https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/2910526 + case __NR_fstatfs: +#if defined(__arm__) + // Only available on ARM 32bit devices + case __NR_fstatfs64: +#endif return Allow(); // Allow only AF_UNIX for |domain|. case __NR_socket: diff --git a/chromium/sandbox/policy/linux/bpf_libassistant_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_libassistant_policy_linux.cc index a00dd4fdc04..f235efa9f57 100644 --- a/chromium/sandbox/policy/linux/bpf_libassistant_policy_linux.cc +++ b/chromium/sandbox/policy/linux/bpf_libassistant_policy_linux.cc @@ -24,16 +24,23 @@ LibassistantProcessPolicy::LibassistantProcessPolicy() = default; LibassistantProcessPolicy::~LibassistantProcessPolicy() = default; ResultExpr LibassistantProcessPolicy::EvaluateSyscall(int sysno) const { + switch (sysno) { +#if defined(__NR_getcpu) + // Needed by arm devices. + case __NR_getcpu: + return Allow(); +#endif #if defined(__NR_sched_setscheduler) - if (sysno == __NR_sched_setscheduler) - return Allow(); + case __NR_sched_setscheduler: + return Allow(); #endif + default: + auto* sandbox_linux = SandboxLinux::GetInstance(); + if (sandbox_linux->ShouldBrokerHandleSyscall(sysno)) + return sandbox_linux->HandleViaBroker(); - auto* sandbox_linux = SandboxLinux::GetInstance(); - if (sandbox_linux->ShouldBrokerHandleSyscall(sysno)) - return sandbox_linux->HandleViaBroker(); - - return BPFBasePolicy::EvaluateSyscall(sysno); + return BPFBasePolicy::EvaluateSyscall(sysno); + } } } // namespace policy diff --git a/chromium/sandbox/policy/linux/bpf_service_policy_linux.h b/chromium/sandbox/policy/linux/bpf_service_policy_linux.h index 2c7a33595b0..7dad88cf61c 100644 --- a/chromium/sandbox/policy/linux/bpf_service_policy_linux.h +++ b/chromium/sandbox/policy/linux/bpf_service_policy_linux.h @@ -29,4 +29,4 @@ class ServiceProcessPolicy : public BPFBasePolicy { } // namespace policy } // namespace sandbox -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SERVICE_POLICY_LINUX_H_ +#endif // SANDBOX_POLICY_LINUX_BPF_SERVICE_POLICY_LINUX_H_ diff --git a/chromium/sandbox/policy/linux/sandbox_linux.cc b/chromium/sandbox/policy/linux/sandbox_linux.cc index ba21e015b5a..e7f2fecb4f7 100644 --- a/chromium/sandbox/policy/linux/sandbox_linux.cc +++ b/chromium/sandbox/policy/linux/sandbox_linux.cc @@ -490,8 +490,12 @@ void SandboxLinux::StartBrokerProcess( PreSandboxHook broker_side_hook, const Options& options) { // Leaked at shutdown, so use bare |new|. + // Use EACCES as the policy's default error number to remain consistent with + // other LSMs like AppArmor and Landlock. Some userspace code, such as + // glibc's |dlopen|, expect to see EACCES rather than EPERM. See + // crbug.com/1233028 for an example. broker_process_ = new syscall_broker::BrokerProcess( - BPFBasePolicy::GetFSDeniedErrno(), allowed_command_set, permissions, + EACCES, allowed_command_set, permissions, syscall_broker::BrokerProcess::BrokerType::SIGNAL_BASED); // The initialization callback will perform generic initialization and then diff --git a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc index 5d572af56b4..185eef17ced 100644 --- a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc +++ b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc @@ -61,9 +61,9 @@ #include "sandbox/policy/linux/bpf_tts_policy_linux.h" #include "chromeos/assistant/buildflags.h" -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #include "sandbox/policy/linux/bpf_libassistant_policy_linux.h" -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) using sandbox::bpf_dsl::Allow; @@ -199,10 +199,10 @@ std::unique_ptr<BPFBasePolicy> SandboxSeccompBPF::PolicyForSandboxType( return std::make_unique<ImeProcessPolicy>(); case SandboxType::kTts: return std::make_unique<TtsProcessPolicy>(); -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case SandboxType::kLibassistant: return std::make_unique<LibassistantProcessPolicy>(); -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) case SandboxType::kZygoteIntermediateSandbox: case SandboxType::kNoSandbox: @@ -236,7 +236,12 @@ void SandboxSeccompBPF::RunSandboxSanityChecks( // open() must be restricted. syscall_ret = open("/etc/passwd", O_RDONLY); CHECK_EQ(-1, syscall_ret); - CHECK_EQ(BPFBasePolicy::GetFSDeniedErrno(), errno); + // The broker used with the GPU process sandbox uses EACCES for + // invalid filesystem access. See crbug.com/1233028 for more info. + CHECK_EQ(sandbox_type == SandboxType::kGpu + ? EACCES + : BPFBasePolicy::GetFSDeniedErrno(), + errno); // We should never allow the creation of netlink sockets. syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); @@ -247,9 +252,9 @@ void SandboxSeccompBPF::RunSandboxSanityChecks( #if BUILDFLAG(IS_CHROMEOS_ASH) case SandboxType::kIme: case SandboxType::kTts: -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case SandboxType::kLibassistant: -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) case SandboxType::kAudio: case SandboxType::kService: diff --git a/chromium/sandbox/policy/mojom/BUILD.gn b/chromium/sandbox/policy/mojom/BUILD.gn new file mode 100644 index 00000000000..0c35f3d5877 --- /dev/null +++ b/chromium/sandbox/policy/mojom/BUILD.gn @@ -0,0 +1,10 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("mojom") { + generate_java = true + sources = [ "sandbox.mojom" ] +} diff --git a/chromium/sandbox/policy/mojom/OWNERS b/chromium/sandbox/policy/mojom/OWNERS new file mode 100644 index 00000000000..08850f42120 --- /dev/null +++ b/chromium/sandbox/policy/mojom/OWNERS @@ -0,0 +1,2 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS diff --git a/chromium/sandbox/policy/mojom/sandbox.mojom b/chromium/sandbox/policy/mojom/sandbox.mojom new file mode 100644 index 00000000000..403504483ca --- /dev/null +++ b/chromium/sandbox/policy/mojom/sandbox.mojom @@ -0,0 +1,61 @@ +// 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. + +module sandbox.mojom; + +// Sandbox type that can be specified as an attribute of mojo interfaces. +// To specify the sandbox a service should be launched in, use the +// [ServiceSandbox=type] attribute. +// If your service does not need access to OS resources it should be +// possible to host it in |kService|. These values are mapped to +// //sandbox/policy/sandbox_type.h values. +enum Sandbox { + // |kService| hosts 'computation only' services such as decoders that + // use limited operating system services. Prefer to use this sandbox + // if possible. + kService, + + // Hosts generic utilities with limited access to system services. + // On some platforms, may be slightly less locked down than |kService|. + // For instance, it allows dynamic code and wider access to APIs on Windows. + kUtility, + + // Hosts the content decryption module. Allows pre-loading of CDM libraries. + // - On Windows, when `CdmServiceBroker` is connected the CDM was not + // sandboxed to allow CDM preloading. + // - On Mac, the process is fully sandboxed when launched. + // - On Linux/ChromeOS, the CDM is preloaded in the zygote sandbox. + kCdm, + + // Composits PDF and XPS documents. + kPrintCompositor, + + // Equivalent to no sandbox on all non-Fuchsia platforms. + // Minimally privileged sandbox on Fuchsia. + // TODO(crbug.com/1236898) Fix this mapping. + kVideoCapture, + + // Allows LPAC capabilities for the Windws Media Foundation CDM, including + // internet and private network access, COM, Identity & others. Allows access + // to files in the `mediaFoundationCdmFiles` Chromium lpac. + [EnableIf=is_win] + kMediaFoundationCdm, + + // |kXrCompositing| hosts XR Device Service on Windows. + [EnableIf=is_win] + kXrCompositing, + + // Hosts Input Method Editors. + [EnableIf=is_chromeos_ash] + kIme, + + // Text-to-speech. + [EnableIf=is_chromeos_ash] + kTts, + + // Hosts the Libassistant service on ChromeOS Ash, only used for + // Chrome branded builds. + [EnableIf=is_chromeos_ash] + kLibassistant, +}; diff --git a/chromium/sandbox/policy/sandbox_type.cc b/chromium/sandbox/policy/sandbox_type.cc index 21b207ca323..a0bdc152f9f 100644 --- a/chromium/sandbox/policy/sandbox_type.cc +++ b/chromium/sandbox/policy/sandbox_type.cc @@ -59,10 +59,10 @@ bool IsUnsandboxedSandboxType(SandboxType sandbox_type) { #if BUILDFLAG(IS_CHROMEOS_ASH) case SandboxType::kIme: case SandboxType::kTts: -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case SandboxType::kLibassistant: -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) -#endif +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) +#endif // // BUILDFLAG(IS_CHROMEOS_ASH) #if !defined(OS_MAC) case SandboxType::kService: #endif @@ -130,9 +130,9 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, #if BUILDFLAG(IS_CHROMEOS_ASH) case SandboxType::kIme: case SandboxType::kTts: -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case SandboxType::kLibassistant: -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if defined(OS_MAC) case SandboxType::kMirroring: @@ -271,10 +271,10 @@ std::string StringFromUtilitySandboxType(SandboxType sandbox_type) { return switches::kImeSandbox; case SandboxType::kTts: return switches::kTtsSandbox; -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case SandboxType::kLibassistant: return switches::kLibassistantSandbox; -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) // The following are not utility processes so should not occur. case SandboxType::kRenderer: @@ -294,6 +294,15 @@ std::string StringFromUtilitySandboxType(SandboxType sandbox_type) { } SandboxType UtilitySandboxTypeFromString(const std::string& sandbox_string) { + // This function should cover all sandbox types used for utilities, the + // CHECK at the end should catch any attempts to forget to add a new type. + + // Most utilities are kUtility or kService so put those first. + if (sandbox_string == switches::kUtilitySandbox) + return SandboxType::kUtility; + if (sandbox_string == switches::kServiceSandbox) + return SandboxType::kService; + if (sandbox_string == switches::kNoneSandbox) return SandboxType::kNoSandbox; if (sandbox_string == switches::kNoneSandboxAndElevatedPrivileges) { @@ -342,11 +351,15 @@ SandboxType UtilitySandboxTypeFromString(const std::string& sandbox_string) { return SandboxType::kIme; if (sandbox_string == switches::kTtsSandbox) return SandboxType::kTts; -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) if (sandbox_string == switches::kLibassistantSandbox) return SandboxType::kLibassistant; -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) + CHECK(false) + << "Command line does not provide a valid sandbox configuration: " + << sandbox_string; + NOTREACHED(); return SandboxType::kUtility; } diff --git a/chromium/sandbox/policy/sandbox_type.h b/chromium/sandbox/policy/sandbox_type.h index 88903f72a3b..8b841e48e62 100644 --- a/chromium/sandbox/policy/sandbox_type.h +++ b/chromium/sandbox/policy/sandbox_type.h @@ -12,6 +12,7 @@ #include "build/chromeos_buildflags.h" #include "printing/buildflags/buildflags.h" #include "sandbox/policy/export.h" +#include "sandbox/policy/mojom/sandbox.mojom.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "chromeos/assistant/buildflags.h" @@ -98,9 +99,9 @@ enum class SandboxType { // Text-to-speech. kTts, -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) kLibassistant, -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) @@ -119,6 +120,42 @@ enum class SandboxType { kMaxValue = kVideoCapture }; +inline constexpr sandbox::policy::SandboxType MapToSandboxType( + sandbox::mojom::Sandbox mojo_sandbox) { + switch (mojo_sandbox) { + case sandbox::mojom::Sandbox::kCdm: + return sandbox::policy::SandboxType::kCdm; + case sandbox::mojom::Sandbox::kPrintCompositor: + return sandbox::policy::SandboxType::kPrintCompositor; + case sandbox::mojom::Sandbox::kService: + return sandbox::policy::SandboxType::kService; + case sandbox::mojom::Sandbox::kUtility: + return sandbox::policy::SandboxType::kUtility; + case sandbox::mojom::Sandbox::kVideoCapture: + return sandbox::policy::SandboxType::kVideoCapture; +#if defined(OS_WIN) + case sandbox::mojom::Sandbox::kMediaFoundationCdm: + return sandbox::policy::SandboxType::kMediaFoundationCdm; + case sandbox::mojom::Sandbox::kXrCompositing: + return sandbox::policy::SandboxType::kXrCompositing; +#endif // OS_WIN +#if BUILDFLAG(IS_CHROMEOS_ASH) + case sandbox::mojom::Sandbox::kIme: + return sandbox::policy::SandboxType::kIme; + case sandbox::mojom::Sandbox::kTts: + return sandbox::policy::SandboxType::kTts; + case sandbox::mojom::Sandbox::kLibassistant: +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) + return sandbox::policy::SandboxType::kLibassistant; +#else + CHECK(false) << "Libassistant sandbox not supported"; + NOTREACHED(); + return sandbox::policy::SandboxType::kService; +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + } +} + SANDBOX_POLICY_EXPORT bool IsUnsandboxedSandboxType(SandboxType sandbox_type); SANDBOX_POLICY_EXPORT void SetCommandLineFlagsForSandboxType( diff --git a/chromium/sandbox/policy/sandbox_type_unittest.cc b/chromium/sandbox/policy/sandbox_type_unittest.cc index e909a684c79..85b3131a19b 100644 --- a/chromium/sandbox/policy/sandbox_type_unittest.cc +++ b/chromium/sandbox/policy/sandbox_type_unittest.cc @@ -54,7 +54,6 @@ TEST(SandboxTypeTest, Utility) { base::CommandLine command_line(base::CommandLine::NO_PROGRAM); command_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess); - EXPECT_EQ(SandboxType::kUtility, SandboxTypeFromCommandLine(command_line)); base::CommandLine command_line2(command_line); SetCommandLineFlagsForSandboxType(&command_line2, SandboxType::kNetwork); @@ -73,8 +72,8 @@ TEST(SandboxTypeTest, Utility) { EXPECT_EQ(SandboxType::kPpapi, SandboxTypeFromCommandLine(command_line5)); base::CommandLine command_line6(command_line); - command_line6.AppendSwitchASCII(switches::kServiceSandboxType, "bogus"); - EXPECT_EQ(SandboxType::kUtility, SandboxTypeFromCommandLine(command_line6)); + SetCommandLineFlagsForSandboxType(&command_line6, SandboxType::kService); + EXPECT_EQ(SandboxType::kService, SandboxTypeFromCommandLine(command_line6)); base::CommandLine command_line7(command_line); SetCommandLineFlagsForSandboxType(&command_line7, @@ -130,6 +129,23 @@ TEST(SandboxTypeTest, Utility) { EXPECT_EQ(SandboxType::kNoSandbox, SandboxTypeFromCommandLine(command_line)); } +TEST(SandboxTypeTest, UtilityDeath) { + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + command_line.AppendSwitchASCII(switches::kProcessType, + switches::kUtilityProcess); + + EXPECT_DEATH_IF_SUPPORTED(SandboxTypeFromCommandLine(command_line), ""); + + // kGPU not valid for utility processes. + base::CommandLine command_line1(command_line); + command_line1.AppendSwitchASCII(switches::kServiceSandboxType, "gpu"); + EXPECT_DEATH_IF_SUPPORTED(SandboxTypeFromCommandLine(command_line1), ""); + + base::CommandLine command_line2(command_line); + command_line2.AppendSwitchASCII(switches::kServiceSandboxType, "bogus"); + EXPECT_DEATH_IF_SUPPORTED(SandboxTypeFromCommandLine(command_line2), ""); +} + TEST(SandboxTypeTest, GPU) { base::CommandLine command_line(base::CommandLine::NO_PROGRAM); command_line.AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess); diff --git a/chromium/sandbox/policy/switches.cc b/chromium/sandbox/policy/switches.cc index c0438f62d61..9ab64bce09c 100644 --- a/chromium/sandbox/policy/switches.cc +++ b/chromium/sandbox/policy/switches.cc @@ -53,9 +53,9 @@ const char kMirroringSandbox[] = "mirroring"; #if BUILDFLAG(IS_CHROMEOS_ASH) const char kImeSandbox[] = "ime"; const char kTtsSandbox[] = "tts"; -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) const char kLibassistantSandbox[] = "libassistant"; -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) // Flags owned by the service manager sandbox. diff --git a/chromium/sandbox/policy/switches.h b/chromium/sandbox/policy/switches.h index a0315cd1ace..1f7dc94681b 100644 --- a/chromium/sandbox/policy/switches.h +++ b/chromium/sandbox/policy/switches.h @@ -54,9 +54,9 @@ SANDBOX_POLICY_EXPORT extern const char kMirroringSandbox[]; #if BUILDFLAG(IS_CHROMEOS_ASH) SANDBOX_POLICY_EXPORT extern const char kImeSandbox[]; SANDBOX_POLICY_EXPORT extern const char kTtsSandbox[]; -#if BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) SANDBOX_POLICY_EXPORT extern const char kLibassistantSandbox[]; -#endif // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX) +#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) // Flags owned by the service manager sandbox. diff --git a/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc b/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc index b2988859fbb..7f257c7c90a 100644 --- a/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc +++ b/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc @@ -11,12 +11,10 @@ namespace media { TEST(SandboxTypeTest, Utility) { - // Setup to have '--type=utility' first. + // Setup to have '--type=utility' first (but no valid sandbox). base::CommandLine command_line(base::CommandLine::NO_PROGRAM); command_line.AppendSwitchASCII(sandbox::policy::switches::kProcessType, sandbox::policy::switches::kUtilityProcess); - EXPECT_EQ(sandbox::policy::SandboxType::kUtility, - sandbox::policy::SandboxTypeFromCommandLine(command_line)); base::CommandLine command_line2(command_line); SetCommandLineFlagsForSandboxType( diff --git a/chromium/sandbox/policy/win/sandbox_diagnostics.h b/chromium/sandbox/policy/win/sandbox_diagnostics.h index 68b01a91699..5e74030f4b5 100644 --- a/chromium/sandbox/policy/win/sandbox_diagnostics.h +++ b/chromium/sandbox/policy/win/sandbox_diagnostics.h @@ -23,9 +23,10 @@ namespace sandbox { namespace policy { // Mediates response from BrokerServices->GetPolicyDiagnostics. -class ServiceManagerDiagnosticsReceiver : public PolicyDiagnosticsReceiver { +class ServiceManagerDiagnosticsReceiver final + : public PolicyDiagnosticsReceiver { public: - ~ServiceManagerDiagnosticsReceiver() final; + ~ServiceManagerDiagnosticsReceiver() override; ServiceManagerDiagnosticsReceiver( scoped_refptr<base::SequencedTaskRunner> origin_task_runner, base::OnceCallback<void(base::Value)> response); diff --git a/chromium/sandbox/policy/win/sandbox_win.cc b/chromium/sandbox/policy/win/sandbox_win.cc index 3b6543c4d49..f9981846a1d 100644 --- a/chromium/sandbox/policy/win/sandbox_win.cc +++ b/chromium/sandbox/policy/win/sandbox_win.cc @@ -11,6 +11,7 @@ #include <vector> #include "base/command_line.h" +#include "base/cxx17_backports.h" #include "base/debug/activity_tracker.h" #include "base/feature_list.h" #include "base/files/file_path.h" @@ -24,7 +25,6 @@ #include "base/no_destructor.h" #include "base/path_service.h" #include "base/process/launch.h" -#include "base/stl_util.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -155,7 +155,7 @@ const base::Feature kEnableCsrssLockdownFeature{ // chrome.exe and chrome.dll. class PolicyTraceHelper : public base::trace_event::ConvertableToTraceFormat { public: - PolicyTraceHelper(TargetPolicy* policy) { + explicit PolicyTraceHelper(TargetPolicy* policy) { // |info| must live until JsonString() output is copied. std::unique_ptr<PolicyInfo> info = policy->GetPolicyInfo(); json_string_ = std::string(info->JsonString()); @@ -660,7 +660,7 @@ ResultCode SetupAppContainerProfile(AppContainer* container, return SBOX_ERROR_UNSUPPORTED; DCHECK(sandbox_type != SandboxType::kNetwork || - base::FeatureList::IsEnabled(features::kNetworkServiceSandboxLPAC)); + sandbox::policy::features::IsNetworkServiceSandboxLPACEnabled()); if (sandbox_type == SandboxType::kGpu && !container->AddImpersonationCapability(L"chromeInstallFiles")) { @@ -704,7 +704,8 @@ ResultCode SetupAppContainerProfile(AppContainer* container, !container->AddCapability(L"lpacAppExperience") || !container->AddCapability(L"lpacInstrumentation") || !container->AddCapability(L"lpacCryptoServices") || - !container->AddCapability(L"lpacEnterprisePolicyChangeNotifications")) { + !container->AddCapability(L"lpacEnterprisePolicyChangeNotifications") || + !container->AddCapability(L"mediaFoundationCdmFiles")) { DLOG(ERROR) << "AppContainer::AddCapability() - " << "SandboxType::kMediaFoundationCdm lpac capabilities failed"; @@ -923,7 +924,7 @@ bool SandboxWin::IsAppContainerEnabledForSandbox( return base::FeatureList::IsEnabled(features::kGpuAppContainer); if (sandbox_type == SandboxType::kNetwork) - return base::FeatureList::IsEnabled(features::kNetworkServiceSandboxLPAC); + return sandbox::policy::features::IsNetworkServiceSandboxLPACEnabled(); return false; } diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn index 2dd5ef90d4b..8fb6c13c8de 100644 --- a/chromium/sandbox/win/BUILD.gn +++ b/chromium/sandbox/win/BUILD.gn @@ -94,25 +94,21 @@ static_library("sandbox") { "src/restricted_token_utils.cc", "src/restricted_token_utils.h", "src/sandbox.cc", - "src/sandbox.h", "src/sandbox_factory.h", "src/sandbox_globals.cc", "src/sandbox_nt_types.h", "src/sandbox_nt_util.cc", "src/sandbox_nt_util.h", - "src/sandbox_policy.h", "src/sandbox_policy_base.cc", "src/sandbox_policy_base.h", "src/sandbox_policy_diagnostic.cc", "src/sandbox_policy_diagnostic.h", "src/sandbox_rand.cc", "src/sandbox_rand.h", - "src/sandbox_types.h", "src/sandbox_utils.cc", "src/sandbox_utils.h", "src/security_capabilities.cc", "src/security_capabilities.h", - "src/security_level.h", "src/service_resolver.cc", "src/service_resolver.h", "src/sharedmem_ipc_client.cc", @@ -127,6 +123,8 @@ static_library("sandbox") { "src/signed_interception.h", "src/signed_policy.cc", "src/signed_policy.h", + "src/socket_dispatcher.cc", + "src/socket_dispatcher.h", "src/startup_information_helper.cc", "src/startup_information_helper.h", "src/sync_dispatcher.cc", @@ -182,18 +180,16 @@ static_library("sandbox") { configs += [ "//build/config:precompiled_headers" ] - public_deps = [ "//base" ] + public_deps = [ + ":common", + "//base", + ] deps = [ + ":maybe_set_appcontainer_acls", "//base:base_static", "//sandbox:common", ] - - # NACL on 32-bit builds this target twice, once for 64-bit and once for 32-bit - # so avoid this dep from running twice with the same output in that case. - if (current_cpu == target_cpu) { - deps += [ ":set_appcontainer_acls" ] - } } test("sbox_integration_tests") { @@ -221,7 +217,6 @@ test("sbox_integration_tests") { "src/sync_policy_test.h", "src/unload_dll_test.cc", "tests/common/controller.cc", - "tests/common/controller.h", "tests/common/test_utils.cc", "tests/common/test_utils.h", "tests/integration_tests/cfi_unittest.cc", @@ -244,7 +239,10 @@ test("sbox_integration_tests") { ":sbox_integration_test_win_proc", ] - libs = [ "ktmw32.lib" ] + libs = [ + "ktmw32.lib", + "iphlpapi.lib", + ] } shared_library("sbox_integration_test_hijack_dll") { @@ -253,56 +251,64 @@ shared_library("sbox_integration_test_hijack_dll") { "tests/integration_tests/hijack_dll.def", ] - deps = [ ":set_appcontainer_acls" ] + deps = [ ":maybe_set_appcontainer_acls" ] +} + +group("maybe_set_appcontainer_acls") { + # NACL on 32-bit builds this target twice, once for 64-bit and once for 32-bit + # so avoid this dep from running twice with the same output in that case. + # Additionally, exclude setting ACLs when build is not happening on Windows. + if (current_cpu == target_cpu && host_os == "win") { + deps = [ ":set_appcontainer_acls" ] + } } -action("set_appcontainer_acls") { - script = "//build/win/set_appcontainer_acls.py" - stamp_file = "$target_out_dir/acls.stamp" - inputs = [ script ] - outputs = [ stamp_file ] +if (current_cpu == target_cpu && host_os == "win") { + action("set_appcontainer_acls") { + script = "//build/win/set_appcontainer_acls.py" + stamp_file = "$target_out_dir/acls.stamp" + inputs = [ script ] + outputs = [ stamp_file ] - args = [ - "--stamp=" + rebase_path(stamp_file, root_out_dir), - "--dir=" + rebase_path(root_out_dir, root_out_dir), - ] + args = [ + "--stamp=" + rebase_path(stamp_file, root_out_dir), + "--dir=" + rebase_path(root_out_dir, root_out_dir), + ] + } } -loadable_module("sbox_integration_test_hijack_shim_dll") { +shared_library("sbox_integration_test_hijack_shim_dll") { sources = [ "tests/integration_tests/hijack_shim_dll.cc", "tests/integration_tests/hijack_shim_dll.def", - "tests/integration_tests/hijack_shim_dll.h", ] # Implicitly linking hijack_dll as loader import resolution required. deps = [ + ":common", ":sbox_integration_test_hijack_dll", "//base", ] } -loadable_module("sbox_integration_test_hooking_dll") { - sources = [ - "tests/integration_tests/hooking_dll.cc", - "tests/integration_tests/hooking_dll.h", - ] +shared_library("sbox_integration_test_hooking_dll") { + sources = [ "tests/integration_tests/hooking_dll.cc" ] + + deps = [ ":common_test" ] } executable("sbox_integration_test_win_proc") { - sources = [ - "tests/integration_tests/hooking_win_proc.cc", - "tests/integration_tests/hooking_win_proc.h", - ] + sources = [ "tests/integration_tests/hooking_win_proc.cc" ] configs -= [ "//build/config/win:console" ] configs += [ "//build/config/win:windowed" ] + + deps = [ ":common_test" ] } test("sbox_validation_tests") { sources = [ "tests/common/controller.cc", - "tests/common/controller.h", "tests/validation_tests/commands.cc", "tests/validation_tests/commands.h", "tests/validation_tests/suite.cc", @@ -382,18 +388,39 @@ shared_library("pocdll") { ] defines = [ "POCDLL_EXPORTS" ] + + deps = [ "//base" ] } # This fuzzer will only work on Windows, add fuzz targets which could run on # Linux to //sandbox/ directly. fuzzer_test("sandbox_policy_rule_fuzzer") { - sources = [ - "fuzzer/fuzzer_types.h", - "fuzzer/sandbox_policy_rule_fuzzer.cc", - ] + sources = [ "fuzzer/sandbox_policy_rule_fuzzer.cc" ] dict = "fuzzer/sandbox_policy_rule.dict" deps = [ "//base", "//sandbox", ] } + +source_set("common") { + sources = [ + "fuzzer/fuzzer_types.h", + "src/sandbox.h", + "src/sandbox_policy.h", + "src/sandbox_types.h", + "src/security_level.h", + "tests/common/controller.h", + "tests/integration_tests/hijack_shim_dll.h", + ] + + deps = [ "//base" ] + public_deps = [ ":common_test" ] +} + +source_set("common_test") { + sources = [ + "tests/integration_tests/hooking_dll.h", + "tests/integration_tests/hooking_win_proc.h", + ] +} diff --git a/chromium/sandbox/win/sandbox_poc/main_ui_window.cc b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc index b5ebe760057..2b168ed249d 100644 --- a/chromium/sandbox/win/sandbox_poc/main_ui_window.cc +++ b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc @@ -645,8 +645,7 @@ void MainUIWindow::InsertLineInListView(wchar_t* debug_message) { struct tm time = {0}; localtime_s(&time, &time_temp); - size_t return_code; - return_code = wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time); + wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time); wcscat_s(message_time, size_message_with_time, debug_message); diff --git a/chromium/sandbox/win/src/app_container_base.cc b/chromium/sandbox/win/src/app_container_base.cc index ca99779109b..9cb27f06385 100644 --- a/chromium/sandbox/win/src/app_container_base.cc +++ b/chromium/sandbox/win/src/app_container_base.cc @@ -67,7 +67,7 @@ bool GetGenericMappingForType(SE_OBJECT_TYPE object_type, class ScopedImpersonation { public: - ScopedImpersonation(const base::win::ScopedHandle& token) { + explicit ScopedImpersonation(const base::win::ScopedHandle& token) { BOOL result = ::ImpersonateLoggedOnUser(token.Get()); DCHECK(result); } diff --git a/chromium/sandbox/win/src/app_container_test.cc b/chromium/sandbox/win/src/app_container_test.cc index 3e231971cd3..29d83ed42a9 100644 --- a/chromium/sandbox/win/src/app_container_test.cc +++ b/chromium/sandbox/win/src/app_container_test.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include <windows.h> +#include <winsock2.h> + +#include <iphlpapi.h> #include <sddl.h> @@ -14,15 +17,20 @@ #include "base/files/file_path.h" #include "base/hash/sha1.h" #include "base/logging.h" +#include "base/process/process_info.h" #include "base/rand_util.h" #include "base/scoped_native_library.h" #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/thread_pool.h" +#include "base/test/task_environment.h" +#include "base/test/test_timeouts.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" #include "base/win/windows_version.h" #include "sandbox/win/src/app_container_base.h" +#include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/sync_policy_test.h" #include "sandbox/win/src/win_utils.h" #include "sandbox/win/tests/common/controller.h" @@ -37,6 +45,17 @@ const wchar_t kAppContainerSid[] = L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-" L"924012148-2839372144"; +// Some tests depend on a timeout happening (e.g. to detect if firewall blocks a +// TCP/UDP connection from App Container). However, if process startup time is +// too slow (which can happen on slower bots with a high degree of test +// concurrency) then tiny_timeout is not long enough, so a slightly longer +// timeout is used here to avoid having to retry flaky tests. +DWORD test_timeout() { + const static DWORD kMillisTimeout = + TestTimeouts::tiny_timeout().InMilliseconds() * 2; + return kMillisTimeout; +} + std::wstring GenerateRandomPackageName() { return base::StringPrintf(L"%016lX%016lX", base::RandUint64(), base::RandUint64()); @@ -200,6 +219,69 @@ ResultCode AddNetworkAppContainerPolicy(TargetPolicy* policy) { return SBOX_ALL_OK; } +void InitWinsock() { + WORD winsock_ver = MAKEWORD(2, 2); + WSAData wsa_data; + WSAStartup(winsock_ver, &wsa_data); +} + +class WSAEventHandleTraits { + public: + typedef HANDLE Handle; + + WSAEventHandleTraits() = delete; + WSAEventHandleTraits(const WSAEventHandleTraits&) = delete; + WSAEventHandleTraits& operator=(const WSAEventHandleTraits&) = delete; + + static bool CloseHandle(HANDLE handle) { + return ::WSACloseEvent(handle) == TRUE; + } + static bool IsHandleValid(HANDLE handle) { + return handle != INVALID_HANDLE_VALUE; + } + static HANDLE NullHandle() { return INVALID_HANDLE_VALUE; } +}; + +typedef base::win::GenericScopedHandle<WSAEventHandleTraits, + base::win::DummyVerifierTraits> + ScopedWSAEventHandle; + +class SocketHandleTraits { + public: + typedef SOCKET Handle; + + SocketHandleTraits() = delete; + SocketHandleTraits(const SocketHandleTraits&) = delete; + SocketHandleTraits& operator=(const SocketHandleTraits&) = delete; + + static bool CloseHandle(SOCKET handle) { return ::closesocket(handle) == 0; } + static bool IsHandleValid(SOCKET handle) { return handle != INVALID_SOCKET; } + static SOCKET NullHandle() { return INVALID_SOCKET; } +}; + +class DummySocketVerifierTraits { + public: + using Handle = SOCKET; + + DummySocketVerifierTraits() = delete; + DummySocketVerifierTraits(const DummySocketVerifierTraits&) = delete; + DummySocketVerifierTraits& operator=(const DummySocketVerifierTraits&) = + delete; + + static void StartTracking(SOCKET handle, + const void* owner, + const void* pc1, + const void* pc2) {} + static void StopTracking(SOCKET handle, + const void* owner, + const void* pc1, + const void* pc2) {} +}; + +typedef base::win::GenericScopedHandle<SocketHandleTraits, + DummySocketVerifierTraits> + ScopedSocketHandle; + class AppContainerTest : public ::testing::Test { public: void SetUp() override { @@ -247,6 +329,124 @@ class AppContainerTest : public ::testing::Test { base::win::ScopedProcessInformation scoped_process_info_; }; +// A Very Simple UDP test server. +// Binds to all interfaces on a random port, and then waits to receive some +// data, then sends it back to the peer. +class UDPEchoServer { + public: + UDPEchoServer() = default; + ~UDPEchoServer(); + + // Start server. + bool Start(); + // Get the listening port. Must be called after Start(). + int GetPort(); + + private: + void RecvTask(); + ScopedSocketHandle socket_; + int port_; + base::test::TaskEnvironment environment_; +}; + +UDPEchoServer::~UDPEchoServer() { + // Make sure to drain threads before destructing. + environment_.RunUntilIdle(); +} + +bool UDPEchoServer::Start() { + SOCKET s = ::WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nullptr, 0, + WSA_FLAG_OVERLAPPED); + if (s == INVALID_SOCKET) + return false; + socket_ = ScopedSocketHandle(s); + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + // Pick a random port. + server.sin_port = 0; + + int ret = + bind(socket_.Get(), reinterpret_cast<sockaddr*>(&server), sizeof(server)); + if (ret == SOCKET_ERROR) + return false; + + struct sockaddr_in bound_server; + int bound_server_len = sizeof(bound_server); + ret = getsockname(socket_.Get(), reinterpret_cast<sockaddr*>(&bound_server), + &bound_server_len); + if (ret == SOCKET_ERROR) + return false; + port_ = ntohs(bound_server.sin_port); + return base::ThreadPool::PostTask( + FROM_HERE, + base::BindOnce(&UDPEchoServer::RecvTask, base::Unretained(this))); +} + +void UDPEchoServer::RecvTask() { + char buf[1024]; + WSABUF wsa_buf; + wsa_buf.buf = buf; + wsa_buf.len = sizeof(buf); + struct sockaddr_in other; + int other_len = sizeof(other); + + HANDLE recv_event_handle = WSACreateEvent(); + ASSERT_NE(WSA_INVALID_EVENT, recv_event_handle); + ScopedWSAEventHandle recv_event(recv_event_handle); + + OVERLAPPED read_overlapped = {}; + read_overlapped.hEvent = recv_event.Get(); + DWORD flags = 0; + int ret = WSARecvFrom(socket_.Get(), &wsa_buf, 1, nullptr, &flags, + reinterpret_cast<sockaddr*>(&other), &other_len, + &read_overlapped, nullptr); + // Return value of 0 means operation completed immediately which should never + // happen. SOCKET_ERROR returned means operation is pending. + ASSERT_EQ(SOCKET_ERROR, ret); + // Operation should be pending. + ASSERT_EQ(WSA_IO_PENDING, ::WSAGetLastError()); + + // Wait to receive data from the child process. Only wait 1 second. + DWORD wait = WaitForSingleObject(recv_event.Get(), test_timeout()); + + if (wait != WAIT_OBJECT_0) + return; // No connections. Expected for certain types of tests. + + DWORD num_bytes_recv = 0; + flags = 0; + BOOL overlapped_result = WSAGetOverlappedResult( + socket_.Get(), &read_overlapped, &num_bytes_recv, FALSE, &flags); + ASSERT_TRUE(overlapped_result); + + // Now reply. + HANDLE send_event_handle = WSACreateEvent(); + ASSERT_NE(WSA_INVALID_EVENT, send_event_handle); + ScopedWSAEventHandle send_event(send_event_handle); + + OVERLAPPED send_overlapped = {}; + read_overlapped.hEvent = send_event.Get(); + + ret = WSASendTo(socket_.Get(), &wsa_buf, 1, nullptr, 0, + reinterpret_cast<sockaddr*>(&other), other_len, + &send_overlapped, nullptr); + // Return value of 0 means operation completed immediately, which means data + // was successfully sent to the peer. + if (ret == 0) + return; + // If not, the operation should be pending. + ASSERT_EQ(WSA_IO_PENDING, ::WSAGetLastError()); + // Wait for send. Only wait 1 second. + wait = WaitForSingleObject(send_event.Get(), test_timeout()); + // Send should always succeed in a timely manner. + EXPECT_EQ(wait, WAIT_OBJECT_0); +} + +int UDPEchoServer::GetPort() { + return port_; +} + } // namespace TEST_F(AppContainerTest, DenyOpenEventForLowBox) { @@ -411,6 +611,192 @@ SBOX_TESTS_COMMAND int LoadDLL(int argc, wchar_t** argv) { return SBOX_TEST_FAILED; } +SBOX_TESTS_COMMAND int CheckIsAppContainer(int argc, wchar_t** argv) { + if (base::IsCurrentProcessInAppContainer()) + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_FAILED; +} + +// Attempts to create a TCP socket, connect to specified address on a specified +// port. +// +// First parameter should contain the host. Second parameter should contain the +// port to connect to. Third parameter indicates whether the socket should be +// brokered (1) or created in-process (0). +// +// SBOX_TEST_FAILED_TO_RUN_TEST - Test failed to run e.g. invalid parameters. +// +// SBOX_TEST_FIRST_ERROR - Could not create socket from call to WSASocket or +// socket broker operation. +// +// SBOX_TEST_SECOND_ERROR - Could not call successfully perform a non-blocking +// TCP connect(). +// +// SBOX_TEST_TIMED_OUT - The connect timed out. This might be the correct result +// for certain types of tests e.g. when App Container is blocking either a TCP +// connect. +SBOX_TESTS_COMMAND int Socket_CreateTCP(int argc, wchar_t** argv) { + InitWinsock(); + SOCKET socket_handle = INVALID_SOCKET; + + if (argc < 3) + return SBOX_TEST_FAILED_TO_RUN_TEST; + + if (::_wtoi(argv[2]) == 1) { + TargetServices* target_services = SandboxFactory::GetTargetServices(); + if (!target_services) + return SBOX_TEST_FAILED_TO_RUN_TEST; + socket_handle = target_services->CreateBrokeredSocket(AF_INET, SOCK_STREAM, + IPPROTO_TCP); + } else { + socket_handle = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, + WSA_FLAG_OVERLAPPED); + } + + if (socket_handle == INVALID_SOCKET) + return SBOX_TEST_FIRST_ERROR; + + ScopedSocketHandle socket(socket_handle); + + sockaddr_in local_service = {}; + local_service.sin_family = AF_INET; + std::string hostname = base::WideToUTF8(argv[0]); + local_service.sin_addr.s_addr = inet_addr(hostname.c_str()); + local_service.sin_port = htons(::_wtoi(argv[1])); + + HANDLE connect_event_handle = WSACreateEvent(); + if (connect_event_handle == WSA_INVALID_EVENT) + return SBOX_TEST_FAILED_TO_RUN_TEST; + ScopedWSAEventHandle connect_event(connect_event_handle); + // For TCP sockets, wait on the connect. + int event_select = + WSAEventSelect(socket.Get(), connect_event.Get(), FD_CONNECT); + + if (event_select) + return SBOX_TEST_FAILED_TO_RUN_TEST; + int ret = ::connect(socket.Get(), reinterpret_cast<sockaddr*>(&local_service), + sizeof(local_service)); + if (ret != SOCKET_ERROR || WSAGetLastError() != WSAEWOULDBLOCK) { + return SBOX_TEST_SECOND_ERROR; + } + // Non-blocking socket, always returns SOCKET_ERROR and sets WSAlastError to + // WSAEWOULDBLOCK. + // Wait for the connect to succeed. + DWORD wait = WaitForSingleObject(connect_event.Get(), test_timeout()); + + if (wait != WAIT_OBJECT_0) + return SBOX_TEST_TIMED_OUT; + + return SBOX_TEST_SUCCEEDED; +} + +// Attempts to create a UDP socket, connect to specified address on a specified +// port, and transmit/receive data. +// +// First parameter should contain the host. Second parameter should contain the +// port to connect to. Third parameter indicates whether the socket should be +// brokered (1) or created in-process (0). +// +// Returns: +// +// SBOX_TEST_FAILED_TO_RUN_TEST - Test failed to run e.g. invalid parameters. +// +// SBOX_TEST_FIRST_ERROR - Could not create socket from call to WSASocket or +// socket broker operation. +// +// SBOX_TEST_THIRD_ERROR - Could not successfully perform a non-blocking UDP +// sendto(). +// +// SBOX_TEST_FOURTH_ERROR - Could not successfully perform a non-blocking UDP +// recv(). +// +// SBOX_TEST_TIMED_OUT - One of the above operations (connect, sendto, recv) +// timed out. This might be the correct result for certain types of tests e.g. +// when App Container is blocking either a TCP connect or UDP recv. +SBOX_TESTS_COMMAND int Socket_CreateUDP(int argc, wchar_t** argv) { + InitWinsock(); + SOCKET socket_handle = INVALID_SOCKET; + + if (argc < 3) + return SBOX_TEST_FAILED_TO_RUN_TEST; + + if (::_wtoi(argv[2]) == 1) { + TargetServices* target_services = SandboxFactory::GetTargetServices(); + if (!target_services) + return SBOX_TEST_FAILED_TO_RUN_TEST; + socket_handle = + target_services->CreateBrokeredSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + } else { + socket_handle = ::WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nullptr, 0, + WSA_FLAG_OVERLAPPED); + } + + if (socket_handle == INVALID_SOCKET) + return SBOX_TEST_FIRST_ERROR; + + ScopedSocketHandle socket(socket_handle); + sockaddr_in local_service = {}; + local_service.sin_family = AF_INET; + std::string hostname = base::WideToUTF8(argv[0]); + local_service.sin_addr.s_addr = inet_addr(hostname.c_str()); + local_service.sin_port = htons(::_wtoi(argv[1])); + + char data[] = "hello"; + + WSABUF write_buffer = {}; + write_buffer.buf = data; + write_buffer.len = sizeof(data); + HANDLE send_event_handle = WSACreateEvent(); + if (send_event_handle == WSA_INVALID_EVENT) + return SBOX_TEST_FAILED_TO_RUN_TEST; + ScopedWSAEventHandle send_event(send_event_handle); + OVERLAPPED write_overlapped = {}; + write_overlapped.hEvent = send_event.Get(); + int ret = ::WSASendTo(socket.Get(), &write_buffer, 1, nullptr, 0, + reinterpret_cast<sockaddr*>(&local_service), + sizeof(local_service), &write_overlapped, nullptr); + if (ret == 0) { + // Operation completed immediately! + } else { + // Winsock should return WSA_IO_PENDING and we wait on the event. + if (WSAGetLastError() != WSA_IO_PENDING) + return SBOX_TEST_THIRD_ERROR; + DWORD wait = WaitForSingleObject(send_event.Get(), test_timeout()); + + if (wait != WAIT_OBJECT_0) + return SBOX_TEST_TIMED_OUT; + } + + // Now try to read the response. + WSABUF read_buffer = {}; + char recv_buf[10] = {}; + read_buffer.buf = recv_buf; + read_buffer.len = sizeof(recv_buf); + HANDLE read_event_handle = WSACreateEvent(); + if (read_event_handle == WSA_INVALID_EVENT) + return SBOX_TEST_FAILED_TO_RUN_TEST; + ScopedWSAEventHandle read_event(read_event_handle); + OVERLAPPED read_overlapped = {}; + read_overlapped.hEvent = read_event.Get(); + DWORD flags = MSG_PARTIAL; + ret = ::WSARecv(socket.Get(), &read_buffer, 1, nullptr, &flags, + &read_overlapped, nullptr); + if (ret == 0) { + // Operation completed immediately! + } else { + // Winsock should return WSA_IO_PENDING and we wait on the event. + if (WSAGetLastError() != WSA_IO_PENDING) { + return SBOX_TEST_FOURTH_ERROR; + } + DWORD wait = WaitForSingleObject(read_event.Get(), test_timeout()); + + if (wait != WAIT_OBJECT_0) + return SBOX_TEST_TIMED_OUT; + } + + return SBOX_TEST_SUCCEEDED; +} + TEST(AppContainerLaunchTest, CheckLPACACE) { if (base::win::GetVersion() < base::win::Version::WIN10_RS1) return; @@ -422,4 +808,171 @@ TEST(AppContainerLaunchTest, CheckLPACACE) { AppContainerBase::Delete(GetAppContainerProfileName().c_str()); } +TEST(AppContainerLaunchTest, IsAppContainer) { + if (base::win::GetVersion() < base::win::Version::WIN10_RS1) + return; + TestRunner runner; + AddNetworkAppContainerPolicy(runner.GetPolicy()); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIsAppContainer")); + + AppContainerBase::Delete(GetAppContainerProfileName().c_str()); +} + +TEST(AppContainerLaunchTest, IsNotAppContainer) { + TestRunner runner; + + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"CheckIsAppContainer")); +} + +class SocketBrokerTest + : public ::testing::Test, + public ::testing::WithParamInterface< + ::testing::tuple</* using app container */ bool, + /* using socket brokering */ bool, + /* connect to real adapter */ bool, + /* add brokering rule */ bool>> { + public: + void SetUp() override { + InitWinsock(); + SetUpSandboxPolicy(); + } + + void TearDown() override { + if (IsTestInAppContainer()) + AppContainerBase::Delete(GetAppContainerProfileName().c_str()); + } + + protected: + bool IsTestInAppContainer() { return ::testing::get<0>(GetParam()); } + bool IsTestUsingBrokeredSockets() { return ::testing::get<1>(GetParam()); } + bool IsTestConnectingToRealAdapter() { return ::testing::get<2>(GetParam()); } + bool ShouldBrokerRuleBeAdded() { return ::testing::get<3>(GetParam()); } + SboxTestResult GetExpectedTestResult() { + if (!IsTestInAppContainer()) + return SBOX_TEST_SUCCEEDED; + + if (IsTestUsingBrokeredSockets()) { + if (ShouldBrokerRuleBeAdded()) + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_FIRST_ERROR; + } + + return SBOX_TEST_TIMED_OUT; + } + + std::wstring GetTestHostName() { + if (!IsTestConnectingToRealAdapter()) + return L"127.0.0.1"; + // Try and obtain a local network address. + // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses. + size_t buffer_size = 16384; + std::unique_ptr<char[]> adapter_info(new char[buffer_size]); + PIP_ADAPTER_ADDRESSES adapter_addrs = + reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info.get()); + ULONG flags = (GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST); + ULONG ret = 0; + do { + adapter_info.reset(new char[buffer_size]); + adapter_addrs = + reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info.get()); + ret = GetAdaptersAddresses(AF_INET, flags, 0, adapter_addrs, + reinterpret_cast<PULONG>(&buffer_size)); + } while (ret == ERROR_BUFFER_OVERFLOW); + if (ret != ERROR_SUCCESS) + return std::wstring(); + while (adapter_addrs) { + if (adapter_addrs->OperStatus == IfOperStatusUp) { + PIP_ADAPTER_UNICAST_ADDRESS address = + adapter_addrs->FirstUnicastAddress; + for (; address; address = address->Next) { + if (address->Address.lpSockaddr->sa_family != AF_INET) + continue; + sockaddr_in* ipv4_addr = + reinterpret_cast<sockaddr_in*>(address->Address.lpSockaddr); + return base::UTF8ToWide(inet_ntoa(ipv4_addr->sin_addr)); + } + } + adapter_addrs = adapter_addrs->Next; + } + return std::wstring(); + } + + void SetUpSandboxPolicy() { + if (IsTestInAppContainer()) { + AddNetworkAppContainerPolicy(runner_.GetPolicy()); + } else { + TargetPolicy* policy = runner_.GetPolicy(); + policy->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, USER_LIMITED); + } + if (ShouldBrokerRuleBeAdded()) { + TargetPolicy* policy = runner_.GetPolicy(); + policy->AddRule(TargetPolicy::SUBSYS_SOCKET, + TargetPolicy::SOCKET_ALLOW_BROKER, nullptr); + } + } + + protected: + TestRunner runner_; +}; + +TEST_P(SocketBrokerTest, SocketBrokerTestUDP) { + // App Container socket brokering only supported on Win10 RS1 and above. + if (base::win::GetVersion() < base::win::Version::WIN10_RS1) + return; + + UDPEchoServer server; + ASSERT_TRUE(server.Start()); + + std::wstring hostname = GetTestHostName(); + ASSERT_TRUE(!hostname.empty()); + EXPECT_EQ( + GetExpectedTestResult(), + runner_.RunTest(base::StringPrintf(L"Socket_CreateUDP %ls %d %d", + hostname.c_str(), server.GetPort(), + IsTestUsingBrokeredSockets() ? 1 : 0) + .c_str())); +} + +TEST_P(SocketBrokerTest, SocketBrokerTestTCP) { + // App Container socket brokering only supported on Win10 RS1 and above. + if (base::win::GetVersion() < base::win::Version::WIN10_RS1) + return; + + std::wstring hostname = GetTestHostName(); + ASSERT_TRUE(!hostname.empty()); + EXPECT_EQ( + GetExpectedTestResult(), + runner_.RunTest(base::StringPrintf(L"Socket_CreateTCP %ls 445 %d", + hostname.c_str(), + IsTestUsingBrokeredSockets() ? 1 : 0) + .c_str())); +} + +INSTANTIATE_TEST_CASE_P( + AppContainerBrokered, + SocketBrokerTest, + ::testing::Combine( + /* using app container */ ::testing::Values(true), + /* using socket brokering */ ::testing::Values(true), + /* connect to real adapter */ ::testing::Values(true, false), + /* add brokering rule */ ::testing::Values(true, false))); +INSTANTIATE_TEST_CASE_P( + AppContainerNonBrokered, + SocketBrokerTest, + ::testing::Combine( + /* using app container */ ::testing::Values(true), + /* using socket brokering */ ::testing::Values(false), + /* connect to real adapter */ ::testing::Values(true, false), + /* add brokering rule */ ::testing::Values(true, false))); +INSTANTIATE_TEST_CASE_P( + NoAppContainerNonBrokered, + SocketBrokerTest, + ::testing::Combine( + /* using app container */ ::testing::Values(false), + /* using socket brokering */ ::testing::Values(false), + /* connect to real adapter */ ::testing::Values(true, false), + /* add brokering rule */ ::testing::Values(true, false))); + } // namespace sandbox diff --git a/chromium/sandbox/win/src/app_container_unittest.cc b/chromium/sandbox/win/src/app_container_unittest.cc index 3f22a49b187..a57bd443c4d 100644 --- a/chromium/sandbox/win/src/app_container_unittest.cc +++ b/chromium/sandbox/win/src/app_container_unittest.cc @@ -103,7 +103,7 @@ std::wstring GenerateRandomPackageName() { class SECURITY_ATTRIBUTES_SDDL : public SECURITY_ATTRIBUTES { public: - SECURITY_ATTRIBUTES_SDDL(LPCWSTR sddl) : SECURITY_ATTRIBUTES() { + explicit SECURITY_ATTRIBUTES_SDDL(LPCWSTR sddl) : SECURITY_ATTRIBUTES() { nLength = sizeof(SECURITY_ATTRIBUTES); if (!::ConvertStringSecurityDescriptorToSecurityDescriptor( sddl, SDDL_REVISION_1, &lpSecurityDescriptor, nullptr)) { diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc index c61ad9410ed..68b40b5707c 100644 --- a/chromium/sandbox/win/src/broker_services.cc +++ b/chromium/sandbox/win/src/broker_services.cc @@ -10,7 +10,7 @@ #include <utility> -#include "base/check.h" +#include "base/check_op.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" @@ -168,11 +168,11 @@ DWORD WINAPI TargetEventsThread(PVOID param) { ::ResetEvent(params->no_targets); while (true) { - DWORD events = 0; + DWORD event = 0; ULONG_PTR key = 0; LPOVERLAPPED ovl = nullptr; - if (!::GetQueuedCompletionStatus(params->iocp, &events, &key, &ovl, + if (!::GetQueuedCompletionStatus(params->iocp, &event, &key, &ovl, INFINITE)) { // This call fails if the port has been closed before we have a // chance to service the last packet which is 'exit' anyway so @@ -185,17 +185,21 @@ DWORD WINAPI TargetEventsThread(PVOID param) { // that jobs can send and some of them depend on the job attributes set. JobTracker* tracker = reinterpret_cast<JobTracker*>(key); - // Processes may be added to a job after the process count has - // reached zero, leading us to manipulate a freed JobTracker - // object or job handle (as the key is no longer valid). We - // therefore check if the tracker has already been deleted. + // Processes may be added to a job after the process count has reached + // zero, leading us to manipulate a freed JobTracker object or job handle + // (as the key is no longer valid). We therefore check if the tracker has + // already been deleted. Note that Windows may emit notifications after + // 'job finished' (active process zero), so not every case is unexpected. if (std::find_if(jobs.begin(), jobs.end(), [&](auto&& p) -> bool { return p.get() == tracker; }) == jobs.end()) { - CHECK(false); + // CHECK if job already deleted. + CHECK_NE(static_cast<int>(event), JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO); + // Continue to next notification otherwise. + continue; } - switch (events) { + switch (event) { case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { // The job object has signaled that the last process associated // with it has terminated. It is safe to free the tracker diff --git a/chromium/sandbox/win/src/crosscall_client.h b/chromium/sandbox/win/src/crosscall_client.h index af13651ddce..dd2974c16aa 100644 --- a/chromium/sandbox/win/src/crosscall_client.h +++ b/chromium/sandbox/win/src/crosscall_client.h @@ -58,7 +58,7 @@ const uint32_t kIPCChannelSize = 1024; template <typename T> class CopyHelper { public: - CopyHelper(const T& t) : t_(t) {} + explicit CopyHelper(const T& t) : t_(t) {} // Returns the pointer to the start of the input. const void* GetStart() const { return &t_; } @@ -91,7 +91,7 @@ class CopyHelper { template <> class CopyHelper<void*> { public: - CopyHelper(void* t) : t_(t) {} + explicit CopyHelper(void* t) : t_(t) {} // Returns the pointer to the start of the input. const void* GetStart() const { return &t_; } @@ -121,7 +121,7 @@ class CopyHelper<void*> { template <> class CopyHelper<const wchar_t*> { public: - CopyHelper(const wchar_t* t) : t_(t) {} + explicit CopyHelper(const wchar_t* t) : t_(t) {} // Returns the pointer to the start of the string. const void* GetStart() const { return t_; } @@ -169,7 +169,7 @@ template <> class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> { public: typedef CopyHelper<const wchar_t*> Base; - CopyHelper(wchar_t* t) : Base(t) {} + explicit CopyHelper(wchar_t* t) : Base(t) {} const void* GetStart() const { return Base::GetStart(); } @@ -189,7 +189,7 @@ class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> { public: typedef const wchar_t array[n]; typedef CopyHelper<const wchar_t*> Base; - CopyHelper(array t) : Base(t) {} + explicit CopyHelper(array t) : Base(t) {} const void* GetStart() const { return Base::GetStart(); } @@ -216,7 +216,7 @@ class InOutCountedBuffer : public CountedBuffer { template <> class CopyHelper<InOutCountedBuffer> { public: - CopyHelper(const InOutCountedBuffer t) : t_(t) {} + explicit CopyHelper(const InOutCountedBuffer t) : t_(t) {} // Returns the pointer to the start of the string. const void* GetStart() const { return t_.Buffer(); } diff --git a/chromium/sandbox/win/src/file_policy_test.cc b/chromium/sandbox/win/src/file_policy_test.cc index 4989ab308fc..b061c049c89 100644 --- a/chromium/sandbox/win/src/file_policy_test.cc +++ b/chromium/sandbox/win/src/file_policy_test.cc @@ -89,14 +89,11 @@ SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t** argv) { if (INVALID_HANDLE_VALUE != file) { ::CloseHandle(file); return SBOX_TEST_SUCCEEDED; - } else { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED; - } } - return SBOX_TEST_SUCCEEDED; + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } + return SBOX_TEST_FAILED; } // Creates the file in parameter using the NtCreateFile api and returns if the diff --git a/chromium/sandbox/win/src/filesystem_policy.cc b/chromium/sandbox/win/src/filesystem_policy.cc index f8cd1647584..fe995b7be70 100644 --- a/chromium/sandbox/win/src/filesystem_policy.cc +++ b/chromium/sandbox/win/src/filesystem_policy.cc @@ -8,8 +8,8 @@ #include <string> +#include "base/cxx17_backports.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" #include "sandbox/win/src/ipc_tags.h" diff --git a/chromium/sandbox/win/src/interception.cc b/chromium/sandbox/win/src/interception.cc index 369beebac9f..b164a83dda9 100644 --- a/chromium/sandbox/win/src/interception.cc +++ b/chromium/sandbox/win/src/interception.cc @@ -13,6 +13,7 @@ #include <set> #include <string> +#include "base/bits.h" #include "base/check_op.h" #include "base/notreached.h" #include "base/scoped_native_library.h" @@ -378,7 +379,7 @@ ResultCode InterceptionManager::PatchNtdll(bool hot_patch_needed) { thunk_offset &= kPageSize - 1; // Make an aligned, padded allocation, and move the pointer to our chunk. - size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1); + size_t thunk_bytes_padded = base::bits::AlignUp(thunk_bytes, kPageSize); thunk_base = reinterpret_cast<BYTE*>( ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, MEM_COMMIT, PAGE_EXECUTE_READWRITE)); diff --git a/chromium/sandbox/win/src/ipc_leak_test.cc b/chromium/sandbox/win/src/ipc_leak_test.cc index 7abcc4cd3c4..e77cd94126b 100644 --- a/chromium/sandbox/win/src/ipc_leak_test.cc +++ b/chromium/sandbox/win/src/ipc_leak_test.cc @@ -8,8 +8,8 @@ #include <stdlib.h> +#include "base/cxx17_backports.h" #include "base/memory/page_size.h" -#include "base/stl_util.h" #include "base/win/win_util.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/filesystem_interception.h" diff --git a/chromium/sandbox/win/src/ipc_tags.h b/chromium/sandbox/win/src/ipc_tags.h index 91e06dbc36e..8603a2488c4 100644 --- a/chromium/sandbox/win/src/ipc_tags.h +++ b/chromium/sandbox/win/src/ipc_tags.h @@ -33,6 +33,7 @@ enum class IpcTag { USER_REGISTERCLASSW, CREATETHREAD, NTCREATESECTION, + WS2SOCKET, LAST }; diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.cc b/chromium/sandbox/win/src/policy_engine_opcodes.cc index 4bac801f9bc..223b848c3f5 100644 --- a/chromium/sandbox/win/src/policy_engine_opcodes.cc +++ b/chromium/sandbox/win/src/policy_engine_opcodes.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <stdint.h> +#include "base/check_op.h" #include "sandbox/win/src/sandbox_nt_types.h" #include "sandbox/win/src/sandbox_types.h" @@ -42,6 +43,11 @@ SANDBOX_INTERCEPT NtExports g_nt; // only the factory method and the evaluation function know the stored argument // order and meaning. +size_t OpcodeFactory::memory_size() const { + DCHECK_GE(memory_bottom_, memory_top_); + return memory_bottom_ - memory_top_; +} + template <int> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.h b/chromium/sandbox/win/src/policy_engine_opcodes.h index 88a703c1297..fe53376b8c4 100644 --- a/chromium/sandbox/win/src/policy_engine_opcodes.h +++ b/chromium/sandbox/win/src/policy_engine_opcodes.h @@ -8,7 +8,6 @@ #include <stddef.h> #include <stdint.h> -#include "base/check_op.h" #include "base/macros.h" #include "base/numerics/safe_conversions.h" #include "sandbox/win/src/policy_engine_params.h" @@ -283,10 +282,7 @@ class OpcodeFactory { } // Returns the available memory to make opcodes. - size_t memory_size() const { - DCHECK_GE(memory_bottom_, memory_top_); - return memory_bottom_ - memory_top_; - } + size_t memory_size() const; // Creates an OpAlwaysFalse opcode. PolicyOpcode* MakeOpAlwaysFalse(uint32_t options); diff --git a/chromium/sandbox/win/src/policy_engine_params.h b/chromium/sandbox/win/src/policy_engine_params.h index d7ffdc47df6..d7d4d5c6fe7 100644 --- a/chromium/sandbox/win/src/policy_engine_params.h +++ b/chromium/sandbox/win/src/policy_engine_params.h @@ -118,43 +118,49 @@ class ParameterSet { template <typename T> class ParameterSetEx : public ParameterSet { public: - ParameterSetEx(const void* address); + explicit ParameterSetEx(const void* address); }; template <> class ParameterSetEx<void const*> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(VOIDPTR_TYPE, address) {} }; template <> class ParameterSetEx<void*> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(VOIDPTR_TYPE, address) {} }; template <> class ParameterSetEx<wchar_t*> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(WCHAR_TYPE, address) {} }; template <> class ParameterSetEx<wchar_t const*> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(WCHAR_TYPE, address) {} }; template <> class ParameterSetEx<uint32_t> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(UINT32_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(UINT32_TYPE, address) {} }; template <> class ParameterSetEx<UNICODE_STRING> : public ParameterSet { public: - ParameterSetEx(const void* address) : ParameterSet(UNISTR_TYPE, address) {} + explicit ParameterSetEx(const void* address) + : ParameterSet(UNISTR_TYPE, address) {} }; template <typename T> diff --git a/chromium/sandbox/win/src/policy_low_level.cc b/chromium/sandbox/win/src/policy_low_level.cc index d987211c8b5..ea6fcbb0431 100644 --- a/chromium/sandbox/win/src/policy_low_level.cc +++ b/chromium/sandbox/win/src/policy_low_level.cc @@ -10,6 +10,8 @@ #include <map> #include <string> +#include "base/compiler_specific.h" + namespace { // A single rule can use at most this amount of memory. diff --git a/chromium/sandbox/win/src/sandbox.cc b/chromium/sandbox/win/src/sandbox.cc index f65e379683d..4a2d3539525 100644 --- a/chromium/sandbox/win/src/sandbox.cc +++ b/chromium/sandbox/win/src/sandbox.cc @@ -41,7 +41,18 @@ TargetServices* SandboxFactory::GetTargetServices() { } // namespace sandbox +// Allow calling to SetInterceptionHint from any loaded DLL. +extern "C" { + // Allows querying for whether the current process has been sandboxed. -extern "C" bool __declspec(dllexport) IsSandboxedProcess() { +__declspec(dllexport) bool IsSandboxedProcess() { return !!sandbox::g_shared_section; } + +__declspec(dllexport) sandbox::TargetServicesBase* GetMainTargetServices() { + if (!sandbox::g_shared_section) + return nullptr; + return sandbox::TargetServicesBase::GetInstance(); +} + +} // extern "C" diff --git a/chromium/sandbox/win/src/sandbox.h b/chromium/sandbox/win/src/sandbox.h index 35996c1726e..3e280ad3004 100644 --- a/chromium/sandbox/win/src/sandbox.h +++ b/chromium/sandbox/win/src/sandbox.h @@ -21,6 +21,7 @@ #if !defined(SANDBOX_FUZZ_TARGET) #include <windows.h> +#include <winsock2.h> #else #include "sandbox/win/fuzzer/fuzzer_types.h" #endif @@ -143,7 +144,7 @@ class [[clang::lto_visibility_public]] BrokerServices { // } // // For more information see the BrokerServices API documentation. -class TargetServices { +class [[clang::lto_visibility_public]] TargetServices { public: // Initializes the target. Must call this function before any other. // returns ALL_OK if successful. All other return values imply failure. @@ -161,6 +162,12 @@ class TargetServices { // LowerToken has been called or not. virtual ProcessState* GetState() = 0; + // Attempts to create a socket in the broker process, and duplicates it back + // to the target. The socket will be created with default flags and no group. + // Only TCP/UDP and IPV4/IPV6 sockets are supported by the broker. + // The socket will be created with WSA_FLAG_OVERLAPPED flags. + virtual SOCKET CreateBrokeredSocket(int af, int family, int protocol) = 0; + protected: ~TargetServices() {} }; diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h index 89713514f28..d24baf47ee8 100644 --- a/chromium/sandbox/win/src/sandbox_policy.h +++ b/chromium/sandbox/win/src/sandbox_policy.h @@ -32,7 +32,8 @@ class TargetPolicy { SUBSYS_REGISTRY, // Creation and opening of registry keys. SUBSYS_SYNC, // Creation of named sync objects. SUBSYS_WIN32K_LOCKDOWN, // Win32K Lockdown related policy. - SUBSYS_SIGNED_BINARY // Signed binary policy. + SUBSYS_SIGNED_BINARY, // Signed binary policy. + SUBSYS_SOCKET // Socket brokering policy. }; // Allowable semantics when a rule is matched. @@ -59,7 +60,8 @@ class TargetPolicy { FAKE_USER_GDI_INIT, // Fakes user32 and gdi32 initialization. This can // be used to allow the DLLs to load and initialize // even if the process cannot access that subsystem. - SIGNED_ALLOW_LOAD // Allows loading the module when CIG is enabled. + SIGNED_ALLOW_LOAD, // Allows loading the module when CIG is enabled. + SOCKET_ALLOW_BROKER // Allows brokering of sockets. }; // Increments the reference count of this object. The reference count must diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc index 0c8896a78a8..4f585e5e380 100644 --- a/chromium/sandbox/win/src/sandbox_policy_base.cc +++ b/chromium/sandbox/win/src/sandbox_policy_base.cc @@ -13,7 +13,6 @@ #include "base/callback.h" #include "base/logging.h" #include "base/macros.h" -#include "base/stl_util.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" #include "sandbox/win/src/acl.h" @@ -781,6 +780,15 @@ ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem, } break; } + case SUBSYS_SOCKET: { + // Only one semantic is supported for this subsystem; to allow socket + // brokering. + DCHECK_EQ(SOCKET_ALLOW_BROKER, semantics); + // A very simple policy that just allows socket brokering if present. + PolicyRule socket_policy(ASK_BROKER); + policy_maker_->AddRule(IpcTag::WS2SOCKET, &socket_policy); + break; + } default: { return SBOX_ERROR_UNSUPPORTED; } } diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc index bc1da58d028..f1f580e5350 100644 --- a/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc @@ -193,6 +193,8 @@ std::string GetIpcTagAsString(IpcTag service) { return "CreateThread"; case IpcTag::NTCREATESECTION: return "NtCreateSection"; + case IpcTag::WS2SOCKET: + return "WSA_Socket"; case IpcTag::LAST: DCHECK(false) << "Unknown IpcTag"; return "Unknown"; diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.h b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h index 89982a3f115..d5ab4664bee 100644 --- a/chromium/sandbox/win/src/sandbox_policy_diagnostic.h +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h @@ -30,7 +30,7 @@ class PolicyBase; class PolicyDiagnostic final : public PolicyInfo { public: // This should quickly copy what it needs from PolicyBase. - PolicyDiagnostic(PolicyBase* policy); + explicit PolicyDiagnostic(PolicyBase* policy); ~PolicyDiagnostic() override; const char* JsonString() override; diff --git a/chromium/sandbox/win/src/socket_dispatcher.cc b/chromium/sandbox/win/src/socket_dispatcher.cc new file mode 100644 index 00000000000..5d042fe91f4 --- /dev/null +++ b/chromium/sandbox/win/src/socket_dispatcher.cc @@ -0,0 +1,132 @@ +// 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. + +#include "sandbox/win/src/socket_dispatcher.h" + +#include <stdint.h> +#include <winsock2.h> + +#include <string> + +#include "base/strings/string_util.h" +#include "base/win/scoped_handle.h" +#include "sandbox/win/src/crosscall_client.h" +#include "sandbox/win/src/interception.h" +#include "sandbox/win/src/interceptors.h" +#include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/policy_engine_params.h" +#include "sandbox/win/src/policy_params.h" +#include "sandbox/win/src/sandbox.h" + +namespace sandbox { + +namespace { + +class SocketHandleTraits { + public: + typedef SOCKET Handle; + + SocketHandleTraits() = delete; + SocketHandleTraits(const SocketHandleTraits&) = delete; + SocketHandleTraits& operator=(const SocketHandleTraits&) = delete; + + static bool CloseHandle(SOCKET handle) { return ::closesocket(handle) == 0; } + static bool IsHandleValid(SOCKET handle) { return handle != INVALID_SOCKET; } + static SOCKET NullHandle() { return INVALID_SOCKET; } +}; + +class DummySocketVerifierTraits { + public: + using Handle = SOCKET; + + DummySocketVerifierTraits() = delete; + DummySocketVerifierTraits(const DummySocketVerifierTraits&) = delete; + DummySocketVerifierTraits& operator=(const DummySocketVerifierTraits&) = + delete; + + static void StartTracking(SOCKET handle, + const void* owner, + const void* pc1, + const void* pc2) {} + static void StopTracking(SOCKET handle, + const void* owner, + const void* pc1, + const void* pc2) {} +}; + +typedef base::win::GenericScopedHandle<SocketHandleTraits, + DummySocketVerifierTraits> + ScopedSocketHandle; + +} // namespace + +SocketDispatcher::SocketDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall create_params = { + {IpcTag::WS2SOCKET, + {UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, INOUTPTR_TYPE}}, + reinterpret_cast<CallbackGeneric>(&SocketDispatcher::WS2Socket)}; + + ipc_calls_.push_back(create_params); +} + +bool SocketDispatcher::SetupService(InterceptionManager* manager, + IpcTag service) { + // This IPC has no interceptions. + return true; +} + +bool SocketDispatcher::WS2Socket(IPCInfo* ipc, + uint32_t af, + uint32_t type, + uint32_t protocol, + InOutCountedBuffer* protocol_info_buffer) { + if (af != AF_INET && af != AF_INET6) + return false; + + if (type != SOCK_STREAM && type != SOCK_DGRAM) + return false; + + if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) + return false; + + if (protocol_info_buffer->Size() != sizeof(WSAPROTOCOL_INFOW)) + return false; + + CountedParameterSet<NameBased> params; + // Policy for the IPC just needs to exist, the parameters here do not matter. + const wchar_t* dummy_param = L""; + params[NameBased::NAME] = ParamPickerMake(dummy_param); + + // Verify that the target process has the permission to broker sockets. + if (policy_base_->EvalPolicy(IpcTag::WS2SOCKET, params.GetBase()) != + ASK_BROKER) { + return false; + } + + ScopedSocketHandle local_socket( + ::WSASocketW(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED)); + + if (!local_socket.IsValid()) { + ipc->return_info.extended_count = 1; + ipc->return_info.extended[0].unsigned_int = + static_cast<uint32_t>(::WSAGetLastError()); + return true; + } + + WSAPROTOCOL_INFOW* protocol_info = + reinterpret_cast<WSAPROTOCOL_INFOW*>(protocol_info_buffer->Buffer()); + + if (::WSADuplicateSocketW(local_socket.Get(), ipc->client_info->process_id, + protocol_info)) { + ipc->return_info.extended_count = 1; + ipc->return_info.extended[0].unsigned_int = + static_cast<uint32_t>(::WSAGetLastError()); + return true; + } + + return true; +} + +} // namespace sandbox diff --git a/chromium/sandbox/win/src/socket_dispatcher.h b/chromium/sandbox/win/src/socket_dispatcher.h new file mode 100644 index 00000000000..478ec114bcb --- /dev/null +++ b/chromium/sandbox/win/src/socket_dispatcher.h @@ -0,0 +1,45 @@ +// 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_WIN_SRC_SOCKET_DISPATCHER_H_ +#define SANDBOX_WIN_SRC_SOCKET_DISPATCHER_H_ + +#include <stdint.h> +#include <winsock2.h> + +#include "base/macros.h" +#include "sandbox/win/src/crosscall_client.h" +#include "sandbox/win/src/crosscall_server.h" +#include "sandbox/win/src/interception.h" +#include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles socket related IPC calls. +class SocketDispatcher : public Dispatcher { + public: + explicit SocketDispatcher(PolicyBase* policy_base); + ~SocketDispatcher() override = default; + + SocketDispatcher(const SocketDispatcher&) = delete; + SocketDispatcher& operator=(const SocketDispatcher&) = delete; + + // Dispatcher interface. + bool SetupService(InterceptionManager* manager, IpcTag service) override; + + private: + // Processes IPC requests coming from calls to WS2_32!WSA_Socket in the + // target. + bool WS2Socket(IPCInfo* ipc, + uint32_t af, + uint32_t type, + uint32_t protocol, + InOutCountedBuffer* buffer); + PolicyBase* policy_base_; +}; + +} // namespace sandbox + +#endif // SANDBOX_WIN_SRC_SOCKET_DISPATCHER_H_ diff --git a/chromium/sandbox/win/src/target_services.cc b/chromium/sandbox/win/src/target_services.cc index 52f61d7102a..23a29b6efae 100644 --- a/chromium/sandbox/win/src/target_services.cc +++ b/chromium/sandbox/win/src/target_services.cc @@ -165,6 +165,38 @@ TargetServicesBase* TargetServicesBase::GetInstance() { return g_target_services; } +SOCKET TargetServicesBase::CreateBrokeredSocket(int af, + int type, + int protocol) { + if (!GetState()->InitCalled()) + return INVALID_SOCKET; + + // IPC must be fully started. + void* memory = GetGlobalIPCMemory(); + if (!memory) + return INVALID_SOCKET; + + CrossCallReturn answer = {0}; + SharedMemIPCClient ipc(memory); + + WSAPROTOCOL_INFOW protocol_info = {}; + + InOutCountedBuffer protocol_info_buffer(&protocol_info, + sizeof(WSAPROTOCOL_INFOW)); + + ResultCode code = CrossCall(ipc, IpcTag::WS2SOCKET, af, type, protocol, + protocol_info_buffer, &answer); + + if (code != SBOX_ALL_OK) + return INVALID_SOCKET; + + if (answer.extended_count == 1) + WSASetLastError(static_cast<int>(answer.extended[0].unsigned_int)); + + return ::WSASocket(af, type, protocol, &protocol_info, 0, + WSA_FLAG_OVERLAPPED); +} + // The broker services a 'test' IPC service with the PING tag. bool TargetServicesBase::TestIPCPing(int version) { void* memory = GetGlobalIPCMemory(); diff --git a/chromium/sandbox/win/src/target_services.h b/chromium/sandbox/win/src/target_services.h index 359ead4c41f..a63072fce87 100644 --- a/chromium/sandbox/win/src/target_services.h +++ b/chromium/sandbox/win/src/target_services.h @@ -41,10 +41,11 @@ class TargetServicesBase : public TargetServices { public: TargetServicesBase(); - // Public interface of TargetServices. + // Public interface of TargetServices. See comments in sandbox.h. ResultCode Init() override; void LowerToken() override; ProcessState* GetState() override; + SOCKET CreateBrokeredSocket(int af, int type, int protocol) override; // Factory method. static TargetServicesBase* GetInstance(); diff --git a/chromium/sandbox/win/src/threadpool.cc b/chromium/sandbox/win/src/threadpool.cc index 1d8fa8ac829..d4f9a554870 100644 --- a/chromium/sandbox/win/src/threadpool.cc +++ b/chromium/sandbox/win/src/threadpool.cc @@ -37,18 +37,24 @@ bool ThreadPool::UnRegisterWaits(void* cookie) { if (0 == cookie) { return false; } - AutoLock lock(&lock_); - bool success = true; - PoolObjects::iterator it = pool_objects_.begin(); - while (it != pool_objects_.end()) { - if (it->cookie == cookie) { - HANDLE wait = it->wait; - it = pool_objects_.erase(it); - success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0); - } else { - ++it; + std::vector<HANDLE> finished_waits; + { + AutoLock lock(&lock_); + PoolObjects::iterator it = pool_objects_.begin(); + while (it != pool_objects_.end()) { + if (it->cookie == cookie) { + HANDLE wait = it->wait; + it = pool_objects_.erase(it); + finished_waits.push_back(wait); + } else { + ++it; + } } } + bool success = true; + for (HANDLE wait : finished_waits) { + success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0); + } return success; } diff --git a/chromium/sandbox/win/src/top_level_dispatcher.cc b/chromium/sandbox/win/src/top_level_dispatcher.cc index da354af9550..522f2f6f7a8 100644 --- a/chromium/sandbox/win/src/top_level_dispatcher.cc +++ b/chromium/sandbox/win/src/top_level_dispatcher.cc @@ -20,6 +20,7 @@ #include "sandbox/win/src/registry_dispatcher.h" #include "sandbox/win/src/sandbox_policy_base.h" #include "sandbox/win/src/signed_dispatcher.h" +#include "sandbox/win/src/socket_dispatcher.h" #include "sandbox/win/src/sync_dispatcher.h" namespace sandbox { @@ -70,6 +71,10 @@ TopLevelDispatcher::TopLevelDispatcher(PolicyBase* policy) : policy_(policy) { dispatcher = new SignedDispatcher(policy_); ipc_targets_[static_cast<size_t>(IpcTag::NTCREATESECTION)] = dispatcher; signed_dispatcher_.reset(dispatcher); + + dispatcher = new SocketDispatcher(policy_); + ipc_targets_[static_cast<size_t>(IpcTag::WS2SOCKET)] = dispatcher; + socket_dispatcher_.reset(dispatcher); } TopLevelDispatcher::~TopLevelDispatcher() {} diff --git a/chromium/sandbox/win/src/top_level_dispatcher.h b/chromium/sandbox/win/src/top_level_dispatcher.h index 94a923cee4c..897d822b5a3 100644 --- a/chromium/sandbox/win/src/top_level_dispatcher.h +++ b/chromium/sandbox/win/src/top_level_dispatcher.h @@ -43,6 +43,7 @@ class TopLevelDispatcher : public Dispatcher { std::unique_ptr<Dispatcher> handle_dispatcher_; std::unique_ptr<Dispatcher> process_mitigations_win32k_dispatcher_; std::unique_ptr<Dispatcher> signed_dispatcher_; + std::unique_ptr<Dispatcher> socket_dispatcher_; Dispatcher* ipc_targets_[kMaxIpcTag]; DISALLOW_COPY_AND_ASSIGN(TopLevelDispatcher); diff --git a/chromium/sandbox/win/src/win_utils.cc b/chromium/sandbox/win/src/win_utils.cc index 0c97c31bcd0..1db77504ba1 100644 --- a/chromium/sandbox/win/src/win_utils.cc +++ b/chromium/sandbox/win/src/win_utils.cc @@ -13,8 +13,8 @@ #include <string> #include <vector> +#include "base/cxx17_backports.h" #include "base/numerics/safe_math.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/win/pe_image.h" #include "sandbox/win/src/internal_types.h" diff --git a/chromium/sandbox/win/src/win_utils.h b/chromium/sandbox/win/src/win_utils.h index 5945cd60965..085be2a95e3 100644 --- a/chromium/sandbox/win/src/win_utils.h +++ b/chromium/sandbox/win/src/win_utils.h @@ -10,8 +10,8 @@ #include <memory> #include <string> +#include "base/cxx17_backports.h" #include "base/macros.h" -#include "base/stl_util.h" #include "sandbox/win/src/nt_internals.h" namespace sandbox { |