Division INT_MIN/-1 is undefined behavior in C and on x86 idivl instruction
generates Divide Exception. Windows in this case raises SEH exception
EXCEPTION_INT_OVERFLOW.

For 32-bit binaries this EXCEPTION_INT_OVERFLOW is not handled by
_gnu_exception_handler() and __mingw_SEH_error_handler() at all.
Case for this exception is hidden inside #ifdef _WIN64. So exception
propagates back to Windows which shows Dr. Watson dialog (if is not
disabled) and kill the process.

For 64-bit binaries this exception in those two functions is handled,
silently ignored and then instructed Windows to restart that fault
instruction. So this logic cause an infinite loop in application without
showing any error and without propagating error back to Windows.

On Linux, this x86 Divide Exception for both 32-bit and 64-bit is
translated to SIGFPE signal for which C application can register handler.

Fix the problem of x86-64 infinite loop by handling the
EXCEPTION_INT_OVERFLOW via SIGFPE signal if is registered by application.
If is not registered then propagate exception back to Windows which could
launch Dr. Watson or kill the process.

This changes also behavior for 32-bit builds as it allows to use SIGFPE too.

Bug: 
https://stackoverflow.com/questions/25363379/different-results-for-idiv-instruction
---
 mingw-w64-crt/crt/crt_handler.c     |  4 ++--
 mingw-w64-crt/testcases/Makefile.am |  1 +
 mingw-w64-crt/testcases/t_sigfpe.c  | 31 +++++++++++++++++++++++++++++
 3 files changed, 34 insertions(+), 2 deletions(-)
 create mode 100644 mingw-w64-crt/testcases/t_sigfpe.c

diff --git a/mingw-w64-crt/crt/crt_handler.c b/mingw-w64-crt/crt/crt_handler.c
index 4564d6df396d..c10f14efe60f 100644
--- a/mingw-w64-crt/crt/crt_handler.c
+++ b/mingw-w64-crt/crt/crt_handler.c
@@ -133,6 +133,7 @@ __mingw_SEH_error_handler (struct _EXCEPTION_RECORD* 
ExceptionRecord,
       /* fall through. */
 
     case EXCEPTION_INT_DIVIDE_BY_ZERO:
+    case EXCEPTION_INT_OVERFLOW:
       /* test if the user has set SIGFPE */
       old_handler = signal (SIGFPE, SIG_DFL);
       if (old_handler == SIG_IGN)
@@ -152,7 +153,6 @@ __mingw_SEH_error_handler (struct _EXCEPTION_RECORD* 
ExceptionRecord,
     case EXCEPTION_DATATYPE_MISALIGNMENT:
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
     case EXCEPTION_FLT_STACK_CHECK:
-    case EXCEPTION_INT_OVERFLOW:
     case EXCEPTION_INVALID_HANDLE:
     /*case EXCEPTION_POSSIBLE_DEADLOCK: */
       action = ExceptionContinueExecution;
@@ -236,6 +236,7 @@ _gnu_exception_handler (EXCEPTION_POINTERS *exception_data)
       /* fall through. */
 
     case EXCEPTION_INT_DIVIDE_BY_ZERO:
+    case EXCEPTION_INT_OVERFLOW:
       /* test if the user has set SIGFPE */
       old_handler = signal (SIGFPE, SIG_DFL);
       if (old_handler == SIG_IGN)
@@ -256,7 +257,6 @@ _gnu_exception_handler (EXCEPTION_POINTERS *exception_data)
     case EXCEPTION_DATATYPE_MISALIGNMENT:
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
     case EXCEPTION_FLT_STACK_CHECK:
-    case EXCEPTION_INT_OVERFLOW:
     case EXCEPTION_INVALID_HANDLE:
     /*case EXCEPTION_POSSIBLE_DEADLOCK: */
       action = EXCEPTION_CONTINUE_EXECUTION;
diff --git a/mingw-w64-crt/testcases/Makefile.am 
b/mingw-w64-crt/testcases/Makefile.am
index 14c59abc2196..f9efab6b8a02 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -56,6 +56,7 @@ testcase_progs = \
   t_stprintf1_a \
   t_stprintf1_u \
   t_setjmp \
+  t_sigfpe \
   t_sigv \
   t_speed_powl \
   t_stat \
diff --git a/mingw-w64-crt/testcases/t_sigfpe.c 
b/mingw-w64-crt/testcases/t_sigfpe.c
new file mode 100644
index 000000000000..eda858d373f9
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_sigfpe.c
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <assert.h>
+#include <limits.h>
+
+static jmp_buf buf;
+
+static void __attribute__((noreturn)) catch_sigfpe(int signum)
+{
+  printf("SIGFPE exception catched\n");
+  assert(signum == SIGFPE);
+  longjmp(buf, 1);
+}
+
+int main(void)
+{
+  signal(SIGFPE, catch_sigfpe);
+  if (!setjmp(buf))
+  {
+    puts("execute: INT_MIN/-1");
+    volatile int a = INT_MIN;
+    volatile int b = -1;
+    volatile int c = a / b;
+    (void)c;
+    puts("FAILED, program continued");
+    return 1;
+  }
+  puts("PASSED, program recovered");
+  return 0;
+}
-- 
2.20.1



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to