1

I'm looking for a way to iterate over memory and print it (or do anything with it really), but if the virtual address hasn't been allocated I'll get a segfault. How can I attempt to read arbitrary addresses in my program and not crash?

This is primarily for debugging so safety/defined behaviour isn't a high priority. For example, I might want to dump the memory a pointer points to and some surrounding values to check for corruption.

Out of interest, is there any reason this cannot be made safe, e.g. side effects of reads?

The actual memory dump is simple, for example the following. This question is really about ignoring the segfault.

EDIT:
I'm running ubuntu 18.04

jozxyqk
  • 16,424
  • 12
  • 91
  • 180

1 Answers1

2
  • Use sigaction to catch signals such as SIGBUS and SIGSEGV
  • Use siglongjmp to exit the handler safely
  • Set SA_NODEFER to be able to catch the signal again

Based on this answer: Catching Segmentation Violations and Getting on with Life

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <setjmp.h>

sigjmp_buf badAddressRead;

void badAddressReadHandler(int signo) {
    siglongjmp(badAddressRead, signo);
}

void hexDump(const char *desc, void *addr, int len)
{
    int i;
    unsigned char buff[17];
    unsigned char *pc = (unsigned char*)addr;

    struct sigaction segvActionOld;
    struct sigaction busActionOld;
    struct sigaction readAction = {};

    readAction.sa_handler = badAddressReadHandler;
    sigemptyset(&readAction.sa_mask);
    readAction.sa_flags = SA_NODEFER;
    sigaction(SIGBUS, &readAction, &segvActionOld);
    sigaction(SIGSEGV, &readAction, &busActionOld);

    // Output description if given.
    if (desc != NULL)
        printf("%s:\n", desc);

    printf("address = %p\n", addr);

    // Process every byte in the data.
    for (i = 0; i < len; i++) {
        // Multiple of 16 means new line (with line offset).

        if ((i % 16) == 0) {
            // Just don't print ASCII for the zeroth line.
            if (i != 0)
                printf("  %s\n", buff);

            // Output the offset.
            printf("  %04x ", i);
        }

        // Attempt to read memory that may not be accessible
        unsigned char byte;
        int caughtSignal;
        if ((caughtSignal = sigsetjmp(badAddressRead, 0)) == 0) {
            // Now the hex code for the specific character.
            byte = pc[i];
            printf(" %02x", byte);
        } else {
            byte = 0;
            if (caughtSignal == SIGSEGV) {
                printf(" SV");
            } else if (caughtSignal == SIGBUS) {
                printf(" BS");
            } else {
                printf(" ??");
            }
        }


        // And store a printable ASCII character for later.
        if ((byte < 0x20) || (byte > 0x7e)) {
            buff[i % 16] = '.';
        } else {
            buff[i % 16] = pc[i];
        }

        buff[(i % 16) + 1] = '\0';
    }

    // Pad out last line if not exactly 16 characters.
    while ((i % 16) != 0) {
        printf("   ");
        i++;
    }

    // And print the final ASCII bit.
    printf("  %s\n", buff);

    sigaction(SIGBUS, &busActionOld, NULL);
    sigaction(SIGSEGV, &segvActionOld, NULL);
}

int main(void) {
    int test  = 123;
    hexDump("test", &test, 8000); // dump 'test' and a whole bunch more after it
    return 0;
}

(hexdump code modified from https://gist.github.com/domnikl/af00cc154e3da1c5d965)

jozxyqk
  • 16,424
  • 12
  • 91
  • 180