6

I have some code that uses libgmp. At some point the user may request a factorial of a very large number. Unfortunately, this results in libgmp raising an abort signal.

For example the following code:

#include <cmath>
#include <gmp.h>
#include <iostream>

int main() {

    mpz_t result;
    mpz_init(result);

    mpz_fac_ui(result, 20922789888000);

    std::cout << mpz_get_si(result) << std::endl;
}

Results in:

$ ./test 
gmp: overflow in mpz type
Aborted

Apparently, the number produced is REALLY big. Is there anyway to handle the error more gracefully than an abort. This is a GUI based application and it aborting is pretty much the least desirable way to handle this sort of issue.

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • Could you validate the user's input and reject "very large" values before feeding it to the factorial function? – TheUndeadFish Aug 24 '10 at 16:41
  • I supposed I could figure out what the biggest value I could put in which doesn't trigger it and make that a maximum value, but that's ugly. I was hoping for a better solution. The problem with that is that the largest value allowed differs from system to system since it is based on the available memory. So that is less than ideal. – Evan Teran Aug 24 '10 at 17:38
  • 4
    The error message you're seeing does not depend on available memory. It's printed whenever the requested amount of memory is larger than `INT_MAX`. That will still require you to predict how many bytes of memory MPZ needs to represent whatever number it's trying to compute. And besides, it's reasonable to assume that factorial isn't the only operation you plan on using; I expect you want to know *in general* how to make your program catch MPZ's abortions, or at least handle them more gracefully. – Rob Kennedy Aug 24 '10 at 17:54
  • @Rob: You are right about `INT_MAX` being the limit. and you are exactly right about the other details as well... factorial is one of many operations which could potentially cause this issue. If there is some generic callback I could set to say "an error occurred" – Evan Teran Aug 24 '10 at 18:27

3 Answers3

4

It would appear that you are out of luck, based on the code in mpz/realloc.c and mpz/realloc2.c. If too much memory was requested, it just does this:

if (UNLIKELY (new_alloc > INT_MAX))
  {
    fprintf (stderr, "gmp: overflow in mpz type\n");
    abort ();
  }
Jack Kelly
  • 18,264
  • 2
  • 56
  • 81
3

The best way to handle these errors gracefully in your application is probably to fork off a helper process to perform the GMP calculations. If the helper process is killed by SIGABRT, your parent process can detect that and report an error to the user.


(The below is my original answer, which has "undefined results" according to the GMP documentation - it is left here for completeness).

You can catch the error if you install a signal handler for SIGABRT that uses longjmp():

jmp_buf abort_jb;

void abort_handler(int x)
{
    longjmp(abort_jb, 1);
}

int dofac(unsigned long n)
{
    signal(SIGABRT, abort_handler);
    if (setjmp(abort_jb))
        goto error;

    mpz_t result;
    mpz_init(result);

    mpz_fac_ui(result, 20922789888000);

    std::cout << mpz_get_si(result) << std::endl;

    signal(SIGABRT, SIG_DFL);
    return 0;

    error:
    signal(SIGABRT, SIG_DFL);
    std::cerr << "Caught SIGABRT from GMP.\n";
    return 1;
}
caf
  • 233,326
  • 40
  • 323
  • 462
  • Standard disclaimer for `setjmp` applies: call it before initializing any local variables, directly as the whole expression of `if`, `while`, or `case`, not as a subexpression, in a function which does not return before the potential `longjmp`. – Potatoswatter Aug 25 '10 at 06:47
  • 3
    -1 This answer invokes undefined behaviour (at least, in GMP 5.0.4). Quoting from [here](http://gmplib.org/manual/Custom-Allocation.html#Custom-Allocation): `There's currently no defined way for the allocation functions to recover from an error such as out of memory, they must terminate program execution. A longjmp or throwing a C++ exception will have undefined results. This may change in the future.` – Mankarse Mar 07 '12 at 01:44
1

Overwrite abort() with LD_PRELOAD.

What is the LD_PRELOAD trick?

Edit: To make the answer more self-contained, I copy the text of that answer here:

If you set LD_PRELOAD to the path of a shared object, that file will be loaded before any other library (including the C runtime, libc.so). So to run ls with a your special malloc() implementation, do this:

$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls

Credits to JesperE.

Community
  • 1
  • 1
nalply
  • 26,770
  • 15
  • 78
  • 101