snprintf в обработчике сигналов создает ошибку сегментации, если запущен с valgrind - PullRequest
5 голосов
/ 20 июля 2011

Эта очень простая программа на c выдает ошибку сегментации при запуске ее с valgrind.Он работает нормально, когда начал нормально.Сбой при отправке сигнала USR1 процессу.

Кажется, проблема в том, как printf обрабатывает форматирование значения с плавающей запятой, потому что он работает нормально, если вы используете строку (% s) или int (% d) параметр формата.

PS Я знаю, вам не следует вызывать какую-либо функцию семейства printf в обработчике сигналов, но все же, почему она вызывает сбой только при использовании valgrind.

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

    void sig_usr1(int sig) {
            char buf[128];
            snprintf(buf, sizeof(buf), "%f", 1.0);
    }

    int main(int argc, char **argv) {
            (void) signal(SIGUSR1, sig_usr1);
            while(1);
    }

Ответы [ 4 ]

4 голосов
/ 20 июля 2011

Как отмечает cnicutar , valgrind может влиять на все, что связано с синхронизацией, и обработчики сигналов, безусловно, подойдут.

Я не думаю, что snprintf безопасно использовать в обработчике сигналов, поэтому он может работать в случае non-valgrind исключительно случайно, а затем входит valgrind, меняет время и вы получаете пламенную смерть, которая ты рисковал без валигринда.

Я нашел список функций, которые безопасны в обработчиках сигналов (согласно POSIX.1-2003) здесь:

http://linux.die.net/man/2/signal

Да, справочные страницы linux.die.net немного устарели, но список здесь (спасибо RedX за то, что нашли это):

https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers

также не упоминает snprintf кроме как в контексте OpenBSD, где говорится:

... асинхронно-безопасный в OpenBSD, но "вероятно, не в других системах", включая snprintf (), ...

Таким образом, подразумевается, что snprintf, как правило, небезопасен в обработчике сигналов.

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

http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03

Начните с этой ссылки и найдите вниз _Exit, и вы увидите список; тогда вы увидите, что snprintf нет в списке.

Также я помню, как использовал write() в обработчике сигналов, потому что fprintf не был безопасен для обработчика сигналов, но это было давно.

У меня нет копии соответствующего стандарта, поэтому я не могу подкрепить это чем-то действительно авторитетным, но я все равно упомянул об этом.

4 голосов
/ 21 июля 2011

Из руководства: http://www.network -theory.co.uk / docs / valgrind / valgrind_27.html и http://www.network -theory.co.uk / docs / valgrind / valgrind_24.html

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

Таким образом, snprintf в обработчике сигналов не является разрешенным сигналом POSIX, и valgrind имеет право блокировать ваши программы.

Почему snprintf не защищен от сигналов?

Руководство glibc гласит: http://www.gnu.org/software/hello/manual/libc/Nonreentrancy.html

Если функция использует и изменяет объект, который вы предоставляете, то он потенциально не реентерабелен; два вызова могут помешать, если они используют один и тот же объект.

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

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

Можно сказать, что s * printf * не в потоках, а в строках. Но внутри, snprintf в glibc работает со специальным потоком:

FTP: //sources.redhat.com/pub/glibc/snapshots/glibc-latest.tar.bz2/glibc-20090518/libio/vsnprintf.c

int
_IO_vsnprintf (string, maxlen, format, args)
{
  _IO_strnfile sf; // <<-- FILE*-like descriptor

Выходной код %f в glibc также содержит вызов malloc:

FTP: //sources.redhat.com/pub/glibc/snapshots/glibc-latest.tar.bz2/glibc-20090518/stdio-common/printf_fp.c

/* Allocate buffer for output.  We need two more because while rounding
   it is possible that we need two more characters in front of all the
   other output.  If the amount of memory we have to allocate is too
   large use `malloc' instead of `alloca'.  */
size_t wbuffer_to_alloc = (2 + (size_t) chars_needed) * sizeof (wchar_t);
buffer_malloced = ! __libc_use_alloca (chars_needed * 2 * sizeof (wchar_t));
if (__builtin_expect (buffer_malloced, 0))
  {
    wbuffer = (wchar_t *) malloc (wbuffer_to_alloc);
    if (wbuffer == NULL)
      /* Signal an error to the caller.  */
      return -1;
  }
else
  wbuffer = (wchar_t *) alloca (wbuffer_to_alloc);
2 голосов
/ 20 июля 2011

Valgrind немного меняет время в вашей программе.

Имейте лут на FAQ .

Моя программа нормально падает, но не под Valgrind, или наоборот Versa . Что происходит?

Когда программа работает под Valgrind, ее среда немного отличается от того, когда он работает изначально. В большинстве случаев это не имеет значения, но может, особенно если ваша программа глючит.

1 голос
/ 22 июля 2011

Это ошибка Valgrind.Он вызывает ваш обработчик сигнала со стеком, который не выровнен по 16 байтов, как того требует ABI.На x86_64 аргументы с плавающей точкой передаются в регистрах XMM, которые могут храниться только по адресам, которые выровнены по 16 байтов.Вы можете обойти эту проблему, компилируя для 32-битной (gcc -m32).

...