aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-08-21 02:16:50 -0700
committerPaul Eggert2018-08-21 02:38:53 -0700
commitd6a497dd887cdbb35c5b4e2929e83962ba708159 (patch)
tree9f0441f9fe88419b71e568b05ef7f7bea0a0ff06 /src
parent77fc2725985b4e5ef977ae6930835c7f0771c61c (diff)
downloademacs-d6a497dd887cdbb35c5b4e2929e83962ba708159.tar.gz
emacs-d6a497dd887cdbb35c5b4e2929e83962ba708159.zip
Avoid libgmp aborts by imposing limits
libgmp calls ‘abort’ when given numbers too big for its internal data structures. The numeric limit is large and platform-dependent; with 64-bit GMP 6.1.2 it is around 2**2**37. Work around the problem by refusing to call libgmp functions with arguments that would cause an abort. With luck libgmp will have a better way to do this in the future. Also, introduce a variable integer-width that lets the user control how large bignums can be. This currently defaults to 2**16, i.e., it allows bignums up to 2**2**16. This should be enough for ordinary computation, and should help Emacs to avoid thrashing or hanging. Problem noted by Pip Cet (Bug#32463#71). * doc/lispref/numbers.texi, etc/NEWS: Document recent bignum changes, including this one. Improve documentation for bitwise operations, in the light of bignums. * src/alloc.c (make_number): Enforce integer-width. (integer_overflow): New function. (xrealloc_for_gmp, xfree_for_gmp): Move here from emacs.c, as it's memory allocation. (init_alloc): Initialize GMP here, rather than in emacs.c. (integer_width): New var. * src/data.c (GMP_NLIMBS_MAX, NLIMBS_LIMIT): New constants. (emacs_mpz_size, emacs_mpz_mul) (emacs_mpz_mul_2exp, emacs_mpz_pow_ui): New functions. (arith_driver, Fash, expt_integer): Use them. (expt_integer): New function, containing integer code that was out of place in floatfns.c. (check_bignum_size, xmalloc_for_gmp): Remove. * src/emacs.c (main): Do not initialize GMP here. * src/floatfns.c (Fexpt): Use expt_integer, which now contains integer code moved from here. * src/lisp.h (GMP_NUMB_BITS): Define if gmp.h doesn’t.
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c73
-rw-r--r--src/data.c109
-rw-r--r--src/emacs.c34
-rw-r--r--src/floatfns.c24
-rw-r--r--src/lisp.h11
5 files changed, 167 insertions, 84 deletions
diff --git a/src/alloc.c b/src/alloc.c
index ddc0696ba91..24a24aab96b 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3746,33 +3746,33 @@ make_bignum_str (const char *num, int base)
3746Lisp_Object 3746Lisp_Object
3747make_number (mpz_t value) 3747make_number (mpz_t value)
3748{ 3748{
3749 if (mpz_fits_slong_p (value)) 3749 size_t bits = mpz_sizeinbase (value, 2);
3750 { 3750
3751 long l = mpz_get_si (value); 3751 if (bits <= FIXNUM_BITS)
3752 if (!FIXNUM_OVERFLOW_P (l))
3753 return make_fixnum (l);
3754 }
3755 else if (LONG_WIDTH < FIXNUM_BITS)
3756 { 3752 {
3757 size_t bits = mpz_sizeinbase (value, 2); 3753 EMACS_INT v = 0;
3754 int i = 0, shift = 0;
3758 3755
3759 if (bits <= FIXNUM_BITS) 3756 do
3760 { 3757 {
3761 EMACS_INT v = 0; 3758 EMACS_INT limb = mpz_getlimbn (value, i++);
3762 int i = 0; 3759 v += limb << shift;
3763 for (int shift = 0; shift < bits; shift += mp_bits_per_limb) 3760 shift += GMP_NUMB_BITS;
3764 { 3761 }
3765 EMACS_INT limb = mpz_getlimbn (value, i++); 3762 while (shift < bits);
3766 v += limb << shift;
3767 }
3768 if (mpz_sgn (value) < 0)
3769 v = -v;
3770 3763
3771 if (!FIXNUM_OVERFLOW_P (v)) 3764 if (mpz_sgn (value) < 0)
3772 return make_fixnum (v); 3765 v = -v;
3773 } 3766
3767 if (!FIXNUM_OVERFLOW_P (v))
3768 return make_fixnum (v);
3774 } 3769 }
3775 3770
3771 /* The documentation says integer-width should be nonnegative, so
3772 a single comparison suffices even though 'bits' is unsigned. */
3773 if (integer_width < bits)
3774 integer_overflow ();
3775
3776 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value, 3776 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value,
3777 PVEC_BIGNUM); 3777 PVEC_BIGNUM);
3778 /* We could mpz_init + mpz_swap here, to avoid a copy, but the 3778 /* We could mpz_init + mpz_swap here, to avoid a copy, but the
@@ -7200,6 +7200,26 @@ verify_alloca (void)
7200 7200
7201#endif /* ENABLE_CHECKING && USE_STACK_LISP_OBJECTS */ 7201#endif /* ENABLE_CHECKING && USE_STACK_LISP_OBJECTS */
7202 7202
7203/* Memory allocation for GMP. */
7204
7205void
7206integer_overflow (void)
7207{
7208 error ("Integer too large to be represented");
7209}
7210
7211static void *
7212xrealloc_for_gmp (void *ptr, size_t ignore, size_t size)
7213{
7214 return xrealloc (ptr, size);
7215}
7216
7217static void
7218xfree_for_gmp (void *ptr, size_t ignore)
7219{
7220 xfree (ptr);
7221}
7222
7203/* Initialization. */ 7223/* Initialization. */
7204 7224
7205void 7225void
@@ -7233,6 +7253,10 @@ init_alloc_once (void)
7233void 7253void
7234init_alloc (void) 7254init_alloc (void)
7235{ 7255{
7256 eassert (mp_bits_per_limb == GMP_NUMB_BITS);
7257 integer_width = 1 << 16;
7258 mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
7259
7236 Vgc_elapsed = make_float (0.0); 7260 Vgc_elapsed = make_float (0.0);
7237 gcs_done = 0; 7261 gcs_done = 0;
7238 7262
@@ -7335,6 +7359,11 @@ The time is in seconds as a floating point value. */);
7335 DEFVAR_INT ("gcs-done", gcs_done, 7359 DEFVAR_INT ("gcs-done", gcs_done,
7336 doc: /* Accumulated number of garbage collections done. */); 7360 doc: /* Accumulated number of garbage collections done. */);
7337 7361
7362 DEFVAR_INT ("integer-width", integer_width,
7363 doc: /* Maximum number of bits in bignums.
7364Integers outside the fixnum range are limited to absolute values less
7365than 2**N, where N is this variable's value. N should be nonnegative. */);
7366
7338 defsubr (&Scons); 7367 defsubr (&Scons);
7339 defsubr (&Slist); 7368 defsubr (&Slist);
7340 defsubr (&Svector); 7369 defsubr (&Svector);
diff --git a/src/data.c b/src/data.c
index 8a6975da3ab..4c6d33f2940 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2384,6 +2384,80 @@ bool-vector. IDX starts at 0. */)
2384 return newelt; 2384 return newelt;
2385} 2385}
2386 2386
2387/* GMP tests for this value and aborts (!) if it is exceeded.
2388 This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */
2389enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) };
2390
2391/* An upper bound on limb counts, needed to prevent libgmp and/or
2392 Emacs from aborting or otherwise misbehaving. This bound applies
2393 to estimates of mpz_t sizes before the mpz_t objects are created,
2394 as opposed to integer-width which operates on mpz_t values after
2395 creation and before conversion to Lisp bignums. */
2396enum
2397 {
2398 NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */
2399 GMP_NLIMBS_MAX,
2400
2401 /* Size calculations need to work. */
2402 min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)),
2403
2404 /* Emacs puts bit counts into fixnums. */
2405 MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS)
2406 };
2407
2408/* Like mpz_size, but tell the compiler the result is a nonnegative int. */
2409
2410static int
2411emacs_mpz_size (mpz_t const op)
2412{
2413 mp_size_t size = mpz_size (op);
2414 eassume (0 <= size && size <= INT_MAX);
2415 return size;
2416}
2417
2418/* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016),
2419 the library code aborts when a number is too large. These wrappers
2420 avoid the problem for functions that can return numbers much larger
2421 than their arguments. For slowly-growing numbers, the integer
2422 width check in make_number should suffice. */
2423
2424static void
2425emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
2426{
2427 if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
2428 integer_overflow ();
2429 mpz_mul (rop, op1, op2);
2430}
2431
2432static void
2433emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, mp_bitcnt_t op2)
2434{
2435 /* Fudge factor derived from GMP 6.1.2, to avoid an abort in
2436 mpz_mul_2exp (look for the '+ 1' in its source code). */
2437 enum { mul_2exp_extra_limbs = 1 };
2438 enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) };
2439
2440 mp_bitcnt_t op2limbs = op2 / GMP_NUMB_BITS;
2441 if (lim - emacs_mpz_size (op1) < op2limbs)
2442 integer_overflow ();
2443 mpz_mul_2exp (rop, op1, op2);
2444}
2445
2446static void
2447emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
2448{
2449 /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in
2450 mpz_n_pow_ui (look for the '5' in its source code). */
2451 enum { pow_ui_extra_limbs = 5 };
2452 enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) };
2453
2454 int nbase = emacs_mpz_size (base), n;
2455 if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n)
2456 integer_overflow ();
2457 mpz_pow_ui (rop, base, exp);
2458}
2459
2460
2387/* Arithmetic functions */ 2461/* Arithmetic functions */
2388 2462
2389Lisp_Object 2463Lisp_Object
@@ -2872,13 +2946,13 @@ arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args)
2872 break; 2946 break;
2873 case Amult: 2947 case Amult:
2874 if (BIGNUMP (val)) 2948 if (BIGNUMP (val))
2875 mpz_mul (accum, accum, XBIGNUM (val)->value); 2949 emacs_mpz_mul (accum, accum, XBIGNUM (val)->value);
2876 else if (! FIXNUMS_FIT_IN_LONG) 2950 else if (! FIXNUMS_FIT_IN_LONG)
2877 { 2951 {
2878 mpz_t tem; 2952 mpz_t tem;
2879 mpz_init (tem); 2953 mpz_init (tem);
2880 mpz_set_intmax (tem, XFIXNUM (val)); 2954 mpz_set_intmax (tem, XFIXNUM (val));
2881 mpz_mul (accum, accum, tem); 2955 emacs_mpz_mul (accum, accum, tem);
2882 mpz_clear (tem); 2956 mpz_clear (tem);
2883 } 2957 }
2884 else 2958 else
@@ -3293,7 +3367,7 @@ In this case, the sign bit is duplicated. */)
3293 mpz_t result; 3367 mpz_t result;
3294 mpz_init (result); 3368 mpz_init (result);
3295 if (XFIXNUM (count) > 0) 3369 if (XFIXNUM (count) > 0)
3296 mpz_mul_2exp (result, XBIGNUM (value)->value, XFIXNUM (count)); 3370 emacs_mpz_mul_2exp (result, XBIGNUM (value)->value, XFIXNUM (count));
3297 else 3371 else
3298 mpz_fdiv_q_2exp (result, XBIGNUM (value)->value, - XFIXNUM (count)); 3372 mpz_fdiv_q_2exp (result, XBIGNUM (value)->value, - XFIXNUM (count));
3299 val = make_number (result); 3373 val = make_number (result);
@@ -3319,7 +3393,7 @@ In this case, the sign bit is duplicated. */)
3319 mpz_set_intmax (result, XFIXNUM (value)); 3393 mpz_set_intmax (result, XFIXNUM (value));
3320 3394
3321 if (XFIXNUM (count) >= 0) 3395 if (XFIXNUM (count) >= 0)
3322 mpz_mul_2exp (result, result, XFIXNUM (count)); 3396 emacs_mpz_mul_2exp (result, result, XFIXNUM (count));
3323 else 3397 else
3324 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count)); 3398 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count));
3325 3399
@@ -3330,6 +3404,33 @@ In this case, the sign bit is duplicated. */)
3330 return val; 3404 return val;
3331} 3405}
3332 3406
3407/* Return X ** Y as an integer. X and Y must be integers, and Y must
3408 be nonnegative. */
3409
3410Lisp_Object
3411expt_integer (Lisp_Object x, Lisp_Object y)
3412{
3413 unsigned long exp;
3414 if (TYPE_RANGED_FIXNUMP (unsigned long, y))
3415 exp = XFIXNUM (y);
3416 else if (MOST_POSITIVE_FIXNUM < ULONG_MAX && BIGNUMP (y)
3417 && mpz_fits_ulong_p (XBIGNUM (y)->value))
3418 exp = mpz_get_ui (XBIGNUM (y)->value);
3419 else
3420 integer_overflow ();
3421
3422 mpz_t val;
3423 mpz_init (val);
3424 emacs_mpz_pow_ui (val,
3425 (FIXNUMP (x)
3426 ? (mpz_set_intmax (val, XFIXNUM (x)), val)
3427 : XBIGNUM (x)->value),
3428 exp);
3429 Lisp_Object res = make_number (val);
3430 mpz_clear (val);
3431 return res;
3432}
3433
3333DEFUN ("1+", Fadd1, Sadd1, 1, 1, 0, 3434DEFUN ("1+", Fadd1, Sadd1, 1, 1, 0,
3334 doc: /* Return NUMBER plus one. NUMBER may be a number or a marker. 3435 doc: /* Return NUMBER plus one. NUMBER may be a number or a marker.
3335Markers are converted to integers. */) 3436Markers are converted to integers. */)
diff --git a/src/emacs.c b/src/emacs.c
index 11ee0b81180..7d07ec85029 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -673,38 +673,6 @@ close_output_streams (void)
673 _exit (EXIT_FAILURE); 673 _exit (EXIT_FAILURE);
674} 674}
675 675
676/* Memory allocation functions for GMP. */
677
678static void
679check_bignum_size (size_t size)
680{
681 /* Do not create a bignum whose log base 2 could exceed fixnum range.
682 This way, functions like mpz_popcount return values in fixnum range.
683 It may also help to avoid other problems with outlandish bignums. */
684 if (MOST_POSITIVE_FIXNUM / CHAR_BIT < size)
685 error ("Integer too large to be represented");
686}
687
688static void * ATTRIBUTE_MALLOC
689xmalloc_for_gmp (size_t size)
690{
691 check_bignum_size (size);
692 return xmalloc (size);
693}
694
695static void *
696xrealloc_for_gmp (void *ptr, size_t ignore, size_t size)
697{
698 check_bignum_size (size);
699 return xrealloc (ptr, size);
700}
701
702static void
703xfree_for_gmp (void *ptr, size_t ignore)
704{
705 xfree (ptr);
706}
707
708/* ARGSUSED */ 676/* ARGSUSED */
709int 677int
710main (int argc, char **argv) 678main (int argc, char **argv)
@@ -803,8 +771,6 @@ main (int argc, char **argv)
803 init_standard_fds (); 771 init_standard_fds ();
804 atexit (close_output_streams); 772 atexit (close_output_streams);
805 773
806 mp_set_memory_functions (xmalloc_for_gmp, xrealloc_for_gmp, xfree_for_gmp);
807
808 sort_args (argc, argv); 774 sort_args (argc, argv);
809 argc = 0; 775 argc = 0;
810 while (argv[argc]) argc++; 776 while (argv[argc]) argc++;
diff --git a/src/floatfns.c b/src/floatfns.c
index 7c52a0a9a20..ea9000b90a0 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -210,29 +210,7 @@ DEFUN ("expt", Fexpt, Sexpt, 2, 2, 0,
210 /* Common Lisp spec: don't promote if both are integers, and if the 210 /* Common Lisp spec: don't promote if both are integers, and if the
211 result is not fractional. */ 211 result is not fractional. */
212 if (INTEGERP (arg1) && NATNUMP (arg2)) 212 if (INTEGERP (arg1) && NATNUMP (arg2))
213 { 213 return expt_integer (arg1, arg2);
214 unsigned long exp;
215 if (TYPE_RANGED_FIXNUMP (unsigned long, arg2))
216 exp = XFIXNUM (arg2);
217 else if (MOST_POSITIVE_FIXNUM < ULONG_MAX && BIGNUMP (arg2)
218 && mpz_fits_ulong_p (XBIGNUM (arg2)->value))
219 exp = mpz_get_ui (XBIGNUM (arg2)->value);
220 else
221 xsignal3 (Qrange_error, build_string ("expt"), arg1, arg2);
222
223 mpz_t val;
224 mpz_init (val);
225 if (FIXNUMP (arg1))
226 {
227 mpz_set_intmax (val, XFIXNUM (arg1));
228 mpz_pow_ui (val, val, exp);
229 }
230 else
231 mpz_pow_ui (val, XBIGNUM (arg1)->value, exp);
232 Lisp_Object res = make_number (val);
233 mpz_clear (val);
234 return res;
235 }
236 214
237 return make_float (pow (XFLOATINT (arg1), XFLOATINT (arg2))); 215 return make_float (pow (XFLOATINT (arg1), XFLOATINT (arg2)));
238} 216}
diff --git a/src/lisp.h b/src/lisp.h
index fe384d1844b..8f48a334844 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -996,6 +996,14 @@ enum More_Lisp_Bits
996#define MOST_POSITIVE_FIXNUM (EMACS_INT_MAX >> INTTYPEBITS) 996#define MOST_POSITIVE_FIXNUM (EMACS_INT_MAX >> INTTYPEBITS)
997#define MOST_NEGATIVE_FIXNUM (-1 - MOST_POSITIVE_FIXNUM) 997#define MOST_NEGATIVE_FIXNUM (-1 - MOST_POSITIVE_FIXNUM)
998 998
999
1000/* GMP-related limits. */
1001
1002/* Number of data bits in a limb. */
1003#ifndef GMP_NUMB_BITS
1004enum { GMP_NUMB_BITS = TYPE_WIDTH (mp_limb_t) };
1005#endif
1006
999#if USE_LSB_TAG 1007#if USE_LSB_TAG
1000 1008
1001INLINE Lisp_Object 1009INLINE Lisp_Object
@@ -3338,7 +3346,7 @@ extern void set_internal (Lisp_Object, Lisp_Object, Lisp_Object,
3338 enum Set_Internal_Bind); 3346 enum Set_Internal_Bind);
3339extern void set_default_internal (Lisp_Object, Lisp_Object, 3347extern void set_default_internal (Lisp_Object, Lisp_Object,
3340 enum Set_Internal_Bind bindflag); 3348 enum Set_Internal_Bind bindflag);
3341 3349extern Lisp_Object expt_integer (Lisp_Object, Lisp_Object);
3342extern void syms_of_data (void); 3350extern void syms_of_data (void);
3343extern void swap_in_global_binding (struct Lisp_Symbol *); 3351extern void swap_in_global_binding (struct Lisp_Symbol *);
3344 3352
@@ -3700,6 +3708,7 @@ extern void display_malloc_warning (void);
3700extern ptrdiff_t inhibit_garbage_collection (void); 3708extern ptrdiff_t inhibit_garbage_collection (void);
3701extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); 3709extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object);
3702extern void free_cons (struct Lisp_Cons *); 3710extern void free_cons (struct Lisp_Cons *);
3711extern _Noreturn void integer_overflow (void);
3703extern void init_alloc_once (void); 3712extern void init_alloc_once (void);
3704extern void init_alloc (void); 3713extern void init_alloc (void);
3705extern void syms_of_alloc (void); 3714extern void syms_of_alloc (void);