Неоднозначность вызова функции обработчика сигналов

Решая одну из задач в варгейме, я столкнулся со странным поведением сигнальной функции в C. Насколько я понимаю,

void (*signal(int sig, void (*func)(int)))(int)

sig — это номер сигнала, при обнаружении которого вызывается функция обработчика func. Код, в котором я пытался найти эксплойт, был таким

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

void catcher(int a)
{
    setresuid(geteuid(),geteuid(),geteuid());
    printf("WIN!\n");
    system("/bin/sh");
    exit(0);
}

int main(int argc, char **argv)
{
    if (argc != 3 || !atoi(argv[2]))
            return 1;
    signal(SIGFPE, catcher);
    return abs(atoi(argv[1])) / atoi(argv[2]);
}

Что моя задача состоит в том, чтобы выполнить оболочку, которая выполняется из функции-ловушки, что означает, что если я каким-то образом смогу направить свой поток управления для запуска функции-ловушки - я готов. Один из методов, который я обнаружил для этого, заключался в том, чтобы использовать тот факт, что функция atoi принимает целые числа за вычетом минимального 32-битного знакового знака ie-2,147,483,647, поэтому мы можем предоставить первый аргумент как -2,147,483,649 и второй аргумент как -1 что, наконец, приведет к значению 2,147,483,649, передаваемому atoi, что приведет к SIGFPE. Этот подход прекрасно работает и на практике.

В чем я сомневаюсь, так это в том, что даже если мы используем описанный выше подход, оператор return выполняется после использования функции signal(). Как же тогда получается, что программа работает в обратном направлении на одну инструкцию и запускает функцию-обработчик catcher(), когда в использовании atoi присутствует SIGFPE?


person yellow_flash    schedule 01.02.2016    source источник
comment
Posix утверждает, что продолжение после SIGFPE является неопределенным поведением. Кроме того, printf и system не являются безопасными асинхронными функциями и не должны вызываться в обработчике сигналов.   -  person kaylum    schedule 01.02.2016
comment
signal просто устанавливает функцию, которая будет вызываться позже, когда произойдет сигнал. В основном это делает sigfpe_handler = &catcher; (где sigfpe_handler на самом деле является некоторой переменной, к которой вы не можете получить прямой доступ)   -  person user253751    schedule 01.02.2016
comment
@immibis, значит ли это, что при встрече с функцией signal () программа запускает другой поток, который затем ожидает возникновения указанного прерывания?   -  person yellow_flash    schedule 01.02.2016
comment
Я думаю, вы хотите использовать -2147483648 и -1 для запуска сигнала.   -  person e0k    schedule 01.02.2016
comment
@ShivanshRai значит ли это, что при встрече с функцией signal() программа запускает другой поток, который затем ожидает возникновения указанного прерывания? Нет. signal() устанавливает функцию, которая будет вызываться при появлении указанного сигнала отправляется в программу. Когда этот сигнал поднимается, текущий поток прерывается и вызывается обработчик сигнала, который был установлен при вызове signal().   -  person Andrew Henle    schedule 01.02.2016


Ответы (1)


Как же тогда получается, что программа работает в обратном направлении на одну инструкцию и запускает функцию-обработчик catcher(), когда в использовании atoi присутствует SIGFPE?

Это не так. signal(SIGFPE, catcher); говорит ядру Linux: «Эй, если я когда-нибудь получу SIGFPE, позвони catcher, хорошо?»

Затем, когда ваша программа получает SIGFPE, как вы и просили, ядро ​​обеспечивает вызов catcher вместо того, чтобы убивать вашу программу, что она обычно делала бы, если бы вы не попросили ее вызвать catcher.

person user253751    schedule 01.02.2016
comment
Теперь я понял, спасибо. Является ли сценарий таким же в случае блоков try-catch? Даже там мы указываем конкретные исключения, которые при обнаружении выполняют указанные инструкции, а не убивают программу. - person yellow_flash; 01.02.2016
comment
@ShivanshRai Они совершенно разные, но общая идея одна и та же. - person user253751; 01.02.2016