Commit b558ba51 authored by Chris Palmer's avatar Chris Palmer Committed by Commit Bot

[sandbox] Remove address space limits on Linux.

Unfortunately, it does not appear possible to set RLIMIT_AS such that it will
both (a) be high enough to support V8's and WebAssembly's address space
requirements while also (b) being low enough to mitigate exploits using integer
overflows that require large allocations, heap spray, or other memory-hungry
attack modes.

Bug: 800348
TBR: jln
Change-Id: I4ec234709a4e49abde1c22f9c63a87bac107b8a0
Reviewed-on: https://chromium-review.googlesource.com/1097553
Commit-Queue: Chris Palmer <palmer@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarJorge Lucangeli Obes <jorgelo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568073}
parent 715eedd0
......@@ -78,7 +78,8 @@ void RestrictAddressSpaceUsage() {
// This could almost certainly be set to zero. GLibc's allocator and others
// would fall-back to mmap if brk() fails.
const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
CHECK(sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize));
CHECK_EQ(0,
sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize));
#if defined(ARCH_CPU_64_BITS)
// NaCl's x86-64 sandbox allocated 88GB address of space during startup:
......@@ -94,7 +95,7 @@ void RestrictAddressSpaceUsage() {
// bits when running under 64 bits kernels. Set a limit in case this happens.
const rlim_t kNewAddressSpaceLimit = std::numeric_limits<uint32_t>::max();
#endif
CHECK(sandbox::ResourceLimits::Lower(RLIMIT_AS, kNewAddressSpaceLimit));
CHECK_EQ(0, sandbox::ResourceLimits::Lower(RLIMIT_AS, kNewAddressSpaceLimit));
}
} // namespace
......
......@@ -8,40 +8,42 @@
#include <sys/resource.h>
#include <sys/time.h>
#include <errno.h>
#include <algorithm>
namespace sandbox {
// static
bool ResourceLimits::Lower(int resource, rlim_t limit) {
int ResourceLimits::Lower(int resource, rlim_t limit) {
return LowerSoftAndHardLimits(resource, limit, limit);
}
// static
bool ResourceLimits::LowerSoftAndHardLimits(int resource,
int ResourceLimits::LowerSoftAndHardLimits(int resource,
rlim_t soft_limit,
rlim_t hard_limit) {
struct rlimit old_rlimit;
if (getrlimit(resource, &old_rlimit))
return false;
return errno;
// Make sure we don't raise the existing limit.
const struct rlimit new_rlimit = {std::min(old_rlimit.rlim_cur, soft_limit),
std::min(old_rlimit.rlim_max, hard_limit)};
return setrlimit(resource, &new_rlimit) == 0;
return setrlimit(resource, &new_rlimit) ? errno : 0;
}
// static
bool ResourceLimits::AdjustCurrent(int resource, long long int change) {
int ResourceLimits::AdjustCurrent(int resource, long long int change) {
struct rlimit old_rlimit;
if (getrlimit(resource, &old_rlimit))
return false;
return errno;
base::CheckedNumeric<rlim_t> checked_limit = old_rlimit.rlim_cur;
checked_limit += change;
const rlim_t new_limit = checked_limit.ValueOrDefault(old_rlimit.rlim_max);
const struct rlimit new_rlimit = {std::min(new_limit, old_rlimit.rlim_max),
old_rlimit.rlim_max};
// setrlimit will fail if limit > old_rlimit.rlim_max.
return setrlimit(resource, &new_rlimit) == 0;
return setrlimit(resource, &new_rlimit) ? errno : 0;
}
} // namespace sandbox
......@@ -17,18 +17,21 @@ namespace sandbox {
class SANDBOX_EXPORT ResourceLimits {
public:
// Lower the soft and hard limit of |resource| to |limit|. If the current
// limit is lower than |limit|, keep it.
static bool Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT;
// Lower the soft limit of |resource| to |limit| and the hard limit to
// |max|. If the current limit is lower than the new values, keep it.
static bool LowerSoftAndHardLimits(int resource,
// limit is lower than |limit|, keep it. Returns 0 on success, or |errno|.
static int Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT;
// Lower the soft limit of |resource| to |limit| and the hard limit to |max|.
// If the current limit is lower than the new values, keep it. Returns 0 on
// success, or |errno|.
static int LowerSoftAndHardLimits(int resource,
rlim_t soft_limit,
rlim_t hard_limit) WARN_UNUSED_RESULT;
// Change soft limit of |resource| to the current limit plus |change|. If
// |resource + change| is larger than the hard limit, the soft limit is set to
// be the same as the hard limit. Fails and returns false if the underlying
// call to setrlimit fails.
static bool AdjustCurrent(int resource,
// call to setrlimit fails. Returns 0 on success, or |errno|.
static int AdjustCurrent(int resource,
long long int change) WARN_UNUSED_RESULT;
private:
......
......@@ -29,7 +29,7 @@ namespace {
// all ASAN builds.
SANDBOX_TEST(ResourceLimits, MAYBE_NoFork) {
// Make sure that fork will fail with EAGAIN.
SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0));
SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0) == 0);
errno = 0;
pid_t pid = fork();
// Reap any child if fork succeeded.
......
......@@ -388,12 +388,19 @@ bool SandboxLinux::InitializeSandbox(SandboxType sandbox_type,
if (options.engage_namespace_sandbox)
EngageNamespaceSandbox(false /* from_zygote */);
DCHECK(!HasOpenDirectories())
CHECK(!HasOpenDirectories())
<< "InitializeSandbox() called after unexpected directories have been "
<< "opened. This breaks the security of the setuid sandbox.";
// Attempt to limit the future size of the address space of the process.
LimitAddressSpace(process_type, options);
int error = 0;
const bool limited_as = LimitAddressSpace(&error);
if (error) {
// Restore errno. Internally to |LimitAddressSpace|, the errno due to
// setrlimit may be lost.
errno = error;
PCHECK(limited_as);
}
return StartSeccompBPF(sandbox_type, std::move(hook), options);
}
......@@ -413,8 +420,7 @@ bool SandboxLinux::seccomp_bpf_with_tsync_supported() const {
return seccomp_bpf_with_tsync_supported_;
}
bool SandboxLinux::LimitAddressSpace(const std::string& process_type,
const Options& options) {
bool SandboxLinux::LimitAddressSpace(int* error) {
#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
......@@ -422,39 +428,11 @@ bool SandboxLinux::LimitAddressSpace(const std::string& process_type,
return false;
}
// Limit the address space to 4 GiB. This is a hard-to-quantify, last-ditch
// defense against exploits that have the precondition of allocating large
// amounts of memory. (For an example, see
// https://bugs.chromium.org/p/project-zero/issues/detail?id=1541, which
// exploits integer overflow but requires allocating an amount of memory which
// would overflow a 32-bit integer.)
rlim_t rlimit_as_soft = std::numeric_limits<uint32_t>::max();
rlim_t rlimit_as_hard = std::numeric_limits<uint32_t>::max();
if (sizeof(rlim_t) == 8) {
// On 64-bit platforms, V8 and possibly others will reserve massive memory
// ranges and rely on on-demand paging for allocation. (Unfortunately, even
// MADV_DONTNEED ranges count towards RLIMIT_AS, which complicates our
// ability to set tight limits while still enabling V8's strategy.) See
// https://crbug.com/169327.
//
// In the GPU process, irrespective of V8, we can exhaust a 4 GiB address
// space under normal usage. See https://crbug.com/271119.
if (process_type == switches::kRendererProcess ||
process_type == switches::kGpuProcess) {
// For now, set the limit to 16 GiB for renderer, worker, and GPU
// processes to accomodate. It may be necessary to raise this limit even
// further; see https://crbug.com/800348.
rlimit_as_soft = 1ULL << 34;
// WebAssembly memory objects use a large amount of address space for
// guard regions. To accomodate this, we allow the address space limit to
// adjust dynamically up to a certain limit. The limit is currently 4 TiB,
// which should allow enough address space for any reasonable page. See
// https://crbug.com/750378.
rlimit_as_hard = 1ULL << 42;
}
}
// Unfortunately, it does not appear possible to set RLIMIT_AS such that it
// will both (a) be high enough to support V8's and WebAssembly's address
// space requirements while also (b) being low enough to mitigate exploits
// using integer overflows that require large allocations, heap spray, or
// other memory-hungry attack modes.
// By default, add a limit to the VmData memory area that would prevent
// allocations that can't be indexed by an int.
......@@ -469,14 +447,12 @@ bool SandboxLinux::LimitAddressSpace(const std::string& process_type,
rlimit_data = 1ULL << 33;
}
bool limited_as = sandbox::ResourceLimits::LowerSoftAndHardLimits(
RLIMIT_AS, rlimit_as_soft, rlimit_as_hard);
bool limited_data = sandbox::ResourceLimits::Lower(RLIMIT_DATA, rlimit_data);
*error = sandbox::ResourceLimits::Lower(RLIMIT_DATA, rlimit_data);
// Cache the resource limit before turning on the sandbox.
base::SysInfo::AmountOfVirtualMemory();
return limited_as && limited_data;
return *error == 0;
#else
base::SysInfo::AmountOfVirtualMemory();
return false;
......
......@@ -174,10 +174,10 @@ class SERVICE_MANAGER_SANDBOX_EXPORT SandboxLinux {
PreSandboxHook hook,
const Options& options);
// Limit the address space of the current process (and its children).
// to make some vulnerabilities harder to exploit.
bool LimitAddressSpace(const std::string& process_type,
const Options& options);
// Limit the address space of the current process (and its children) to make
// some vulnerabilities harder to exploit. Writes the errno due to setrlimit
// (including 0 if no error) into |error|.
bool LimitAddressSpace(int* error);
// Returns a file descriptor to proc. The file descriptor is no longer valid
// after the sandbox has been sealed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment