Skip to content

Commit

Permalink
Android: Add seccomp restrictions for ioctl().
Browse files Browse the repository at this point in the history
This unconditionally allows support for most ashmem and binder ioctls,
and conditionally permits specific userfaultfd ones for an upcoming
ART GC.

Bug: 1309700
Change-Id: I2355cca1bca78aaab45eed8f27348773ae0b9b87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3561260
Reviewed-by: Matthew Denton <mpdenton@chromium.org>
Reviewed-by: Peter Beverloo <peter@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#989002}
  • Loading branch information
rsesek authored and Chromium LUCI CQ committed Apr 5, 2022
1 parent 51faa55 commit 6644d52
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 12 deletions.
Expand Up @@ -20,7 +20,8 @@ void JNI_ImageDecoder_InitializePhotoPickerSandbox(JNIEnv* env) {

#if BUILDFLAG(USE_SECCOMP_BPF)
// The policy compiler is only available if USE_SECCOMP_BPF is enabled.
starter.set_policy(std::make_unique<sandbox::BaselinePolicyAndroid>());
starter.set_policy(std::make_unique<sandbox::BaselinePolicyAndroid>(
starter.GetDefaultBaselineOptions()));
#endif
starter.StartSandbox();

Expand Down
7 changes: 4 additions & 3 deletions content/renderer/renderer_main_platform_delegate_android.cc
Expand Up @@ -37,10 +37,11 @@ bool RendererMainPlatformDelegate::EnableSandbox() {
sandbox::SeccompStarterAndroid starter(info->sdk_int());
// The policy compiler is only available if USE_SECCOMP_BPF is enabled.
#if BUILDFLAG(USE_SECCOMP_BPF)
bool allow_sched_affinity =
sandbox::BaselinePolicyAndroid::RuntimeOptions options(
starter.GetDefaultBaselineOptions());
options.allow_sched_affinity =
base::FeatureList::IsEnabled(features::kBigLittleScheduling);
starter.set_policy(
std::make_unique<sandbox::BaselinePolicyAndroid>(allow_sched_affinity));
starter.set_policy(std::make_unique<sandbox::BaselinePolicyAndroid>(options));
#endif
starter.StartSandbox();

Expand Down
65 changes: 60 additions & 5 deletions sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
Expand Up @@ -6,7 +6,10 @@

#include <errno.h>
#include <fcntl.h>
#include <linux/android/binder.h>
#include <linux/ashmem.h>
#include <linux/net.h>
#include <linux/userfaultfd.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
Expand All @@ -25,9 +28,10 @@ using sandbox::bpf_dsl::AllOf;
using sandbox::bpf_dsl::Allow;
using sandbox::bpf_dsl::AnyOf;
using sandbox::bpf_dsl::Arg;
using sandbox::bpf_dsl::BoolConst;
using sandbox::bpf_dsl::BoolExpr;
using sandbox::bpf_dsl::If;
using sandbox::bpf_dsl::Error;
using sandbox::bpf_dsl::If;
using sandbox::bpf_dsl::ResultExpr;

namespace sandbox {
Expand Down Expand Up @@ -58,13 +62,61 @@ BoolExpr RestrictSocketArguments(const Arg<int>& domain,
}
#endif // !defined(__i386__)

ResultExpr RestrictAndroidIoctl(bool allow_userfaultfd_ioctls) {
const Arg<int> request(1);

// There is no way at runtime to test if the system is running with
// BINDER_IPC_32BIT. Instead, compute the corresponding bitness' ioctl
// request number, so that either are allowed in the case of mixed-bitness
// systems.
#ifdef BINDER_IPC_32BIT
const int kBinderWriteRead32 = BINDER_WRITE_READ;
const int kBinderWriteRead64 =
(BINDER_WRITE_READ & ~IOCSIZE_MASK) |
((sizeof(binder_write_read) * 2) << _IOC_SIZESHIFT);
#else
const int kBinderWriteRead64 = BINDER_WRITE_READ;
const int kBinderWriteRead32 =
(BINDER_WRITE_READ & ~IOCSIZE_MASK) |
((sizeof(binder_write_read) / 2) << _IOC_SIZESHIFT);
#endif

// ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), a legacy interface
// for getting clock information from /dev/alarm. It was removed in Android O
// (https://android-review.googlesource.com/c/221812), and it can be safely
// blocked in earlier releases because there is a fallback. Constant expanded
// from
// https://cs.android.com/android/platform/superproject/+/android-7.0.0_r1:external/kernel-headers/original/uapi/linux/android_alarm.h;l=57.
const int kAndroidAlarmGetTimeElapsedRealtime = 0x40086134;

return Switch(request)
.CASES((
// Android shared memory.
ASHMEM_SET_NAME, ASHMEM_GET_NAME, ASHMEM_SET_SIZE,
ASHMEM_GET_SIZE, ASHMEM_SET_PROT_MASK, ASHMEM_GET_PROT_MASK,
ASHMEM_PIN, ASHMEM_UNPIN, ASHMEM_GET_PIN_STATUS,
// Binder.
kBinderWriteRead32, kBinderWriteRead64, BINDER_SET_MAX_THREADS,
BINDER_THREAD_EXIT, BINDER_VERSION,
BINDER_ENABLE_ONEWAY_SPAM_DETECTION),
Allow())
.CASES((
// userfaultfd ART GC (https://crbug.com/1300653).
UFFDIO_REGISTER, UFFDIO_UNREGISTER, UFFDIO_WAKE, UFFDIO_COPY,
UFFDIO_ZEROPAGE, UFFDIO_CONTINUE),
If(BoolConst(allow_userfaultfd_ioctls), Allow())
.Else(RestrictIoctl()))
.CASES((kAndroidAlarmGetTimeElapsedRealtime), Error(EINVAL))
.Default(RestrictIoctl());
}

} // namespace

BaselinePolicyAndroid::BaselinePolicyAndroid()
: BaselinePolicy() {}

BaselinePolicyAndroid::BaselinePolicyAndroid(bool allow_sched_affinity)
: BaselinePolicy(), allow_sched_affinity_(allow_sched_affinity) {}
BaselinePolicyAndroid::BaselinePolicyAndroid(const RuntimeOptions& options)
: BaselinePolicy(), options_(options) {}

BaselinePolicyAndroid::~BaselinePolicyAndroid() {}

Expand Down Expand Up @@ -96,7 +148,6 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
#endif
case __NR_getdents64:
case __NR_getpriority:
case __NR_ioctl:
case __NR_membarrier: // https://crbug.com/966433
case __NR_mremap:
#if defined(__i386__)
Expand Down Expand Up @@ -155,11 +206,15 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
// (crbug.com/1111789). Should be removed or reconsidered once
// the experiment is complete.
if (sysno == __NR_sched_setaffinity || sysno == __NR_sched_getaffinity) {
if (allow_sched_affinity_)
if (options_.allow_sched_affinity)
return Allow();
return Error(EPERM);
}

if (sysno == __NR_ioctl) {
return RestrictAndroidIoctl(options_.allow_userfaultfd_ioctls);
}

// Ptrace is allowed so the crash reporter can fork in a renderer
// and then ptrace the parent. https://crbug.com/933418
if (sysno == __NR_ptrace) {
Expand Down
12 changes: 10 additions & 2 deletions sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h
Expand Up @@ -24,8 +24,16 @@ namespace sandbox {
// features. This needs an audit. https://crbug.com/739879
class SANDBOX_EXPORT BaselinePolicyAndroid : public BaselinePolicy {
public:
struct RuntimeOptions {
// Allows sched_setaffinity for a core selection performance experiment.
bool allow_sched_affinity = false;

// Allows a subset of the userfaultfd ioctls that are needed for ART GC.
bool allow_userfaultfd_ioctls = false;
};

BaselinePolicyAndroid();
explicit BaselinePolicyAndroid(bool allow_sched_affinity);
explicit BaselinePolicyAndroid(const RuntimeOptions& options);

BaselinePolicyAndroid(const BaselinePolicyAndroid&) = delete;
BaselinePolicyAndroid& operator=(const BaselinePolicyAndroid&) = delete;
Expand All @@ -37,7 +45,7 @@ class SANDBOX_EXPORT BaselinePolicyAndroid : public BaselinePolicy {
int system_call_number) const override;

private:
bool allow_sched_affinity_ = false;
const RuntimeOptions options_;
};

} // namespace sandbox
Expand Down
Expand Up @@ -5,11 +5,17 @@
#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h"

#include <fcntl.h>
#include <linux/android/binder.h>
#include <linux/ashmem.h>
#include <linux/userfaultfd.h>
#include <sched.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "base/files/scoped_file.h"
#include "base/posix/eintr_wrapper.h"
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"

Expand Down Expand Up @@ -60,7 +66,8 @@ BPF_TEST_C(BaselinePolicyAndroid,

class AllowSchedSetaffinityBaselinePoliyAndroid : public BaselinePolicyAndroid {
public:
AllowSchedSetaffinityBaselinePoliyAndroid() : BaselinePolicyAndroid(true) {}
AllowSchedSetaffinityBaselinePoliyAndroid()
: BaselinePolicyAndroid(RuntimeOptions{.allow_sched_affinity = true}) {}
};

BPF_TEST_C(BaselinePolicyAndroid,
Expand All @@ -71,5 +78,65 @@ BPF_TEST_C(BaselinePolicyAndroid,
BPF_ASSERT_NE(-1, sched_setaffinity(0, sizeof(set), &set));
}

BPF_TEST_C(BaselinePolicyAndroid, Ioctl, BaselinePolicyAndroid) {
base::ScopedFD fd(HANDLE_EINTR(open("/dev/null", O_RDWR)));
BPF_ASSERT(fd.is_valid());

errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), ASHMEM_SET_PROT_MASK));
BPF_ASSERT_EQ(ENOTTY, errno);

errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), BINDER_WRITE_READ));
BPF_ASSERT_EQ(ENOTTY, errno);
// 32- and 64-bit constant values for BINDER_WRITE_READ.
errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), 0xc0186201));
BPF_ASSERT_EQ(ENOTTY, errno);
errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), 0xc0306201));
BPF_ASSERT_EQ(ENOTTY, errno);

errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), TCGETS));
BPF_ASSERT_EQ(ENOTTY, errno);
}

BPF_DEATH_TEST_C(BaselinePolicyAndroid,
UserfaultfdIoctl_BlockedDefault,
DEATH_SEGV_MESSAGE(GetIoctlErrorMessageContentForTests()),
BaselinePolicyAndroid) {
base::ScopedFD fd(HANDLE_EINTR(open("/dev/null", O_RDWR)));
BPF_ASSERT(fd.is_valid());
ioctl(fd.get(), UFFDIO_WAKE);
}

class AllowUserfaultfdBaselinePolicyAndroid : public BaselinePolicyAndroid {
public:
AllowUserfaultfdBaselinePolicyAndroid()
: BaselinePolicyAndroid(
RuntimeOptions{.allow_userfaultfd_ioctls = true}) {}
};

BPF_TEST_C(BaselinePolicyAndroid,
UserfaultfdIoctl_Allowed,
AllowUserfaultfdBaselinePolicyAndroid) {
base::ScopedFD fd(HANDLE_EINTR(open("/dev/null", O_RDWR)));
BPF_ASSERT(fd.is_valid());

errno = 0;
BPF_ASSERT_EQ(-1, ioctl(fd.get(), UFFDIO_WAKE));
BPF_ASSERT_EQ(ENOTTY, errno);
}

