From 726bfb1d1c5a31b75250c3c9ccc8894195332d63 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 1 Oct 2023 20:18:23 +0200 Subject: [PATCH] Avoid crash by SIGFPE during plural form expression evaluation. Libraries (such as libintl and libgettextpo) should not raise signals, because catching such signals cannot be done in a multithread-safe way. * gettext-runtime/intl/plural-exp.h (enum eval_status): New enum items PE_INTDIV, PE_INTOVF. * gettext-runtime/intl/eval-plural.h (plural_eval_recurse): When a division argument is zero, return status PE_INTDIV instead of raising SIGFPE. * gettext-runtime/intl/dcigettext.c (INTDIV0_RAISES_SIGFPE): Remove macro. Don't include . * gettext-runtime/intl/configure.ac: Don't invoke gt_INTDIV0. * gettext-runtime/intl/m4/intdiv0.m4: Remove file. * gettext-runtime/intl/Makefile.am (ACLOCAL_AMFLAGS): Remove option '-I m4'. (EXTRA_DIST): Remove m4/intdiv0.m4. * autogen.sh: When invoking aclocal in gettext-runtime/intl, omit option '-I m4'. * gettext-tools/src/msgl-check.c: Don't include , . (plural_expression_histogram): Don't invoke install_sigfpe_handler, uninstall_sigfpe_handler. (check_plural_eval): Don't invoke setjmp, install_sigfpe_handler, uninstall_sigfpe_handler. Remove 'volatile' from array variable. Produce error message when status PE_INTDIV or PE_INTOVF occurred. * gettext-tools/src/plural-eval.h: Don't include . (sigjmp_buf, sigsetjmp, siglongjmp): Remove definitions. (USE_SIGINFO): Remove macro. (sigfpe_exit, sigfpe_code, install_sigfpe_handler, uninstall_sigfpe_handler): Remove declarations. * gettext-tools/src/plural-eval.c: Don't include . (sigfpe_exit, sigfpe_code): Remove variables. (sigfpe_handler, install_sigfpe_handler, uninstall_sigfpe_handler): Remove functions. * gettext-tools/configure.ac: Don't invoke gt_SIGINFO. * gettext-tools/m4/siginfo.m4: Remove file. * gettext-tools/m4/Makefile.am (EXTRA_DIST): Remove it. --- autogen.sh | 2 +- gettext-runtime/intl/Makefile.am | 5 +- gettext-runtime/intl/configure.ac | 1 - gettext-runtime/intl/dcigettext.c | 14 --- gettext-runtime/intl/eval-plural.h | 8 +- gettext-runtime/intl/m4/intdiv0.m4 | 90 ----------------- gettext-runtime/intl/plural-exp.h | 2 + gettext-tools/configure.ac | 1 - gettext-tools/m4/Makefile.am | 3 +- gettext-tools/m4/siginfo.m4 | 53 ---------- gettext-tools/src/msgl-check.c | 157 ++++++++++------------------- gettext-tools/src/plural-eval.c | 67 +----------- gettext-tools/src/plural-eval.h | 40 -------- 13 files changed, 60 insertions(+), 383 deletions(-) delete mode 100644 gettext-runtime/intl/m4/intdiv0.m4 delete mode 100644 gettext-tools/m4/siginfo.m4 diff --git a/autogen.sh b/autogen.sh index 6df9cf4cd..094fe9756 100755 --- a/autogen.sh +++ b/autogen.sh @@ -438,7 +438,7 @@ dir0=`pwd` echo "$0: generating configure in gettext-runtime/intl..." cd gettext-runtime/intl -aclocal -I ../../m4 -I ../m4 -I m4 -I gnulib-m4 \ +aclocal -I ../../m4 -I ../m4 -I gnulib-m4 \ && autoconf \ && autoheader && touch config.h.in \ && touch ChangeLog \ diff --git a/gettext-runtime/intl/Makefile.am b/gettext-runtime/intl/Makefile.am index ea166277b..4080b1f8a 100644 --- a/gettext-runtime/intl/Makefile.am +++ b/gettext-runtime/intl/Makefile.am @@ -18,12 +18,11 @@ ## Process this file with automake to produce Makefile.in. AUTOMAKE_OPTIONS = 1.10 gnu no-dependencies -ACLOCAL_AMFLAGS = -I ../../m4 -I ../m4 -I m4 -I gnulib-m4 +ACLOCAL_AMFLAGS = -I ../../m4 -I ../m4 -I gnulib-m4 SUBDIRS = gnulib-lib -EXTRA_DIST = \ - m4/intdiv0.m4 +EXTRA_DIST = BUILT_SOURCES = MOSTLYCLEANFILES = core *.stackdump CLEANFILES = diff --git a/gettext-runtime/intl/configure.ac b/gettext-runtime/intl/configure.ac index 190d9a8bf..b68c4000e 100644 --- a/gettext-runtime/intl/configure.ac +++ b/gettext-runtime/intl/configure.ac @@ -118,7 +118,6 @@ AC_C_INLINE AC_TYPE_SIZE_T AC_FUNC_ALLOCA AC_FUNC_MMAP -gt_INTDIV0 gl_LOCK AC_LINK_IFELSE( diff --git a/gettext-runtime/intl/dcigettext.c b/gettext-runtime/intl/dcigettext.c index 7bdc7a760..56bcea2fc 100644 --- a/gettext-runtime/intl/dcigettext.c +++ b/gettext-runtime/intl/dcigettext.c @@ -69,20 +69,6 @@ extern int errno; #include -#ifdef _LIBC - /* Guess whether integer division by zero raises signal SIGFPE. - Set to 1 only if you know for sure. In case of doubt, set to 0. */ -# if defined __alpha__ || defined __arm__ || defined __i386__ \ - || defined __m68k__ || defined __s390__ -# define INTDIV0_RAISES_SIGFPE 1 -# else -# define INTDIV0_RAISES_SIGFPE 0 -# endif -#endif -#if !INTDIV0_RAISES_SIGFPE -# include -#endif - #if defined HAVE_SYS_PARAM_H || defined _LIBC # include #endif diff --git a/gettext-runtime/intl/eval-plural.h b/gettext-runtime/intl/eval-plural.h index 90eb34050..d709642cc 100644 --- a/gettext-runtime/intl/eval-plural.h +++ b/gettext-runtime/intl/eval-plural.h @@ -117,16 +117,12 @@ plural_eval_recurse (const struct expression *pexp, unsigned long int n, case mult: return OK (leftarg.value * rightarg.value); case divide: -#if !INTDIV0_RAISES_SIGFPE if (rightarg.value == 0) - raise (SIGFPE); -#endif + return (struct eval_result) { .status = PE_INTDIV }; return OK (leftarg.value / rightarg.value); case module: -#if !INTDIV0_RAISES_SIGFPE if (rightarg.value == 0) - raise (SIGFPE); -#endif + return (struct eval_result) { .status = PE_INTDIV }; return OK (leftarg.value % rightarg.value); case plus: return OK (leftarg.value + rightarg.value); diff --git a/gettext-runtime/intl/m4/intdiv0.m4 b/gettext-runtime/intl/m4/intdiv0.m4 deleted file mode 100644 index 092a406de..000000000 --- a/gettext-runtime/intl/m4/intdiv0.m4 +++ /dev/null @@ -1,90 +0,0 @@ -# intdiv0.m4 serial 9 (gettext-0.21.1) -dnl Copyright (C) 2002, 2007-2008, 2010-2020 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Bruno Haible. - -AC_DEFUN([gt_INTDIV0], -[ - AC_REQUIRE([AC_PROG_CC])dnl - AC_REQUIRE([AC_CANONICAL_HOST])dnl - - AC_CACHE_CHECK([whether integer division by zero raises SIGFPE], - [gt_cv_int_divbyzero_sigfpe], - [ - gt_cv_int_divbyzero_sigfpe= -changequote(,)dnl - case "$host_os" in - macos* | darwin[6-9]* | darwin[1-9][0-9]*) - # On Mac OS X 10.2 or newer, just assume the same as when cross- - # compiling. If we were to perform the real test, 1 Crash Report - # dialog window would pop up. - case "$host_cpu" in - i[34567]86 | x86_64) - gt_cv_int_divbyzero_sigfpe="guessing yes" ;; - esac - ;; - esac -changequote([,])dnl - if test -z "$gt_cv_int_divbyzero_sigfpe"; then - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include /* for exit() */ -#include -#if !(defined _WIN32 && !defined __CYGWIN__) -#include /* for _exit() */ -#endif - -static void -sigfpe_handler (int sig) -{ - /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */ - _exit (sig != SIGFPE); -} - -int x = 1; -int y = 0; -int z; -int inan; - -int main () -{ - signal (SIGFPE, sigfpe_handler); -/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */ -#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP) - signal (SIGTRAP, sigfpe_handler); -#endif -/* Linux/SPARC yields signal SIGILL. */ -#if defined (__sparc__) && defined (__linux__) - signal (SIGILL, sigfpe_handler); -#endif - - z = x / y; - inan = y / y; - exit (2); -} -]])], - [gt_cv_int_divbyzero_sigfpe=yes], - [gt_cv_int_divbyzero_sigfpe=no], - [ - # Guess based on the CPU. -changequote(,)dnl - case "$host_cpu" in - alpha* | i[34567]86 | x86_64 | m68k | s390*) - gt_cv_int_divbyzero_sigfpe="guessing yes";; - *) - gt_cv_int_divbyzero_sigfpe="guessing no";; - esac -changequote([,])dnl - ]) - fi - ]) - case "$gt_cv_int_divbyzero_sigfpe" in - *yes) value=1 ;; - *) value=0 ;; - esac - AC_DEFINE_UNQUOTED([INTDIV0_RAISES_SIGFPE], [$value], - [Define if integer division by zero raises signal SIGFPE.]) -]) diff --git a/gettext-runtime/intl/plural-exp.h b/gettext-runtime/intl/plural-exp.h index 7eb9aed8b..16080d63d 100644 --- a/gettext-runtime/intl/plural-exp.h +++ b/gettext-runtime/intl/plural-exp.h @@ -117,6 +117,8 @@ extern void EXTRACT_PLURAL_EXPRESSION (const char *nullentry, enum eval_status { PE_OK, /* Evaluation succeeded, produced a value */ + PE_INTDIV, /* Integer division by zero */ + PE_INTOVF, /* Integer overflow */ PE_STACKOVF, /* Stack overflow */ PE_ASSERT /* Assertion failure */ }; diff --git a/gettext-tools/configure.ac b/gettext-tools/configure.ac index ff758dbbb..74e92ca56 100644 --- a/gettext-tools/configure.ac +++ b/gettext-tools/configure.ac @@ -231,7 +231,6 @@ AC_TYPE_PID_T dnl Checks for library functions. AC_CHECK_FUNCS([select]) -gt_SIGINFO AC_C_BIGENDIAN([endianness=1], [endianness=0], [echo "AC-C-BIGENDIAN fails to work on your system." | sed -e 's,-,_,g' 1>&2 diff --git a/gettext-tools/m4/Makefile.am b/gettext-tools/m4/Makefile.am index 469de03a6..702309e7a 100644 --- a/gettext-tools/m4/Makefile.am +++ b/gettext-tools/m4/Makefile.am @@ -46,5 +46,4 @@ EXTRA_DIST = \ csharpexec-test.cs csharpexec-test.exe \ exported.m4 \ hostname.m4 \ - locale-de.m4 \ - siginfo.m4 + locale-de.m4 diff --git a/gettext-tools/m4/siginfo.m4 b/gettext-tools/m4/siginfo.m4 deleted file mode 100644 index f39f6b5ac..000000000 --- a/gettext-tools/m4/siginfo.m4 +++ /dev/null @@ -1,53 +0,0 @@ -# siginfo.m4 serial 2 (gettext-0.21.1) -dnl Copyright (C) 2001-2002, 2020 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -# Determine how to determine the precise cause of a signal, for example -# division by zero. -# - SUSV2 and POSIX specify the use of sigaction with SA_SIGINFO and a member -# void (*)(int sig, siginfo_t *info, void *context) sa_sigaction. -# Linux (2.2.x and newer) and Solaris implement this. -# Linux (2.4.x and newer) on i386, m68k, sparc, sparc64, ia64 actually -# deliver FPE_INTDIV. -# - Without SA_SIGINFO: -# - Linux on m68k calls the handler as -# void (*)(int sig, int code, struct sigcontext* scp). -# For division by zero, code would be VEC_ZERODIV<<2. -# - Linux on sparc calls the handler either as -# void (*)(int sig, int code, struct sigcontext* scp), -# code for division by zero would be SUBSIG_IDIVZERO, or as -# void (*)(int sig, siginfo_t *info, void *context). -# Which one depends on a process specific flag in the kernel. -# - Linux on sparc64 always calls the handler as -# void (*)(int sig, siginfo_t *info, void *context). -# - FreeBSD on i386 calls the handler as -# void (*)(int sig, int code, void* scp, char* addr). -# For division by zero, code would be FPE_INTDIV. -# - SunOS 4 calls the handler as -# void (*)(int sig, int code, void* scp, char* addr). -# - Solaris? -# - Irix 5, OSF/1, AIX call the handler as -# void (*)(int sig, int code, struct sigcontext *scp). -# These are so many OS and CPU dependencies that we don't bother, and rely -# only on SA_SIGINFO. -AC_DEFUN([gt_SIGINFO], -[ - AC_CACHE_CHECK([for signal handlers with siginfo_t], gt_cv_siginfo_t, - [AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[struct sigaction action; - siginfo_t info; - action.sa_flags = SA_SIGINFO; - action.sa_sigaction = (void *) 0; - ]])], - [gt_cv_siginfo_t=yes], - [gt_cv_siginfo_t=no])]) - if test $gt_cv_siginfo_t = yes; then - AC_DEFINE([HAVE_SIGINFO], [1], - [Define to 1 if defines the siginfo_t type and - struct sigaction has the sa_sigaction member and the SA_SIGINFO flag.]) - fi -]) diff --git a/gettext-tools/src/msgl-check.c b/gettext-tools/src/msgl-check.c index 0d4d98fdc..c048e71ec 100644 --- a/gettext-tools/src/msgl-check.c +++ b/gettext-tools/src/msgl-check.c @@ -23,8 +23,6 @@ #include "msgl-check.h" #include -#include -#include #include #include #include @@ -69,9 +67,6 @@ plural_expression_histogram (const struct plural_distribution *self, unsigned long n; unsigned int count; - /* Protect against arithmetic exceptions. */ - install_sigfpe_handler (); - count = 0; for (n = min; n <= max; n++) { @@ -81,9 +76,6 @@ plural_expression_histogram (const struct plural_distribution *self, count++; } - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); - return count; } else @@ -105,7 +97,7 @@ check_plural_eval (const struct expression *plural_expr, /* Do as if the plural formula assumes a value N infinitely often if it assumes it at least 5 times. */ #define OFTEN 5 - unsigned char * volatile array; + unsigned char *array; /* Allocate a distribution array. */ if (nplurals_value <= 100) @@ -114,116 +106,69 @@ check_plural_eval (const struct expression *plural_expr, /* nplurals_value is nonsense. Don't risk an out-of-memory. */ array = NULL; - if (sigsetjmp (sigfpe_exit, 1) == 0) + unsigned long n; + + for (n = 0; n <= 1000; n++) { - unsigned long n; - - /* Protect against arithmetic exceptions. */ - install_sigfpe_handler (); - - for (n = 0; n <= 1000; n++) + struct eval_result res = plural_eval (plural_expr, n); + if (res.status != PE_OK) { - struct eval_result res = plural_eval (plural_expr, n); - if (res.status != PE_OK) - { - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); + if (res.status == PE_INTDIV) + po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, + _("plural expression can produce division by zero")); + else if (res.status == PE_INTOVF) + po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, + _("plural expression can produce integer overflow")); + else if (res.status == PE_STACKOVF) + po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, + _("plural expression can produce stack overflow")); + else + /* Other res.status values should not occur. */ + abort (); - if (res.status == PE_STACKOVF) - po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, - _("plural expression can produce stack overflow")); - else - /* Other res.status values should not occur. */ - abort (); - - free (array); - return 1; - } - - unsigned long val = res.value; - - if ((long) val < 0) - { - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); - - po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, - _("plural expression can produce negative values")); - free (array); - return 1; - } - else if (val >= nplurals_value) - { - char *msg; - - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); - - msg = xasprintf (_("nplurals = %lu but plural expression can produce values as large as %lu"), - nplurals_value, val); - po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg); - free (msg); - free (array); - return 1; - } - - if (array != NULL && array[val] < OFTEN) - array[val]++; + free (array); + return 1; } - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); + unsigned long val = res.value; - /* Normalize the array[val] statistics. */ - if (array != NULL) + if ((long) val < 0) { - unsigned long val; - - for (val = 0; val < nplurals_value; val++) - array[val] = (array[val] == OFTEN ? 1 : 0); + po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, + _("plural expression can produce negative values")); + free (array); + return 1; + } + else if (val >= nplurals_value) + { + char *msg = + xasprintf (_("nplurals = %lu but plural expression can produce values as large as %lu"), + nplurals_value, val); + po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg); + free (msg); + free (array); + return 1; } - distribution->expr = plural_expr; - distribution->often = array; - distribution->often_length = (array != NULL ? nplurals_value : 0); - distribution->histogram = plural_expression_histogram; - - return 0; + if (array != NULL && array[val] < OFTEN) + array[val]++; } - else + + /* Normalize the array[val] statistics. */ + if (array != NULL) { - /* Caught an arithmetic exception. */ - const char *msg; + unsigned long val; - /* End of protection against arithmetic exceptions. */ - uninstall_sigfpe_handler (); - -#if USE_SIGINFO - switch (sigfpe_code) -#endif - { -#if USE_SIGINFO -# ifdef FPE_INTDIV - case FPE_INTDIV: - msg = _("plural expression can produce division by zero"); - break; -# endif -# ifdef FPE_INTOVF - case FPE_INTOVF: - msg = _("plural expression can produce integer overflow"); - break; -# endif - default: -#endif - msg = _("plural expression can produce arithmetic exceptions, possibly division by zero"); - } - - po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg); - - free (array); - - return 1; + for (val = 0; val < nplurals_value; val++) + array[val] = (array[val] == OFTEN ? 1 : 0); } + + distribution->expr = plural_expr; + distribution->often = array; + distribution->often_length = (array != NULL ? nplurals_value : 0); + distribution->histogram = plural_expression_histogram; + + return 0; #undef OFTEN } diff --git a/gettext-tools/src/plural-eval.c b/gettext-tools/src/plural-eval.c index 82fbe56ef..dac9a845f 100644 --- a/gettext-tools/src/plural-eval.c +++ b/gettext-tools/src/plural-eval.c @@ -1,5 +1,5 @@ /* Expression evaluation for plural form selection. - Copyright (C) 2000-2003, 2005, 2019-2020 Free Software Foundation, Inc. + Copyright (C) 2000-2023 Free Software Foundation, Inc. Written by Ulrich Drepper , 2000. This program is free software: you can redistribute it and/or modify @@ -23,7 +23,6 @@ #include "plural-eval.h" #include -#include #include "plural-exp.h" @@ -33,67 +32,3 @@ /* Include the expression evaluation code from libintl, this time with 'extern' linkage. */ #include "eval-plural.h" - - -/* Exit point. Must be set before calling install_sigfpe_handler(). */ -sigjmp_buf sigfpe_exit; - -#if USE_SIGINFO - -/* Additional information that is set before sigfpe_exit is invoked. */ -int volatile sigfpe_code; - -/* Signal handler called in case of arithmetic exception (e.g. division - by zero) during plural_eval. */ -static _GL_ASYNC_SAFE void -sigfpe_handler (int sig, siginfo_t *sip, void *scp) -{ - sigfpe_code = sip->si_code; - /* This handler is invoked on the thread that caused the SIGFPE, that is, - the thread that is doing plural evaluation. Therefore it's OK to use - siglongjmp. */ - siglongjmp (sigfpe_exit, 1); -} - -#else - -/* Signal handler called in case of arithmetic exception (e.g. division - by zero) during plural_eval. */ -static _GL_ASYNC_SAFE void -sigfpe_handler (int sig) -{ - /* This handler is invoked on the thread that caused the SIGFPE, that is, - the thread that is doing plural evaluation. Therefore it's OK to use - siglongjmp. */ - siglongjmp (sigfpe_exit, 1); -} - -#endif - -void -install_sigfpe_handler (void) -{ -#if USE_SIGINFO - struct sigaction action; - action.sa_sigaction = sigfpe_handler; - action.sa_flags = SA_SIGINFO; - sigemptyset (&action.sa_mask); - sigaction (SIGFPE, &action, (struct sigaction *) NULL); -#else - signal (SIGFPE, sigfpe_handler); -#endif -} - -void -uninstall_sigfpe_handler (void) -{ -#if USE_SIGINFO - struct sigaction action; - action.sa_handler = SIG_DFL; - action.sa_flags = 0; - sigemptyset (&action.sa_mask); - sigaction (SIGFPE, &action, (struct sigaction *) NULL); -#else - signal (SIGFPE, SIG_DFL); -#endif -} diff --git a/gettext-tools/src/plural-eval.h b/gettext-tools/src/plural-eval.h index d2b3ce983..944965ef1 100644 --- a/gettext-tools/src/plural-eval.h +++ b/gettext-tools/src/plural-eval.h @@ -24,44 +24,4 @@ #include "plural-exp.h" -/* Protection against signals during plural evaluation. */ - -#include - -/* Some platforms don't have the sigjmp_buf type in . */ -#if defined _MSC_VER || defined __MINGW32__ -/* Native Woe32 API. */ -# define sigjmp_buf jmp_buf -# define sigsetjmp(env,savesigs) setjmp (env) -# define siglongjmp longjmp -#endif - -/* We use siginfo to get precise information about the signal. - But siginfo doesn't work on Irix 6.5 and on Cygwin 2005. */ -#if HAVE_SIGINFO && !defined (__sgi) && !defined (__CYGWIN__) -# define USE_SIGINFO 1 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Exit point. Must be set before calling install_sigfpe_handler(). */ -extern sigjmp_buf sigfpe_exit; - -#if USE_SIGINFO -/* Additional information that is set before sigfpe_exit is invoked. */ -extern int volatile sigfpe_code; -#endif - -/* Protect against signals during plural evaluation. Must be called around - calls to plural_eval(). Must be called in pairs. */ -extern void install_sigfpe_handler (void); -extern void uninstall_sigfpe_handler (void); - -#ifdef __cplusplus -} -#endif - - #endif /* _PLURAL_EVAL_H */