Странное поведение обработчика сигналов при вызове mremap (2) - PullRequest
0 голосов
/ 28 мая 2020

Во-первых, вот код

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h> /* mmap() is defined in this header */
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <execinfo.h>
#define FILE_NAME "mremap_test_file.txt"

void sighandler(int signum) {
    printf("Signal SIGSEGV recieved\n");
    exit(EXIT_FAILURE);
}

int main (int argc, char *argv[])
{


    //int fdin;
    int fdout = 0;
    static char stack[SIGSTKSZ];
    stack_t ss = {
        .ss_size = SIGSTKSZ,
        .ss_sp = stack,
    };
    struct sigaction sigact;
    sigaltstack(&ss, 0);
    int ret = sigaction(SIGSEGV, NULL, &sigact);
    if (ret == -1){
        perror("register sighandler");
        return -1;
    }
    sigact.sa_handler = sighandler;
    sigact.sa_flags |= SA_ONSTACK | SA_RESTART;
    ret = sigaction(SIGSEGV, &sigact, NULL);
    if (ret == -1){
        perror("register sighandler");
        return -1;
    }

    fdout = open(FILE_NAME, O_CREAT| O_RDWR);
    if( fdout < 0){

        perror("open");
        return -1;
    }
    char test_str[1024];
    memset(test_str,65,1024);
    write(fdout,test_str,1024);
    close(fdout);
    sync();
    if(argc > 2)
        return 0;
    fdout = open(FILE_NAME, O_CREAT| O_RDWR);
    if( fdout < 0){

        perror("open");
        return -1;
    }

    void * addr = NULL;
    addr = mmap(NULL, 1024, PROT_EXEC |PROT_WRITE, MAP_SHARED, fdout, 0);

    if(addr == MAP_FAILED ){
        perror("mmap");
        return -1;
    }
    void* remap_addr = addr;
    remap_addr = mremap(remap_addr, 1024, 512, (MREMAP_FIXED | MREMAP_MAYMOVE), (void*)((unsigned long)remap_addr + 4096) );
    if(remap_addr == MAP_FAILED){
        perror("mremap");
        return -1;
    }
    int a = *(int*)(0);

    remove(FILE_NAME);
    return 0;
}

Я использую g cc версии 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1 ~ 16.04.12) для компиляции этого кода

Вы можете видеть, что внутри кода есть int a = *(int*)(0); для запуска SIGSEGV. Странно то, что если эта строка кода выполняется до mremap(2), пользовательский обработчик SIGSEGV запускается; если эта строка кода запускается после mremap(2), она запускает обработчик SIGSEGV по умолчанию.

mremap(2) действительно возвращает успех без каких-либо проблем, так что я не мог понять такое поведение

First case 2nd case

Я также пробовал другой случай и обнаружил это 3rd case Кажется, что оба по умолчанию и обслуживающий персонал называется

Ответы [ 2 ]

1 голос
/ 28 мая 2020

Я не вижу такого поведения, в обоих случаях незаконный доступ вызывает вызов sighandler().

Конечно, это может быть вызвано тем, что Мне пришлось исправить пару проблем с вашим кодом (то, что вы предоставили, не скомпилировалось из коробки), в частности:

  • создание файлов с разрешениями, которые позволяют мне открывать их снова, поскольку в противном случае они получают создано с нулевой маской разрешений:
    fdout = open(FILE_NAME, O_CREAT| O_RDWR, 0777);
  • Переназначение на основе исходного адреса, а не пока еще не существующего переназначенного адреса:
    void *remap_addr = mremap(addr, 1024, 512, (MREMAP_FIXED | MREMAP_MAYMOVE), (void*)((unsigned long)addr + 4096) );

Как только эти проблемы были исправлены, незаконный доступ вызывал специальный обработчик независимо от того, был ли он до или после mremap. Если это не решит вашу проблему, я предлагаю вам опубликовать свой фактический код, который вызывает проблему: -)


Еще одна вещь, которую вы, возможно, захотите рассмотреть, - это printf является не одной из функций, которую можно безопасно вызывать из обработчиков сигналов. В этой ссылке подробно описано, что безопасно использовать, поэтому может быть вызвано проблемой внутри самого printf.


Как Кроме того, было бы интересно посмотреть, что произойдет, если вы установите (или переустановите) обработчик сигналов после переназначения. Я упоминаю только это, потому что были и другие случаи, когда вызов определенных функций мешал тому, что изначально считалось отдельным элементом (смутное воспоминание о конфликте между sleep и сигналом SIGALRM, но это был long time a go).


Единственное, что я мог бы предложить, это использовать sigaction для получения текущего расположения сигнала после вызова mremap. Если он по какой-то причине был отсоединен от вашего обработчика, мы надеемся, что это должно прояснить это.

0 голосов
/ 28 мая 2020

Я немного отладил это. Похоже, что при вызове системного вызова mmap(2) система выбрала адрес, принадлежащий стеку программы для выделения. И адрес стека программы растет вниз. Поэтому, если я попытаюсь изменить mremap(2) на addr + 4096, он переопределит незаконный сегмент памяти программы. Это недопустимое распределение вызвано ядром do_unmap на addr и addr + 4096, а затем move_vma на этом адресе. (Ссылка https://elixir.bootlin.com/linux/v3.18.140/source/mm/mremap.c#L406)

Поэтому, когда MREMAP_FIXED указывается при вызове mremap(2), не важно, может ли параметр new_address нарушить адресное пространство своей программы .

Возможный обходной путь для этого - вместо переназначения на addr + 4096 переназначить на addr - 4096

remap_addr = mremap(remap_addr, 1024, 512, (MREMAP_FIXED | MREMAP_MAYMOVE), (void*)((unsigned long)remap_addr - 4096) );
...