aboutsummaryrefslogtreecommitdiffstats
path: root/src/data.c
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/data.c
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/data.c')
-rw-r--r--src/data.c109
1 files changed, 105 insertions, 4 deletions
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. */)