Restore SEM_FAILCRITICALERRORS [was: Aren't Windows System Error popups meant to be disabled in Cygwin?]

Corinna Vinschen corinna-cygwin@cygwin.com
Sat Feb 3 12:39:31 GMT 2024


On Feb  2 19:51, Corinna Vinschen via Cygwin wrote:
> On Feb  2 18:22, Corinna Vinschen via Cygwin wrote:
> > On Feb  2 14:56, David Allsopp via Cygwin wrote:
> > > On Fri, 2 Feb 2024 at 14:18, Corinna Vinschen via Cygwin wrote:
> > > > Is it actually a safe bet that the error mode set by SetThreadErrorMode
> > > > is then propagated as process error mode to the child process?
> > > >
> > > > I have to ask that because Microsoft conveniently forgot to document
> > > > this scenario in the MSDN docs.
> > > 
> > > :o) Never knowingly clear, are they! It would seem to be the intent of
> > > SetThreadErrorMode that it would behave that way but who knows.
> > > 
> > > Happy to set up a quick experiment to check that it does work (i.e.
> > > [...]
> > Wanna try this?
> [...]
> However, it occured to me that this won't work at all.
> [...]

Sorry to say that, but SetThreadErrorMode/CreateProcess don't do what we
want them to do.  I just tested this myself with a modified Cygwin DLL
(code below) and it turns out that the child process error mode is
the same as the parent's process error mode.  Changing the thread
error mode from the Cygwin default 3 (aka SEM_FAILCRITICALERRORS |
SEM_NOGPFAULTERRORBOX) to 0 doesn't have any effect.

Check the below Cygwin patch and look at output ("gem" is my MingW test
app just printing the process error mode retrieved via GetErrorMode):

  $ ./gem
	0 [main] dash 990 child_info_spawn::worker: 1 = SetThreadErrorMode (0, 0) GT:0 GP:3
    16158 [main] dash 990 child_info_spawn::worker: 1 = SetThreadErrorMode (3, 0)
  Error mode 0x3
  $

The terrible thing here is the output of the old thread error mode
from GetThreadErrorMode as well as from SetThreadErrorMode.

MSDN says:

  Each process has an associated error mode that indicates to the system
  how the application is going to respond to serious errors. A thread
  inherits the error mode of the process in which it is running. To
  retrieve the process error mode, use the GetErrorMode function. To
  retrieve the error mode of the calling thread, use the
  GetThreadErrorMode function.

What that means is, even though the process error mode is 3 , and even
though "A thread inherits the error mode of the process in which it is
running", GetThreadErrorMode/SetThreadErrorMode return a thread error
code of 0!!!

So if GetThreadErrorMode returns 0, you *have* to call GetErrorMode to
retrieve the *actual* thread error mode, because the thread error mode
just says "yo man, it's default".

In extension this probably *also* means, setting the thread error mode
to 0 does NOT mean "set it to system default" as MSDN claims, but it
means "set it to process default".

But in fact, even if I set a non-0 thread error mode, this has no effect
on the child process.  I forced the thread error mode to 1 before calling
CreateProcess, and the resulting child process error mode was still the
Cygwin process error mode 3.

Isn't that completely screwed up?

Ok, my Cygwin DLL test patch follows below.


Corinna


diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index a40129c22232..14ba4e3769f1 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -718,7 +718,8 @@ dll_crt0_0 ()
   init_windows_system_directory ();
   initial_env ();
 
-  SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+  UINT proc_error_mode = SetErrorMode (SEM_FAILCRITICALERRORS
+				       | SEM_NOGPFAULTERRORBOX);
 
   lock_process::init ();
   user_data->impure_ptr = _impure_ptr;
@@ -738,6 +739,13 @@ dll_crt0_0 ()
   if (!child_proc_info)
     {
       setup_cygheap ();
+      /* Memorize the original error mode when this Cygwin process
+	 has been called from a non-Cygwin process.  We restore to
+	 this error mode on spawning a non-Cygwin process.  This allows
+	 to set a non-default error mode prior to calling the first
+	 Cygwin process and forward it to any subsequent non-Cygwin
+	 child process at spawn time. */
+      cygheap->orig_proc_error_mode = proc_error_mode;
       memory_init ();
     }
   else
diff --git a/winsup/cygwin/local_includes/cygheap.h b/winsup/cygwin/local_includes/cygheap.h
index b6acdf7f18b7..02e3cb4621e3 100644
--- a/winsup/cygwin/local_includes/cygheap.h
+++ b/winsup/cygwin/local_includes/cygheap.h
@@ -517,6 +517,7 @@ struct init_cygheap: public mini_cygheap
   mode_t umask;
   LONG rlim_as_id;
   unsigned long rlim_core;
+  UINT orig_proc_error_mode; /* Set when started from non-Cygwin process */
   HANDLE console_h;
   cwdstuff cwd;
   dtable fdtab;
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 8a2db5cf72e2..85dbec431b28 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -401,13 +401,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 
       c_flags |= CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT;
 
-      /* Add CREATE_DEFAULT_ERROR_MODE flag for non-Cygwin processes so they
-	 get the default error mode instead of inheriting the mode Cygwin
-	 uses.  This allows things like Windows Error Reporting/JIT debugging
-	 to work with processes launched from a Cygwin shell. */
-      if (!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(*),
@@ -648,6 +641,17 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	      && !::cygheap->user.groups.issetgroups ()
 	      && !::cygheap->user.setuid_to_restricted))
 	{
+	  if (!iscygwin ())
+	    {
+	      UINT old, old2, old3;
+	      BOOL ret;
+
+	      old2 = GetThreadErrorMode ();
+	      old3 = GetErrorMode ();
+	      ret = SetThreadErrorMode (cygheap->orig_proc_error_mode, &old);
+	      system_printf ("%d = SetThreadErrorMode (%u, %u) GT:%u GP:%u\r\n",
+			     ret, cygheap->orig_proc_error_mode, old, old2, old3);
+	    }
 	  rc = CreateProcessW (runpath,		/* image name w/ full path */
 			       cmd.wcs (wcmd),	/* what was passed to exec */
 			       sa,		/* process security attrs */
@@ -658,6 +662,19 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       NULL,
 			       &si,
 			       &pi);
+	  if (!iscygwin ())
+	    {
+	      UINT old;
+	      BOOL ret;
+
+	      ret = SetThreadErrorMode (SEM_FAILCRITICALERRORS
+					| SEM_NOGPFAULTERRORBOX,
+					&old);
+	      system_printf ("%d = SetThreadErrorMode (%u, %u)\r\n",
+			     ret,
+			     SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX,
+			     old);
+	    }
 	}
       else
 	{


More information about the Cygwin mailing list