Как malloc () может вызвать SIGSEGV? - PullRequest
8 голосов
/ 17 сентября 2009

У меня странная ошибка в моей программе, мне кажется, что malloc () вызывает SIGSEGV, что, насколько я понимаю, не имеет никакого смысла. Я использую библиотеку под названием simclist для динамических списков.

Вот структура, на которую ссылаются позже:

typedef struct {
    int msgid;
    int status;
    void* udata;
    list_t queue;
} msg_t;

А вот и код:

msg_t* msg = (msg_t*) malloc( sizeof( msg_t ) );

msg->msgid = msgid;
msg->status = MSG_STAT_NEW;
msg->udata = udata;
list_init( &msg->queue );

list_init - там, где происходит сбой программы, вот код для list_init:

/* list initialization */
int list_init(list_t *restrict l) {
    if (l == NULL) return -1;

    srandom((unsigned long)time(NULL));

    l->numels = 0;

    /* head/tail sentinels and mid pointer */
    l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->head_sentinel->next = l->tail_sentinel;
    l->tail_sentinel->prev = l->head_sentinel;
    l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
    l->head_sentinel->data = l->tail_sentinel->data = NULL;

    /* iteration attributes */
    l->iter_active = 0;
    l->iter_pos = 0;
    l->iter_curentry = NULL;

    /* free-list attributes */
    l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
    l->spareelsnum = 0;

#ifdef SIMCLIST_WITH_THREADS
    l->threadcount = 0;
#endif

    list_attributes_setdefaults(l);

    assert(list_repOk(l));
    assert(list_attrOk(l));

    return 0;
}

строка l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * - это то, где SIGSEGV вызывается в соответствии с трассировкой стека. Я использую GDB / Nemiver для отладки, но я в растерянности. В первый раз, когда эта функция вызывается, она работает нормально, но всегда выходит из строя во второй раз. Как malloc () может вызвать SIGSEGV?

Это трассировка стека:

#0  ?? () at :0
#1  malloc () at :0
#2  list_init (l=0x104f290) at src/simclist.c:205
#3  msg_new (msg_switch=0x1050dc0, msgid=8, udata=0x0) at src/msg_switch.c:218
#4  exread (sockfd=8, conn_info=0x104e0e0) at src/zimr-proxy/main.c:504
#5  zfd_select (tv_sec=0) at src/zfildes.c:124
#6  main (argc=3, argv=0x7fffcabe44f8) at src/zimr-proxy/main.c:210

Любая помощь или понимание очень ценится!

Ответы [ 6 ]

26 голосов
/ 18 сентября 2009

malloc может вызвать ошибку, например, когда куча повреждена. Убедитесь, что вы не пишете ничего, выходящего за рамки предыдущего выделения.

17 голосов
/ 18 сентября 2009

Возможно нарушение памяти происходит в другой части вашего кода. Если вы работаете в Linux, вам обязательно стоит попробовать valgrind. Я бы никогда не поверил своим собственным программам на Си, если бы они не проходили через valgrind.

РЕДАКТИРОВАТЬ: еще один полезный инструмент Электрический забор . Glibc также предоставляет переменную окружения MALLOC_CHECK_ , чтобы помочь отладить проблемы с памятью. Эти два метода не влияют на скорость бега так сильно, как valgrind.

12 голосов
/ 18 сентября 2009

Возможно, вы где-то перед этим вызовом испортили кучу из-за переполнения буфера или вызова free с указателем, который не был выделен malloc (или уже освобожден).

Если внутренние структуры данных, используемые malloc, будут повреждены таким образом, malloc использует недопустимые данные и может произойти сбой.

4 голосов
/ 18 сентября 2009

Существует множество способов запуска дампа ядра из malloc()realloc() и calloc()). К ним относятся:

  • Переполнение буфера: запись за пределами выделенного пространства (вытаптывание управляющей информации, которую там хранил malloc()).
  • Переполнение буфера: запись перед началом выделенного пространства (вытащить управляющую информацию, которую там хранил malloc()).
  • Освобождение памяти, которая не была выделена malloc(). В смешанной программе на C и C ++ это будет включать освобождение памяти, выделенной в C ++ на new.
  • Освобождение указателя, который частично указывает путь к блоку памяти, выделенному malloc() - это особый случай предыдущего случая.
  • Освобождение указателя, который уже был освобожден - пресловутое «двойное освобождение».

Использование диагностической версии malloc() или включение диагностики в стандартной версии вашей системы может помочь выявить некоторые из этих проблем. Например, он может обнаруживать небольшие переполнения и переполнения (поскольку он выделяет дополнительное пространство для обеспечения буферной зоны вокруг запрошенного вами пространства) и может, вероятно, обнаруживать попытки освободить память, которая не была выделена или уже была освобождена или указатели частично проходят через выделенное пространство - потому что они будут хранить информацию отдельно от выделенного пространства. Стоимость заключается в том, что отладочная версия занимает больше места. Действительно хороший распределитель сможет записывать трассировку стека и номера строк, чтобы сообщить вам, где произошло выделение в вашем коде или где произошло первое освобождение.

1 голос
/ 18 сентября 2009

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

Это означает:

# 1: скомпилировать код с -O0, чтобы убедиться, что GDB получает правильную информацию о нумерации строк.

# 2: написать модульный тест, который вызывает эту часть кода.

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

Использование Valgrind, как предлагали другие, также очень хорошая идея.

0 голосов
/ 27 сентября 2009

Код проблемный. Если malloc возвращает NULL, этот случай неправильно обрабатывается в вашем коде. Вы просто предполагаете, что память была выделена для вас, когда это на самом деле не было. Это может привести к повреждению памяти.

...