From 0d87dc1ee79daea085de5a542cedee69d2122955 Mon Sep 17 00:00:00 2001 From: Simon South Date: Thu, 30 Sep 2021 11:33:23 -0400 Subject: [PATCH] sandbox: Allow use with fragile hardening on AArch64 and elsewhere Update the sandbox implementation to allow its use with fragile hardening enabled on AArch64 (ARM64) and other architectures that use Linux's generic syscall interface. Note that in this configuration the sandbox is completely unable to filter requests to open files and directories. Update the sandbox unit tests to match. --- src/lib/sandbox/sandbox.c | 52 ++++++++++++++++++++++++++++++++++++--- src/test/test_sandbox.c | 8 +++--- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c index b09fdcb89c..1573362774 100644 --- a/src/lib/sandbox/sandbox.c +++ b/src/lib/sandbox/sandbox.c @@ -524,6 +524,11 @@ libc_uses_openat_for_open(void) #endif /* defined(__NR_open) */ } +/* Calls to opendir() cannot be filtered by the sandbox when built with fragile + * hardening for an architecture that uses Linux's generic syscall interface, + * so prevent a compiler warning by omitting this function along with + * sb_opendir(). */ +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) /* Return true if we think we're running with a libc that uses openat for the * opendir function on linux. */ static int @@ -537,6 +542,8 @@ libc_uses_openat_for_opendir(void) return 1; #endif /* defined(__NR_open) */ } +#endif /* !(defined(ENABLE_FRAGILE_HARDENING) && + defined(ARCH_USES_GENERIC_SYSCALLS)) */ /** Allow a single file to be opened. If use_openat is true, * we're using a libc that remaps all the opens into openats. */ @@ -566,10 +573,25 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) int use_openat = libc_uses_openat_for_open(); #ifdef ENABLE_FRAGILE_HARDENING - /* AddressSanitizer uses the "open" syscall to access information about the - * running process via the filesystem, so that call must be allowed without + /* AddressSanitizer uses either the "open" or the "openat" syscall (depending + * on the architecture) to access information about the running process via + * the filesystem, so the appropriate call must be allowed without * restriction or the sanitizer will be unable to execute normally when the * process terminates. */ +#ifdef ARCH_USES_GENERIC_SYSCALLS + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), + SCMP_CMP_LOWER32_EQ(0, AT_FDCWD)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " + "libseccomp error %d", rc); + return rc; + } + + /* The "open" syscall is not defined on this architecture, so any other + * requests to open files will necessarily use "openat" as well and there is + * no need to consider any additional rules. */ + return 0; +#else rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open)); if (rc != 0) { log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " @@ -581,7 +603,8 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) * there is no need to consider any additional rules. */ if (!use_openat) return 0; -#endif +#endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ +#endif /* defined(ENABLE_FRAGILE_HARDENING) */ // for each dynamic parameter filters for (elem = filter; elem != NULL; elem = elem->next) { @@ -832,6 +855,17 @@ sb_renameat2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) } #endif /* defined(__NR_rename) || defined(__NR_renameat) */ +/* If Tor is built with fragile hardening for an architecture that uses Linux's + * generic syscall interface a rule allowing the "openat" syscall without + * restriction will have already been added by sb_open(), so there is no need + * to consider adding additional, more restrictive rules here as they will + * simply be ignored. + * + * Also, since the "open" syscall is not defined on these architectures, glibc + * will necessarily use "openat" for its implementation of opendir() as well. + * This means neither of the following two functions will have any effect and + * both can be omitted. */ +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) /** * Function responsible for setting up the openat syscall for * the seccomp filter sandbox. @@ -887,6 +921,8 @@ sb_opendir(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#endif /* !(defined(ENABLE_FRAGILE_HARDENING) && + defined(ARCH_USES_GENERIC_SYSCALLS)) */ #ifdef ENABLE_FRAGILE_HARDENING /** @@ -906,9 +942,17 @@ sb_ptrace(scmp_filter_ctx ctx, sandbox_cfg_t *filter) if (rc) return rc; + /* AddressSanitizer uses "PTRACE_GETREGSET" on AArch64 (ARM64) and + * System/390, "PTRACE_GETREGS" everywhere else. */ +#if defined(__aarch64__) || defined(__s390__) + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), + SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGSET), + SCMP_CMP(1, SCMP_CMP_EQ, pid)); +#else rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGS), SCMP_CMP(1, SCMP_CMP_EQ, pid)); +#endif /* defined(__aarch64__) || defined(__s390__) */ if (rc) return rc; @@ -1494,8 +1538,10 @@ static sandbox_filter_func_t filter_func[] = { sb_chmod, #endif sb_open, +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) sb_openat, sb_opendir, +#endif #ifdef ENABLE_FRAGILE_HARDENING sb_ptrace, #endif diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c index 7ec08a3546..28b60e9f3e 100644 --- a/src/test/test_sandbox.c +++ b/src/test/test_sandbox.c @@ -310,22 +310,22 @@ test_sandbox_stat_filename(void *arg) struct testcase_t sandbox_tests[] = { SANDBOX_TEST(is_active, TT_FORK), -/* When Tor is built with fragile compiler-hardening the sandbox is unable to - * filter requests to open files or directories (on systems where glibc uses - * the "open" system call to provide this functionality), as doing so would +/* When Tor is built with fragile compiler-hardening the sandbox is usually + * unable to filter requests to open files or directories, as doing so would * interfere with the address sanitizer as it retrieves information about the * running process via the filesystem. Skip these tests in that case as the * corresponding functions are likely to have no effect and this will cause the * tests to fail. */ #ifdef ENABLE_FRAGILE_HARDENING SANDBOX_TEST_SKIPPED(open_filename), + SANDBOX_TEST_SKIPPED(openat_filename), SANDBOX_TEST_SKIPPED(opendir_dirname), #else SANDBOX_TEST_IN_SANDBOX(open_filename), + SANDBOX_TEST_IN_SANDBOX(openat_filename), SANDBOX_TEST_IN_SANDBOX(opendir_dirname), #endif /* defined(ENABLE_FRAGILE_HARDENING) */ - SANDBOX_TEST_IN_SANDBOX(openat_filename), SANDBOX_TEST_IN_SANDBOX(chmod_filename), SANDBOX_TEST_IN_SANDBOX(chown_filename), SANDBOX_TEST_IN_SANDBOX(rename_filename),