BPF_DEATH_TEST_C(BaselinePolicyAndroid,
UserfaultfdIoctl_BlockedSubset,
DEATH_SEGV_MESSAGE(GetIoctlErrorMessageContentForTests()),
BaselinePolicyAndroid) {
base::ScopedFD fd(HANDLE_EINTR(open("/dev/null", O_RDWR)));
BPF_ASSERT(fd.is_valid());
ioctl(fd.get(), UFFDIO_API);
}

} // namespace
} // namespace sandbox
13 changes: 13 additions & 0 deletions sandbox/linux/seccomp-bpf-helpers/seccomp_starter_android.cc
Expand Up @@ -20,6 +20,19 @@ SeccompStarterAndroid::SeccompStarterAndroid(int build_sdk)

SeccompStarterAndroid::~SeccompStarterAndroid() = default;

#if BUILDFLAG(USE_SECCOMP_BPF)
BaselinePolicyAndroid::RuntimeOptions
SeccompStarterAndroid::GetDefaultBaselineOptions() const {
BaselinePolicyAndroid::RuntimeOptions options;
// On Android S+, there are CTS-enforced requirements that the kernel carries
// patches to userfaultfd that enforce usermode pages only (i.e.
// UFFD_USER_MODE_ONLY). Userfaultfd is used for a new ART garbage collector.
// See https://crbug.com/1300653 for details.
options.allow_userfaultfd_ioctls = sdk_int_ >= base::android::SDK_VERSION_S;
return options;
}
#endif

bool SeccompStarterAndroid::StartSandbox() {
#if BUILDFLAG(USE_SECCOMP_BPF)
DCHECK(policy_);
Expand Down
7 changes: 7 additions & 0 deletions sandbox/linux/seccomp-bpf-helpers/seccomp_starter_android.h
Expand Up @@ -12,6 +12,7 @@
#include <memory>

#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h"
#endif

namespace sandbox {
Expand Down Expand Up @@ -44,6 +45,12 @@ class SANDBOX_EXPORT SeccompStarterAndroid {

~SeccompStarterAndroid();

#if BUILDFLAG(USE_SECCOMP_BPF)
// Returns the default runtime-configured options for the baseline Android
// seccomp policy.
BaselinePolicyAndroid::RuntimeOptions GetDefaultBaselineOptions() const;
#endif

// Sets the BPF policy to apply. This must be called before StartSandbox()
// if BUILDFLAG(USE_SECCOMP_BPF) is true.
void set_policy(std::unique_ptr<bpf_dsl::Policy> policy) {
Expand Down

0 comments on commit 6644d52

Please sign in to comment.