summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authorjschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-07 09:05:45 +0000
committerjschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-07 09:05:45 +0000
commit8df046284280f3bde7da8e52e4b89216c1dc4e4d (patch)
tree6714b9632677344fd8a2f9be0ff284562438a0c2 /sandbox
parent18c1ed3a7782602736e2014102acf0c64a6486b7 (diff)
downloadchromium_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.cc7
-rw-r--r--sandbox/win/src/job.cc13
-rw-r--r--sandbox/win/src/job.h3
-rw-r--r--sandbox/win/src/job_unittest.cc29
-rw-r--r--sandbox/win/src/restricted_token_utils.cc2
-rw-r--r--sandbox/win/src/sandbox_policy.h5
-rw-r--r--sandbox/win/src/sandbox_policy_base.cc15
-rw-r--r--sandbox/win/src/sandbox_policy_base.h2
-rw-r--r--sandbox/win/src/sandbox_types.h1
-rw-r--r--sandbox/win/tests/validation_tests/commands.cc19
-rw-r--r--sandbox/win/tests/validation_tests/suite.cc21
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