diff options
author | jschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-07 09:05:45 +0000 |
---|---|---|
committer | jschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-07 09:05:45 +0000 |
commit | 8df046284280f3bde7da8e52e4b89216c1dc4e4d (patch) | |
tree | 6714b9632677344fd8a2f9be0ff284562438a0c2 /sandbox | |
parent | 18c1ed3a7782602736e2014102acf0c64a6486b7 (diff) | |
download | chromium_src-8df046284280f3bde7da8e52e4b89216c1dc4e4d.zip chromium_src-8df046284280f3bde7da8e52e4b89216c1dc4e4d.tar.gz chromium_src-8df046284280f3bde7da8e52e4b89216c1dc4e4d.tar.bz2 |
Add sandbox support for process memory limits
BUG=335192
Review URL: https://codereview.chromium.org/319573006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275666 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/win/src/broker_services.cc | 7 | ||||
-rw-r--r-- | sandbox/win/src/job.cc | 13 | ||||
-rw-r--r-- | sandbox/win/src/job.h | 3 | ||||
-rw-r--r-- | sandbox/win/src/job_unittest.cc | 29 | ||||
-rw-r--r-- | sandbox/win/src/restricted_token_utils.cc | 2 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy.h | 5 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy_base.cc | 15 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy_base.h | 2 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_types.h | 1 | ||||
-rw-r--r-- | sandbox/win/tests/validation_tests/commands.cc | 19 | ||||
-rw-r--r-- | sandbox/win/tests/validation_tests/suite.cc | 21 |
11 files changed, 94 insertions, 23 deletions
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index 2350342..895d535 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc @@ -278,6 +278,13 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { break; } + case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: { + BOOL res = ::TerminateJobObject(tracker->job, + SBOX_FATAL_MEMORY_EXCEEDED); + DCHECK(res); + break; + } + default: { NOTREACHED(); break; diff --git a/sandbox/win/src/job.cc b/sandbox/win/src/job.cc index 62b8d5c..8852ab0 100644 --- a/sandbox/win/src/job.cc +++ b/sandbox/win/src/job.cc @@ -16,7 +16,8 @@ Job::~Job() { DWORD Job::Init(JobLevel security_level, const wchar_t* job_name, - DWORD ui_exceptions) { + DWORD ui_exceptions, + size_t memory_limit) { if (job_handle_) return ERROR_ALREADY_INITIALIZED; @@ -52,11 +53,11 @@ DWORD Job::Init(JobLevel security_level, jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; } case JOB_UNPROTECTED: { - // The JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag is not supported on - // Windows 2000. We need a mechanism on Windows 2000 to ensure - // that processes in the job are terminated when the job is closed - if (base::win::GetVersion() == base::win::VERSION_PRE_XP) - break; + if (memory_limit) { + jeli.BasicLimitInformation.LimitFlags |= + JOB_OBJECT_LIMIT_PROCESS_MEMORY; + jeli.ProcessMemoryLimit = memory_limit; + } jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; diff --git a/sandbox/win/src/job.h b/sandbox/win/src/job.h index 7c215ef..60dc314 100644 --- a/sandbox/win/src/job.h +++ b/sandbox/win/src/job.h @@ -31,7 +31,8 @@ class Job { // the error. DWORD Init(JobLevel security_level, const wchar_t* job_name, - DWORD ui_exceptions); + DWORD ui_exceptions, + size_t memory_limit); // Assigns the process referenced by process_handle to the job. // If the function succeeds, the return value is ERROR_SUCCESS. If the diff --git a/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc index 3b2b331..a1b7aca 100644 --- a/sandbox/win/src/job_unittest.cc +++ b/sandbox/win/src/job_unittest.cc @@ -16,7 +16,7 @@ TEST(JobTest, TestCreation) { { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); // check if the job exists. HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, @@ -40,7 +40,7 @@ TEST(JobTest, TestDetach) { { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); job_handle = job.Detach(); ASSERT_TRUE(job_handle != NULL); @@ -73,7 +73,7 @@ TEST(JobTest, TestExceptions) { // Create the job. Job job; ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", - JOB_OBJECT_UILIMIT_READCLIPBOARD)); + JOB_OBJECT_UILIMIT_READCLIPBOARD, 0)); job_handle = job.Detach(); ASSERT_TRUE(job_handle != NULL); @@ -93,7 +93,7 @@ TEST(JobTest, TestExceptions) { { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); job_handle = job.Detach(); ASSERT_TRUE(job_handle != NULL); @@ -115,8 +115,8 @@ TEST(JobTest, TestExceptions) { TEST(JobTest, DoubleInit) { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); - ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0)); + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); + ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0, 0)); } // Tests the error case when we use a method and the object is not yet @@ -131,34 +131,35 @@ TEST(JobTest, NoInit) { // Tests the initialization of the job with different security level. TEST(JobTest, SecurityLevel) { Job job1; - ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0)); + ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0, 0)); Job job2; - ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0)); + ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0, 0)); Job job3; - ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0)); + ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0, 0)); Job job4; - ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0)); + ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0, 0)); Job job5; - ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0)); + ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0, 0)); // JOB_NONE means we run without a job object so Init should fail. Job job6; - ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init(JOB_NONE, L"job6", 0)); + ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init(JOB_NONE, L"job6", 0, 0)); Job job7; ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init( - static_cast<JobLevel>(JOB_NONE+1), L"job7", 0)); + static_cast<JobLevel>(JOB_NONE+1), L"job7", 0, 0)); } // Tests the method "AssignProcessToJob". TEST(JobTest, ProcessInJob) { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0)); + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0, + 0)); BOOL result = FALSE; diff --git a/sandbox/win/src/restricted_token_utils.cc b/sandbox/win/src/restricted_token_utils.cc index f3b1859..93b212e 100644 --- a/sandbox/win/src/restricted_token_utils.cc +++ b/sandbox/win/src/restricted_token_utils.cc @@ -146,7 +146,7 @@ DWORD StartRestrictedProcessInJob(wchar_t *command_line, JobLevel job_level, HANDLE *const job_handle_ret) { Job job; - DWORD err_code = job.Init(job_level, NULL, 0); + DWORD err_code = job.Init(job_level, NULL, 0, 0); if (ERROR_SUCCESS != err_code) return err_code; diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h index 4694fbe..9379d45 100644 --- a/sandbox/win/src/sandbox_policy.h +++ b/sandbox/win/src/sandbox_policy.h @@ -128,6 +128,11 @@ class TargetPolicy { // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN. virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) = 0; + // Sets a hard limit on the size of the commit set for the sandboxed process. + // If the limit is reached, the process will be terminated with + // SBOX_FATAL_MEMORY_EXCEEDED (7012). + virtual ResultCode SetJobMemoryLimit(size_t memory_limit) = 0; + // Specifies the desktop on which the application is going to run. If the // desktop does not exist, it will be created. If alternate_winstation is // set to true, the desktop will be created on an alternate window station. diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc index 3077604..41dc6be 100644 --- a/sandbox/win/src/sandbox_policy_base.cc +++ b/sandbox/win/src/sandbox_policy_base.cc @@ -80,6 +80,7 @@ PolicyBase::PolicyBase() initial_level_(USER_LOCKDOWN), job_level_(JOB_LOCKDOWN), ui_exceptions_(0), + memory_limit_(0), use_alternate_desktop_(false), use_alternate_winstation_(false), file_system_init_(false), @@ -170,11 +171,22 @@ TokenLevel PolicyBase::GetLockdownTokenLevel() const{ } ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) { + if (memory_limit_ && job_level == JOB_NONE) { + return SBOX_ERROR_BAD_PARAMS; + } job_level_ = job_level; ui_exceptions_ = ui_exceptions; return SBOX_ALL_OK; } +ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) { + if (memory_limit && job_level_ == JOB_NONE) { + return SBOX_ERROR_BAD_PARAMS; + } + memory_limit_ = memory_limit; + return SBOX_ALL_OK; +} + ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) { use_alternate_desktop_ = true; use_alternate_winstation_ = alternate_winstation; @@ -471,7 +483,8 @@ ResultCode PolicyBase::MakeJobObject(HANDLE* job) { if (job_level_ != JOB_NONE) { // Create the windows job object. Job job_obj; - DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_); + DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_, + memory_limit_); if (ERROR_SUCCESS != result) { return SBOX_ERROR_GENERIC; } diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h index 179cb6c..540f261 100644 --- a/sandbox/win/src/sandbox_policy_base.h +++ b/sandbox/win/src/sandbox_policy_base.h @@ -45,6 +45,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy { virtual TokenLevel GetLockdownTokenLevel() const OVERRIDE; virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) OVERRIDE; + virtual ResultCode SetJobMemoryLimit(size_t memory_limit) OVERRIDE; virtual ResultCode SetAlternateDesktop(bool alternate_winstation) OVERRIDE; virtual base::string16 GetAlternateDesktop() const OVERRIDE; virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) OVERRIDE; @@ -127,6 +128,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy { TokenLevel initial_level_; JobLevel job_level_; uint32 ui_exceptions_; + size_t memory_limit_; bool use_alternate_desktop_; bool use_alternate_winstation_; // Helps the file system policy initialization. diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h index 48f1b61..22840ce 100644 --- a/sandbox/win/src/sandbox_types.h +++ b/sandbox/win/src/sandbox_types.h @@ -58,6 +58,7 @@ enum TerminationCodes { SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching. SBOX_FATAL_CLOSEHANDLES = 7010, // Failed to close pending handles. SBOX_FATAL_MITIGATION = 7011, // Could not set the mitigation policy. + SBOX_FATAL_MEMORY_EXCEEDED = 7012, // Exceeded the job memory limit. SBOX_FATAL_LAST }; diff --git a/sandbox/win/tests/validation_tests/commands.cc b/sandbox/win/tests/validation_tests/commands.cc index e7620c3..4b12094 100644 --- a/sandbox/win/tests/validation_tests/commands.cc +++ b/sandbox/win/tests/validation_tests/commands.cc @@ -310,5 +310,24 @@ SBOX_TESTS_COMMAND int SleepCmd(int argc, wchar_t **argv) { return SBOX_TEST_SUCCEEDED; } +SBOX_TESTS_COMMAND int AllocateCmd(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + size_t mem_size = static_cast<size_t>(_wtoll(argv[0])); + void* memory = ::VirtualAlloc(NULL, mem_size, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + if (!memory) { + // We need to give the broker a chance to kill our process on failure. + ::Sleep(5000); + return SBOX_TEST_DENIED; + } + + if (!::VirtualFree(memory, 0, MEM_RELEASE)) + return SBOX_TEST_FAILED; + + return SBOX_TEST_SUCCEEDED; +} + } // namespace sandbox diff --git a/sandbox/win/tests/validation_tests/suite.cc b/sandbox/win/tests/validation_tests/suite.cc index f85b8da..4a8ae43 100644 --- a/sandbox/win/tests/validation_tests/suite.cc +++ b/sandbox/win/tests/validation_tests/suite.cc @@ -217,4 +217,25 @@ TEST(ValidationSuite, TestThread) { EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); } +// Tests if an over-limit allocation will be denied. +TEST(ValidationSuite, TestMemoryLimit) { + TestRunner runner; + wchar_t command[1024] = {0}; + const int kAllocationSize = 256 * 1024 * 1024; + + wsprintf(command, L"AllocateCmd %d", kAllocationSize); + runner.GetPolicy()->SetJobMemoryLimit(kAllocationSize); + EXPECT_EQ(SBOX_FATAL_MEMORY_EXCEEDED, runner.RunTest(command)); +} + +// Tests a large allocation will succeed absent limits. +TEST(ValidationSuite, TestMemoryNoLimit) { + TestRunner runner; + wchar_t command[1024] = {0}; + const int kAllocationSize = 256 * 1024 * 1024; + + wsprintf(command, L"AllocateCmd %d", kAllocationSize); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); +} + } // namespace sandbox |