diff --git a/winsup/cygwin/resource.cc b/winsup/cygwin/resource.cc index c4c79ca6f..21e85bd18 100644 --- a/winsup/cygwin/resource.cc +++ b/winsup/cygwin/resource.cc @@ -174,6 +174,87 @@ job_shared_name (PWCHAR buf, LONG num) return buf; } +static void +__get_rlimit_as (struct rlimit *rlp) +{ + UNICODE_STRING uname; + WCHAR jobname[32]; + OBJECT_ATTRIBUTES attr; + HANDLE job = NULL; + NTSTATUS status; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo; + + if (cygheap->rlim_as_id) + { + RtlInitUnicodeString (&uname, + job_shared_name (jobname, + cygheap->rlim_as_id)); + InitializeObjectAttributes (&attr, &uname, 0, + get_session_parent_dir (), NULL); + /* May fail, just check NULL job in that case. */ + NtOpenJobObject (&job, JOB_OBJECT_QUERY, &attr); + } + status = NtQueryInformationJobObject (job, + JobObjectExtendedLimitInformation, + &jobinfo, sizeof jobinfo, NULL); + if (NT_SUCCESS (status) + && (jobinfo.BasicLimitInformation.LimitFlags + & JOB_OBJECT_LIMIT_PROCESS_MEMORY)) + rlp->rlim_cur = rlp->rlim_max = jobinfo.ProcessMemoryLimit; + if (job) + NtClose (job); +} + +static int +__set_rlimit_as (unsigned long new_as_limit) +{ + LONG new_as_id = 0; + UNICODE_STRING uname; + WCHAR jobname[32]; + OBJECT_ATTRIBUTES attr; + NTSTATUS status = STATUS_SUCCESS; + HANDLE job = NULL; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 }; + + /* If we already have a limit, we must not change it because that + would potentially influence already running child processes. + Just try to create another, nested job. On systems prior to + Windows 8 / Server 2012 this will fail, but that's ok. */ + while (new_as_id == 0) + new_as_id = InterlockedIncrement (&job_serial_number); + RtlInitUnicodeString (&uname, + job_shared_name (jobname, new_as_id)); + InitializeObjectAttributes (&attr, &uname, 0, + get_session_parent_dir (), NULL); + status = NtCreateJobObject (&job, JOB_OBJECT_ALL_ACCESS, &attr); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + jobinfo.BasicLimitInformation.LimitFlags + = JOB_OBJECT_LIMIT_PROCESS_MEMORY; + /* Per Linux man page, round down to system pagesize. */ + jobinfo.ProcessMemoryLimit + = rounddown (new_as_limit, wincap.allocation_granularity ()); + status = NtSetInformationJobObject (job, + JobObjectExtendedLimitInformation, + &jobinfo, sizeof jobinfo); + /* If creating the job and setting up the job limits succeeded, + try to add the process to the job. This must be the last step, + otherwise we couldn't remove the job if anything failed. */ + if (NT_SUCCESS (status)) + status = NtAssignProcessToJobObject (job, NtCurrentProcess ()); + NtClose (job); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + cygheap->rlim_as_id = new_as_id; + return 0; +} + extern "C" int getrlimit (int resource, struct rlimit *rlp) { @@ -189,34 +270,7 @@ getrlimit (int resource, struct rlimit *rlp) case RLIMIT_DATA: break; case RLIMIT_AS: - { - UNICODE_STRING uname; - WCHAR jobname[32]; - OBJECT_ATTRIBUTES attr; - HANDLE job = NULL; - NTSTATUS status; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo; - - if (cygheap->rlim_as_id) - { - RtlInitUnicodeString (&uname, - job_shared_name (jobname, - cygheap->rlim_as_id)); - InitializeObjectAttributes (&attr, &uname, 0, - get_session_parent_dir (), NULL); - /* May fail, just check NULL job in that case. */ - NtOpenJobObject (&job, JOB_OBJECT_QUERY, &attr); - } - status = NtQueryInformationJobObject (job, - JobObjectExtendedLimitInformation, - &jobinfo, sizeof jobinfo, NULL); - if (!NT_SUCCESS (status)) - break; - if (jobinfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) - rlp->rlim_cur = rlp->rlim_max = jobinfo.ProcessMemoryLimit; - if (job) - NtClose (job); - } + __get_rlimit_as (rlp); break; case RLIMIT_STACK: __get_rlimit_stack (rlp); @@ -270,51 +324,8 @@ setrlimit (int resource, const struct rlimit *rlp) switch (resource) { case RLIMIT_AS: - { - LONG new_as_id = 0; - UNICODE_STRING uname; - WCHAR jobname[32]; - OBJECT_ATTRIBUTES attr; - NTSTATUS status = STATUS_SUCCESS; - HANDLE job = NULL; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 }; - - /* If we already have a limit, we must not change it because that - would potentially influence already running child processes. - Just try to create another, nested job. On systems prior to - Windows 8 / Server 2012 this will fail, but that's ok. */ - while (new_as_id == 0) - new_as_id = InterlockedIncrement (&job_serial_number); - RtlInitUnicodeString (&uname, - job_shared_name (jobname, new_as_id)); - InitializeObjectAttributes (&attr, &uname, 0, - get_session_parent_dir (), NULL); - status = NtCreateJobObject (&job, JOB_OBJECT_ALL_ACCESS, &attr); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - __leave; - } - status = NtAssignProcessToJobObject (job, NtCurrentProcess ()); - if (NT_SUCCESS (status)) - { - jobinfo.BasicLimitInformation.LimitFlags - = JOB_OBJECT_LIMIT_PROCESS_MEMORY; - /* Per Linux man page, round down to system pagesize. */ - jobinfo.ProcessMemoryLimit - = rounddown (rlp->rlim_cur, wincap.allocation_granularity ()); - status = NtSetInformationJobObject (job, - JobObjectExtendedLimitInformation, - &jobinfo, sizeof jobinfo); - } - NtClose (job); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - __leave; - } - cygheap->rlim_as_id = new_as_id; - } + if (rlp->rlim_cur != RLIM_INFINITY) + return __set_rlimit_as (rlp->rlim_cur); break; case RLIMIT_CORE: cygheap->rlim_core = rlp->rlim_cur;