From: Corinna Vinschen <[email protected]> Implement RLIMIT_NPROC by creating per-user job objects. Those are not auto-breakaway. Only breakaway explicitely from per-user jobs if we change the user context in child_info_spawn::worker(). Only then the real uid changes and the new child has to be assigned to the per-user job objects for the new real uid.
Signed-off-by: Corinna Vinschen <[email protected]> --- winsup/cygwin/dcrt0.cc | 1 + winsup/cygwin/fork.cc | 1 + winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/include/sys/resource.h | 3 +- winsup/cygwin/local_includes/child_info.h | 3 + winsup/cygwin/resource.cc | 99 +++++++++++++++++++++++ winsup/cygwin/spawn.cc | 2 + 7 files changed, 110 insertions(+), 2 deletions(-) diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index c9fdcb4b3c72..1d5a452b4fbc 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -894,6 +894,7 @@ dll_crt0_1 (void *) uinfo_init (); /* initialize user info */ + setup_user_rlimits (); if (child_proc_info) child_proc_info->inherit_process_rlimits (); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 463fa54d0beb..eeed7155ee63 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -152,6 +152,7 @@ frok::child (volatile char * volatile here) clear_procimptoken (); cygheap->user.reimpersonate (); + setup_user_rlimits (); ch.inherit_process_rlimits (); #ifdef DEBUGGING diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 00eedeb27ab4..ef552ffcba9c 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -499,12 +499,13 @@ details. */ 358: Export acl_get_fd_np, acl_get_link_np, acl_get_perm_np, acl_is_trivial_np, acl_set_fd_np, acl_set_link_np, acl_strip_np. 359: Export wrappers for C++14 and C++17 new and delete overloads. + 360: Add RLIMIT_NPROC. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 359 +#define CYGWIN_VERSION_API_MINOR 360 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/include/sys/resource.h b/winsup/cygwin/include/sys/resource.h index 16dcdcd0a2d3..56fb4a897426 100644 --- a/winsup/cygwin/include/sys/resource.h +++ b/winsup/cygwin/include/sys/resource.h @@ -28,8 +28,9 @@ extern "C" { #define RLIMIT_NOFILE 5 /* max number of open files */ #define RLIMIT_OFILE RLIMIT_NOFILE /* BSD name */ #define RLIMIT_AS 6 /* address space (virt. memory) limit */ +#define RLIMIT_NPROC 7 /* max processes for this user */ -#define RLIMIT_NLIMITS 7 /* upper bound of RLIMIT_* defines */ +#define RLIMIT_NLIMITS 8 /* upper bound of RLIMIT_* defines */ #define RLIM_NLIMITS RLIMIT_NLIMITS #define RLIM_INFINITY (~0UL) diff --git a/winsup/cygwin/local_includes/child_info.h b/winsup/cygwin/local_includes/child_info.h index aaa3e936f229..f2e4fb165862 100644 --- a/winsup/cygwin/local_includes/child_info.h +++ b/winsup/cygwin/local_includes/child_info.h @@ -202,6 +202,9 @@ extern child_info_spawn ch_spawn; #define have_execed ch_spawn.has_execed () #define have_execed_cygwin ch_spawn.has_execed_cygwin () +/* resource.cc */ +extern void setup_user_rlimits (); + extern "C" { extern child_info *child_proc_info; extern child_info_spawn *spawn_info asm (_SYMSTR (child_proc_info)); diff --git a/winsup/cygwin/resource.cc b/winsup/cygwin/resource.cc index 2286fe85052b..83ae9add4cdb 100644 --- a/winsup/cygwin/resource.cc +++ b/winsup/cygwin/resource.cc @@ -308,6 +308,54 @@ __set_rlimit_as (const struct rlimit *rlp) return ret; } +static int +__set_rlimit_nproc_single (rlim_t val, int flags) +{ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 }; + + __get_os_limits (jobinfo, PER_USER | flags); + /* ActiveProcessLimit is a DWORD */ + if (val == RLIM_INFINITY || val > UINT_MAX) + { + jobinfo.BasicLimitInformation.LimitFlags + &= ~JOB_OBJECT_LIMIT_ACTIVE_PROCESS; + jobinfo.BasicLimitInformation.ActiveProcessLimit = 0; + } + else /* Per Linux man page, round down to system pagesize. */ + { + jobinfo.BasicLimitInformation.LimitFlags + |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; + jobinfo.BasicLimitInformation.ActiveProcessLimit = val; + } + return __set_os_limits (jobinfo, PER_USER | flags); +} + +int +__set_rlimit_nproc (const struct rlimit *rlp) +{ + int ret = __set_rlimit_nproc_single (rlp->rlim_max, HARD_LIMIT); + if (ret == 0) + ret = __set_rlimit_nproc_single (rlp->rlim_cur, SOFT_LIMIT); + return ret; +} + +void +__get_rlimit_nproc (struct rlimit *rlp) +{ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo; + + rlp->rlim_cur = RLIM_INFINITY; + rlp->rlim_max = RLIM_INFINITY; + if (__get_os_limits (jobinfo, PER_USER | HARD_LIMIT) + && (jobinfo.BasicLimitInformation.LimitFlags + & JOB_OBJECT_LIMIT_ACTIVE_PROCESS)) + rlp->rlim_max = jobinfo.BasicLimitInformation.ActiveProcessLimit; + if (__get_os_limits (jobinfo, PER_USER | SOFT_LIMIT) + && (jobinfo.BasicLimitInformation.LimitFlags + & JOB_OBJECT_LIMIT_ACTIVE_PROCESS)) + rlp->rlim_cur = jobinfo.BasicLimitInformation.ActiveProcessLimit; +} + /* Called during fork/exec in the parent to collect the per-process rlimits. */ void child_info::collect_process_rlimits () @@ -328,6 +376,51 @@ child_info::inherit_process_rlimits () rlimit_as.rlim_max, rlimit_as.rlim_cur); } +static void +__setup_user_rlimits_single (int flags) +{ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 }; + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING uname; + WCHAR jobname[32]; + HANDLE job = NULL; + + RtlInitUnicodeString (&uname, job_shared_name (jobname, PER_USER | flags)); + InitializeObjectAttributes (&attr, &uname, OBJ_OPENIF, + get_shared_parent_dir (), NULL); + status = NtCreateJobObject (&job, JOB_OBJECT_ALL_ACCESS, &attr); + if (!NT_SUCCESS (status)) + { + debug_printf ("NtCreateJobObject (%S): status %y", &uname, status); + return; + } + /* Did we just create the job? */ + if (status != STATUS_OBJECT_NAME_EXISTS) + { + jobinfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; + status = NtSetInformationJobObject (job, + JobObjectExtendedLimitInformation, + &jobinfo, sizeof jobinfo); + } + NTSTATUS in_job = NtIsProcessInJob (NtCurrentProcess (), job); + /* Assign the process to the job if it's not already assigned. */ + if (NT_SUCCESS (status) && in_job == STATUS_PROCESS_NOT_IN_JOB) + { + status = NtAssignProcessToJobObject (job, NtCurrentProcess ()); + if (!NT_SUCCESS (status)) + debug_printf ("NtAssignProcessToJobObject: %y\r", status); + } + /* Never close the handle. */ +} + +void +setup_user_rlimits () +{ + __setup_user_rlimits_single (HARD_LIMIT); + __setup_user_rlimits_single (SOFT_LIMIT); +} + extern "C" int getrlimit (int resource, struct rlimit *rlp) { @@ -345,6 +438,9 @@ getrlimit (int resource, struct rlimit *rlp) case RLIMIT_AS: __get_rlimit_as (rlp); break; + case RLIMIT_NPROC: + __get_rlimit_nproc (rlp); + break; case RLIMIT_STACK: __get_rlimit_stack (rlp); break; @@ -401,6 +497,9 @@ setrlimit (int resource, const struct rlimit *rlp) case RLIMIT_AS: return __set_rlimit_as (rlp); break; + case RLIMIT_NPROC: + return __set_rlimit_nproc (rlp); + break; case RLIMIT_CORE: cygheap->rlim_core = rlp->rlim_cur; break; diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index f7ed9cf4f70b..b1f7f571b36c 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -676,6 +676,8 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, } } + c_flags |= CREATE_BREAKAWAY_FROM_JOB; + rc = CreateProcessAsUserW (::cygheap->user.primary_token (), runpath, /* image name w/ full path */ cmd.wcs (wcmd), /* what was passed to exec */ -- 2.52.0
