From: Corinna Vinschen <[email protected]>

The check is now subsumed into setup_user_rlimits().

Perform the check only if we're the root process of a Cygwin process
tree.  If we start mintty from Cygwin, the PCA trigger doesn't occur.

Signed-off-by: Corinna Vinschen <[email protected]>
---
 winsup/cygwin/dcrt0.cc                    |  2 +-
 winsup/cygwin/fork.cc                     | 13 ++++++-
 winsup/cygwin/globals.cc                  |  1 +
 winsup/cygwin/local_includes/child_info.h |  2 +-
 winsup/cygwin/resource.cc                 | 41 +++++++++++++++++++----
 winsup/cygwin/spawn.cc                    | 40 ++++++----------------
 6 files changed, 59 insertions(+), 40 deletions(-)

diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index 1d5a452b4fbc..e080aa41bca2 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -894,7 +894,7 @@ dll_crt0_1 (void *)
 
   uinfo_init ();       /* initialize user info */
 
-  setup_user_rlimits ();
+  enforce_breakaway_from_job = setup_user_rlimits (!child_proc_info);
   if (child_proc_info)
     child_proc_info->inherit_process_rlimits ();
 
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index eeed7155ee63..3e5d81fe46e8 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -152,7 +152,7 @@ frok::child (volatile char * volatile here)
   clear_procimptoken ();
   cygheap->user.reimpersonate ();
 
-  setup_user_rlimits ();
+  setup_user_rlimits (false);
   ch.inherit_process_rlimits ();
 
 #ifdef DEBUGGING
@@ -253,6 +253,17 @@ frok::parent (volatile char * volatile stack_here)
      systems. */
   c_flags |= CREATE_UNICODE_ENVIRONMENT;
 
+  /* Despite all our executables having a valid manifest, "mintty" still
+     triggers the "Program Compatibility Assistant (PCA) Service" for
+     some reason, maybe due to some heuristics in PCA.
+     We use job objects for rlimits extensively, so we still have to let
+     child processes breakaway from job.  Otherwise we can't add processes
+     running in different terminals to an already existing per-user job.
+     The check for this situation is now done in setup_user_rlimits()
+     called from dll_crt0_1(). */
+  if (enforce_breakaway_from_job)
+    c_flags |= CREATE_BREAKAWAY_FROM_JOB;
+
   errmsg = NULL;
   hchild = NULL;
 
diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc
index 86b0c2718a87..f73c35f88e7f 100644
--- a/winsup/cygwin/globals.cc
+++ b/winsup/cygwin/globals.cc
@@ -28,6 +28,7 @@ PWCHAR windows_directory = windows_directory_buf + 4;
 UINT windows_directory_length;
 UNICODE_STRING windows_directory_path;
 WCHAR global_progname[NT_MAX_PATH];
+bool NO_COPY enforce_breakaway_from_job;
 
 /* program exit the program */
 
diff --git a/winsup/cygwin/local_includes/child_info.h 
b/winsup/cygwin/local_includes/child_info.h
index f2e4fb165862..e10aa777610c 100644
--- a/winsup/cygwin/local_includes/child_info.h
+++ b/winsup/cygwin/local_includes/child_info.h
@@ -203,7 +203,7 @@ extern child_info_spawn ch_spawn;
 #define have_execed_cygwin ch_spawn.has_execed_cygwin ()
 
 /* resource.cc */
-extern void setup_user_rlimits ();
+extern bool setup_user_rlimits (bool);
 
 extern "C" {
 extern child_info *child_proc_info;
diff --git a/winsup/cygwin/resource.cc b/winsup/cygwin/resource.cc
index 83ae9add4cdb..12705eb36f78 100644
--- a/winsup/cygwin/resource.cc
+++ b/winsup/cygwin/resource.cc
@@ -376,8 +376,8 @@ child_info::inherit_process_rlimits ()
                rlimit_as.rlim_max, rlimit_as.rlim_cur);
 }
 
-static void
-__setup_user_rlimits_single (int flags)
+static bool
+__setup_user_rlimits_single (int flags, bool root_process)
 {
   JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 };
   NTSTATUS status = STATUS_SUCCESS;
@@ -385,6 +385,7 @@ __setup_user_rlimits_single (int flags)
   UNICODE_STRING uname;
   WCHAR jobname[32];
   HANDLE job = NULL;
+  bool ret = false;
 
   RtlInitUnicodeString (&uname, job_shared_name (jobname, PER_USER | flags));
   InitializeObjectAttributes (&attr, &uname, OBJ_OPENIF,
@@ -393,7 +394,7 @@ __setup_user_rlimits_single (int flags)
   if (!NT_SUCCESS (status))
     {
       debug_printf ("NtCreateJobObject (%S): status %y", &uname, status);
-      return;
+      return false;
     }
   /* Did we just create the job? */
   if (status != STATUS_OBJECT_NAME_EXISTS)
@@ -404,6 +405,28 @@ __setup_user_rlimits_single (int flags)
                                          &jobinfo, sizeof jobinfo);
     }
   NTSTATUS in_job = NtIsProcessInJob (NtCurrentProcess (), job);
