Может ли некорректное чтение / запись вызвать ошибку SIGBUS? - PullRequest
1 голос
/ 25 июня 2019

РЕДАКТИРОВАТЬ 1 : Платформа x86_64 для примера программы.

РЕДАКТИРОВАТЬ 2: Я редактирую это для лучшего понимания. Ниже приведены два разных вопроса. Первый вопрос: может ли неправильное чтение / запись вызвать SIGBUS? и второй вопрос: пригодится ли Valgrind для анализа SIGBUS? Пример кода для второго вопроса подтверждает мое мнение о том, что Valgrind не будет полезен вообще в случае ошибки SIGBUS. Я могу ошибаться здесь.

Фактический сценарий: У нас есть приложение для чтения с экрана, которое дает сбой после 2 дней непрерывного тестирования (один раз сбой из-за SIGBUS). У меня есть файл coredump, но у меня нет нужных двоичных и отладочных пакетов. По сути, я должен проверить это в другом двоичном файле, и coredump не работает должным образом в GDB из-за несоответствия в пакетах отладки. Я могу видеть некоторые некорректные чтения / записи в модуле чтения с экрана во время анализа Valgrind. Мой товарищ по команде предположил, что исправление этих недопустимых операций чтения / записи решит эту проблему, но я думаю, что это не решит ее. Ниже мое понимание обоих сигналов.

SIGSEGV: Адрес действителен, но разрешения на чтение / запись отсутствуют.

SIGBUS: Сам адрес недействителен (ЦП не может найти адрес из-за неправильной привязки и т. Д.)

У меня есть вопрос, связанный с сигналом SIGBUS. Я искал похожие вопросы о переполнении стека, но не нашел четкого ответа на этот вопрос.

Может ли неправильное чтение / запись вызвать ошибку шины (SIGBUS)? .

Насколько я понимаю, неправильное чтение / запись всегда будет вызывать ошибку сегментации (SIGSEGV), и лучший способ исправить ошибку шины - запустить приложение gdb. Анализ Valgrind в случае ошибки шины не поможет. Ниже код объясняет это более подробно.

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

typedef struct {
char *name;
int val;
}data;

void fun1()
{
    data *ptr = malloc(sizeof(data));
    ptr->val = 100;
    ptr->name = "name in structure";

    printf("val:%d name:%s\n",ptr->val,ptr->name);
    free(ptr);
    ptr = NULL;
    printf("val:%d name:%s\n",ptr->val,ptr->name); //SIGSEGV
    return;
}

int fun2()
{
    #if defined(__GNUC__) 
    # if defined(__i386__) 
    /* Enable Alignment Checking on x86 */
    __asm__("pushf\norl $0x40000,(%esp)\npopf"); 
    # elif defined(__x86_64__)  
    /* Enable Alignment Checking on x86_64 */
    __asm__("pushf\norl $0x40000,(%rsp)\npopf"); 
    # endif 
    #endif 

    char *cptr = malloc(sizeof(int) + 1);
    char *optr = cptr;
    int *iptr = (int *) ++cptr; 
    *iptr = 42; //SIGBUS
    free(optr);

    return 0; 
}

void fun()
{
    fun2();
    //fun1();
}

int main()
{
    fun();
    return 0;
}

В случае ошибки сегментации в отчете Valgrind будут содержаться сведения о коде, вызывающем сбой, но в случае сбоя SIGBUS я не нашел таких подробностей в отчете Valgrind.

Отчет Valgrind для SIGSEGV:

==28128== Memcheck, a memory error detector
==28128== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28128== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28128== Command: ./a.out
==28128== Parent PID: 27953
==28128== 
==28128== Invalid read of size 8
==28128==    at 0x400619: fun1 (tmp.c:18)
==28128==    by 0x400695: fun (tmp.c:46)
==28128==    by 0x4006A6: main (tmp.c:51)
==28128==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==28128== 
==28128== 
==28128== Process terminating with default action of signal 11 (SIGSEGV)
==28128==  Access not within mapped region at address 0x0
==28128==    at 0x400619: fun1 (tmp.c:18)
==28128==    by 0x400695: fun (tmp.c:46)
==28128==    by 0x4006A6: main (tmp.c:51)
==28128==  If you believe this happened as a result of a stack
==28128==  overflow in your program's main thread (unlikely but
==28128==  possible), you can try to increase the size of the
==28128==  main thread stack using the --main-stacksize= flag.
==28128==  The main thread stack size used in this run was 8388608.
==28128== 
==28128== HEAP SUMMARY:
==28128==     in use at exit: 0 bytes in 0 blocks
==28128==   total heap usage: 2 allocs, 2 frees, 1,040 bytes allocated
==28128== 
==28128== All heap blocks were freed -- no leaks are possible
==28128== 
==28128== For counts of detected and suppressed errors, rerun with: -v
==28128== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Отчет Valgrind для SIGBUS:

==28176== Memcheck, a memory error detector
==28176== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28176== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28176== Command: ./a.out
==28176== Parent PID: 27953
==28176== 
==28176== 
==28176== HEAP SUMMARY:
==28176==     in use at exit: 0 bytes in 0 blocks
==28176==   total heap usage: 1 allocs, 1 frees, 5 bytes allocated
==28176== 
==28176== All heap blocks were freed -- no leaks are possible
==28176== 
==28176== For counts of detected and suppressed errors, rerun with: -v
==28176== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

1 Ответ

2 голосов
/ 25 июня 2019
int *iptr = (int *) ++cptr; 
*iptr = 42; //SIGBUS

нарушает несколько частей стандарта C.

Вы столкнулись с 6.3.2.3. Указатели , пункт 7 :

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

, а также нарушение правила строгого наложения 6,5 выражений , пункт 7 :

Объект должен иметь свое сохраненное значение, доступное только через выражение lvalue, которое имеет один из следующих типов:

  • тип, совместимый с эффективным типом объекта,
  • квалифицированная версия типа, совместимого с эффективным типом объекта,
  • тип, который является типом со знаком или без знака, соответствующим действующему типу объекта,
  • тип, который является типом со знаком или без знака, соответствующим квалифицированной версии действующего типа объекта,
  • агрегатный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих членов (включая, рекурсивно, член субагрегированного или автономного объединения), или
  • тип символа.

Per Документация Valgrind для Memcheck :

4,1. Обзор

Memcheck - детектор ошибок памяти. Он может обнаружить следующее проблемы, которые часто встречаются в программах на C и C ++.

  • Доступ к памяти вы не должны, например, переполнение и переполнение блоков кучи, переполнение вершины стека и доступ к памяти после освобождения.

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

  • Неправильное освобождение памяти кучи, например, двойное освобождение блоков кучи или несоответствующее использование malloc / new / new [] и бесплатно / удалить / удалить []

  • Перекрывающиеся указатели src и dst в memcpy и связанных с ними функциях.

  • Передача подозрительного (предположительно отрицательного) значения параметру размера функции выделения памяти.

  • Утечки памяти.

Обратите внимание, что ваш код

int *iptr = (int *) ++cptr; 
*iptr = 42; //SIGBUS

не делает ничего из того, что утверждает Valgrind. Вы не обращаетесь к памяти, к которой у вас нет прав доступа, а также к памяти за пределами региона, который вы создали с помощью malloc(). У вас еще не было free() памяти. У вас нет неинициализированных переменных, вы не удваиваете free() памяти и не используете memcpy() неправильно с перекрывающимися регионами источника и назначения. И вы не передаете отрицательные / "подозрительные" размеры функциям распределения. И у тебя нет утечки памяти.

Итак, нет, Valgrind даже не претендует на то, что сможет обнаружить код, который вызовет SIGBUS.

...