Как я могу сбросить память с произвольного виртуального адреса, игнорируя SIGSEGV - PullRequest
1 голос
/ 05 июня 2019

Я ищу способ перебрать память и распечатать ее (или сделать что-нибудь с ней на самом деле), но если виртуальный адрес не был выделен, я получу ошибку.Как я могу попытаться прочитать произвольные адреса в моей программе и не дать сбоя?

Это в первую очередь для отладки, поэтому безопасное / определенное поведение не является высоким приоритетом.Например, я мог бы захотеть сбросить память, на которую указывает указатель, и некоторые окружающие значения для проверки на наличие повреждений.

Из интереса, есть ли причины, по которым это нельзя сделать безопасными, например, побочные эффекты чтения?

Фактический дамп памяти прост, например, следующий.Этот вопрос на самом деле о игнорировании segfault.

EDIT: Я использую Ubuntu 18.04

1 Ответ

2 голосов
/ 05 июня 2019
  • Используйте sigaction для перехвата таких сигналов, как SIGBUS и SIGSEGV
  • Используйте siglongjmp для безопасного выхода из обработчика
  • Установите SA_NODEFER, чтобы можно было снова поймать сигнал

Основываясь на этом ответе: Ловить нарушения сегментации и продолжать жизнь

#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;
}

(шестнадцатеричный код изменен с https://gist.github.com/domnikl/af00cc154e3da1c5d965)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...