diff options
| author | Paul Eggert | 2018-08-21 02:16:50 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-21 02:38:53 -0700 |
| commit | d6a497dd887cdbb35c5b4e2929e83962ba708159 (patch) | |
| tree | 9f0441f9fe88419b71e568b05ef7f7bea0a0ff06 /src/data.c | |
| parent | 77fc2725985b4e5ef977ae6930835c7f0771c61c (diff) | |
| download | emacs-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.c | 109 |
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. */ | ||
| 2389 | enum { 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. */ | ||
| 2396 | enum | ||
| 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 | |||
| 2410 | static int | ||
| 2411 | emacs_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 | |||
| 2424 | static void | ||
| 2425 | emacs_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 | |||
| 2432 | static void | ||
| 2433 | emacs_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 | |||
| 2446 | static void | ||
| 2447 | emacs_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 | ||
| 2389 | Lisp_Object | 2463 | Lisp_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 | |||
| 3410 | Lisp_Object | ||
| 3411 | expt_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 | |||
| 3333 | DEFUN ("1+", Fadd1, Sadd1, 1, 1, 0, | 3434 | DEFUN ("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. |
| 3335 | Markers are converted to integers. */) | 3436 | Markers are converted to integers. */) |