2

I'm writing a program that's examining its own address space.

Specifically, I care about all malloc-ed data blocks. If there some system call to get a list of them, that would be fantastic (for my application, I cannot use LD_PRELOAD, -wrap, nor any extra command line options). If there's a way to do this, I'd love to hear it even more than an answer to my stated problem, below.

In lieu of this, my current approach is to just dereference everything and look around. Obviously, the set of all possible pointers is a minefield of segfaults waiting to happen, so I tried registering a signal handler and using setjmp/longjmp (simply ignoring the segfault by making the handler do nothing is an infinite loop because the handler will return to the faulting instruction). Some example code goes like so:

static jmp_buf buf;
void handler(int i) {
    printf("    Segfaulted!\n");
    longjmp(buf,-1);
}
void segfault(void) {
    int* x = 0x0;
    int y = *x;
}
void test_function(void) {
    signal(11,handler);
    while (1) {
        if (setjmp(buf)==0) {
            printf("Segfaulting:\n");
            segfault();
        }
        else {
            printf("Recovered and not segfaulting!\n");
        }
        printf("\n");
    }
}

The output is:

    Segfaulting:
        Segfaulted!
    Recovered and not segfaulting!

    Segfaulting:
    Segmentation fault

So, the handler didn't work the second time around. I don't know why this is, but I speculated it had something to do with not clearing the original signal. I don't know how to do that.

As an aside, I tried sigsetjmp/siglongjmp first, but they weren't defined for some reason in setjmp.h. I got vague vibes that one needed to pass some extra compile flags, but, as before, that is not allowed for this application.

The system being used is Ubuntu Linux 10.04 x86-64, and any solution does not need to be portable.

[EDIT: sigrelse in the handler clears the signal, and fixes the problem effectively. Question now concerns the other issues raised--is there a better way (i.e., get the blocks of malloc)? What's up with sigsetjmp/siglongjmp? Why do I need to reset the signal?]

geometrian
  • 14,775
  • 10
  • 56
  • 132
  • You can't user LD_PRELOAD or the like at runtime, but since you're compiling the code is there a reason you can't simply define your own `malloc()`, or include one of those others at link time ? – evil otto Apr 25 '12 at 00:30
  • 1
    You may be able to do the instrumentation you want very easily using glibc's 'memory allocation hooks': http://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html#Hooks-for-Malloc – Michael Burr Apr 25 '12 at 01:18

2 Answers2

2

signal() is a legacy interface, and may or may not re-register a signal handler after it has been invoked, depending on the OS; you may need to issue another signal() call to reset the signal handler as the last action in your handler. See man 2 signal.

sigaction() is the preferred mechanism to set signal handlers, as it has well defined and portable behavior.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • Thanks. I had tried setting the signal handler within the loop, but that does not fix the problem. I'm not concerned with portability; this program is being written for my purposes only. – geometrian Apr 25 '12 at 00:22
  • The point here is not so much the portability as the well-defined-ness of the behavior (which comes as a side effect of portability). `signal`'s behavior is not well defined; this is why `sigaction` exists. – geekosaur Apr 25 '12 at 00:23
  • I chose sigaction because it was simpler to use, but you're absolutely right: portability *is* important to defined behavior. In this case, though, the problem ended up being my not clearing the signal with sigrelse, so that wasn't the issue. – geometrian Apr 25 '12 at 00:29
1

When the signal handler for SIGSEGV is invoked, the SIGSEGV signal will be masked as if by sigprocmask. This is true for any signal. Normally returning from the signal handler would unmask it, but since you're not returning, that never happens. There are a couple possible solutions:

  • You can call sigprocmask either before or after the longjmp to unmask it yourself.
  • You can install the signal handler with sigaction (the preferred way to do it anyway) and use the SA_NODEFER flag to prevent it from being masked.
  • You can use the sigsetjmp and siglongjmp functions, which themselves take responsibility for saving and restoring the signal mask.
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711