Неправильное употребление errno в обработчике сигнала

Вы читаете errno после вызова errno - устанавливающий функцию в обработчике сигнала

Описание

Неправильное употребление errno в обработчике сигнала происходит, когда вы вызываете одну из этих функций в обработчике сигнала:

  • signal: Вы вызываете функцию signal в обработчике сигнала и затем читаете значение errno.

    Например, функция-обработчик сигнала, handler вызывает signal и затем вызывает perror, который читает errno.

    void handler(int signum) {
      pfv old_handler = signal(signum, SIG_DFL);
      if (old_handler == SIG_ERR) {
        perror("SIGINT handler"); 
      }
    }

  • errno - установка функции POSIX®: Вы вызываете errno - функция установки POSIX в обработчике сигнала, но не восстанавливаете errno при возврате из обработчика сигнала.

    Например, функция-обработчик сигнала, handler вызывает waitpid, который изменяет errno, но не восстанавливает errno перед возвратом.

    void handler(int signum) {
      int rc = waitpid(-1, NULL, WNOHANG);
      if (ECHILD != errno) {
      }
    }

Риск

В каждом случае, который отмечает средство проверки, вы рискуете полагаться на неопределенное значение errno.

  • signal: Если вызов signal в обработчике сигнала перестал работать, значение errno неопределенно (см. Стандарт C11, Секунда. 7.14.1.1). Если вы полагаетесь на определенное значение errno, вы видите неожиданные результаты.

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

Фиксация

Избегайте ситуаций, где вы рискуете полагаться на неопределенное значение errno.

  • signal: После вызывания функции signal в обработчике сигнала не читайте errno или используйте функцию, которая читает errno.

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

Примеры

развернуть все

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

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        perror("SIGINT handler");
    }
}

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 0;
}

В этом примере функциональный handler называется, чтобы обработать сигнал SIGINT. В теле handler вызвана функция signal. После этого вызова значение errno неопределенно. Средство проверки повышает дефект, когда функция perror вызвана, потому что perror полагается на значение errno.

Исправление — старается не читать errno после вызова signal

Одно возможное исправление не должно читать errno после вызывания функции signal в обработчике сигнала. Исправленный код здесь вызывает функцию abort через макрос fatal_error вместо функции perror.

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

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        fatal_error();
    }
} 

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 0;
}

Информация о результате

Группа: Программирование
Язык: C | C++
Значение по умолчанию: на
Синтаксис командной строки: SIG_HANDLER_ERRNO_MISUSE
Влияние: носитель

Введенный в R2018a