+
+  /* Check if we're already running in a job, even though we're not
+     running in one of our own user-specific jobs.  If so, this process
+     is doomed, but we can try to create child processes with
+     CREATE_BREAKAWAY_FROM_JOB, so at least the next process in the
+     process tree will be happy.
+     We're only checking processes which have been started from non-Cygwin
+     processes (root processes of a Cygwin process tree).
+     Given that breaking away also requires that the foreign job allows
+     JOB_OBJECT_LIMIT_BREAKAWAY_OK, we're testing this right here, too. */
+  if ((flags & HARD_LIMIT) && root_process
+      && in_job == STATUS_PROCESS_NOT_IN_JOB)
+    {
+      status = NtQueryInformationJobObject (NULL,
+                                           JobObjectExtendedLimitInformation,
+                                           &jobinfo, sizeof jobinfo, NULL);
+      if (NT_SUCCESS (status)
+         && (jobinfo.BasicLimitInformation.LimitFlags
+             & JOB_OBJECT_LIMIT_BREAKAWAY_OK))
+       ret = true;
+    }
+
   /* Assign the process to the job if it's not already assigned. */
   if (NT_SUCCESS (status) && in_job == STATUS_PROCESS_NOT_IN_JOB)
     {
@@ -412,13 +435,17 @@ __setup_user_rlimits_single (int flags)
        debug_printf ("NtAssignProcessToJobObject: %y\r", status);
     }
   /* Never close the handle. */
+  return ret;
 }
 
-void
-setup_user_rlimits ()
+bool
+setup_user_rlimits (bool root_process)
 {
-  __setup_user_rlimits_single (HARD_LIMIT);
-  __setup_user_rlimits_single (SOFT_LIMIT);
+  bool in_a_foreign_job;
+
+  in_a_foreign_job = __setup_user_rlimits_single (HARD_LIMIT, root_process);
+  __setup_user_rlimits_single (SOFT_LIMIT, false);
+  return in_a_foreign_job;
 }
 
 extern "C" int
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index b1f7f571b36c..65f2084354a0 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -414,36 +414,16 @@ child_info_spawn::worker (const char *prog_arg, const 
char *const *argv,
       if (winjitdebug && !real_path.iscygexec ())
        c_flags |= CREATE_DEFAULT_ERROR_MODE;
 
-      /* We're adding the CREATE_BREAKAWAY_FROM_JOB flag here to workaround
-        issues with the "Program Compatibility Assistant (PCA) Service".
-        For some reason, when starting long running sessions from mintty(*),
-        the affected svchost.exe process takes more and more memory and at one
-        point takes over the CPU.  At this point the machine becomes
-        unresponsive.  The only way to get back to normal is to stop the
-        entire mintty session, or to stop the PCA service.  However, a process
-        which is controlled by PCA is part of a compatibility job, which
-        allows child processes to break away from the job.  This helps to
-        avoid this issue.
-
-        First we call IsProcessInJob.  It fetches the information whether or
-        not we're part of a job 20 times faster than QueryInformationJobObject.
-
-        (*) Note that this is not mintty's fault.  It has just been observed
-        with mintty in the first place.  See the archives for more info:
-        http://cygwin.com/ml/cygwin-developers/2012-02/msg00018.html */
-      JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo;
-      BOOL is_in_job;
-
-      if (IsProcessInJob (GetCurrentProcess (), NULL, &is_in_job)
-         && is_in_job
-         && QueryInformationJobObject (NULL, JobObjectBasicLimitInformation,
-                                    &jobinfo, sizeof jobinfo, NULL)
-         && (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK
-                                   | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
-       {
-         debug_printf ("Add CREATE_BREAKAWAY_FROM_JOB");
-         c_flags |= CREATE_BREAKAWAY_FROM_JOB;
-       }
+      /* Despite all our executables having a valid manifest, "mintty" still
+        triggers the "Program Compatibility Assistant (PCA) Service" for
+        some reason, maybe due to some heuristics in PCA.
+        We use job objects for rlimits extensively, so we still have to let
+        child processes breakaway from job.  Otherwise we can't add processes
+        running in different terminals to an already existing per-user job.
+        The check for this situation is now done in setup_user_rlimits()
+        called from dll_crt0_1(). */
+      if (enforce_breakaway_from_job)
+       c_flags |= CREATE_BREAKAWAY_FROM_JOB;
 
       if (mode == _P_DETACH)
        c_flags |= DETACHED_PROCESS;
-- 
2.52.0

Reply via email to