Странное переполнение стека?

Я столкнулся со странной ситуацией при передаче указателя на структуру с очень большим массивом, определенным в определении struct {}, массивом с плавающей запятой размером около 34 МБ. Вкратце псевдокод выглядит так:

typedef config_t{
  ...
  float values[64000][64];
} CONFIG;


int32_t Create_Structures(CONFIG **the_config)
{
  CONFIG  *local_config;
  int32_t number_nodes;

  number_nodes = Find_Nodes();

  local_config = (CONFIG *)calloc(number_nodes,sizeof(CONFIG));
  *the_config = local_config;
  return(number_nodes);
}


int32_t Read_Config_File(CONFIG *the_config)
{
    /* do init work here */
    return(SUCCESS);
}


main()
{
    CONFIG *the_config;
    int32_t number_nodes,rc;

    number_nodes = Create_Structures(&the_config);

    rc = Read_Config_File(the_config);
    ...
    exit(0);
}

Код компилируется нормально, но когда я пытаюсь его запустить, я получаю SIGSEGV в { под Read_Config_File().

(gdb) run
...
Program received signal SIGSEGV, Segmentation fault.
0x0000000000407d0a in Read_Config_File (the_config=Cannot access memory at address 0x7ffffdf45428
) at ../src/config_parsing.c:763
763 {
(gdb) bt
#0  0x0000000000407d0a in Read_Config_File (the_config=Cannot access memory at address 0x7ffffdf45428
) at ../src/config_parsing.c:763
#1  0x00000000004068d2 in main (argc=1, argv=0x7fffffffe448) at ../src/main.c:148

Я делал такие вещи все время, с меньшими массивами. И как ни странно, 0x7fffffffe448 - 0x7ffffdf45428 = 0x20B8EF8, или примерно 34 МБ моего массива с плавающей запятой.

Valgrind даст мне аналогичный вывод:

==10894== Warning: client switching stacks?  SP change: 0x7ff000290 --> 0x7fcf47398
==10894==          to suppress, use: --max-stackframe=34311928 or greater
==10894== Invalid write of size 8
==10894==    at 0x407D0A: Read_Config_File (config_parsing.c:763)
==10894==    by 0x4068D1: main (main.c:148)
==10894==  Address 0x7fcf47398 is on thread 1's stack

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

Может ли кто-нибудь помочь мне с этим? Я использую 64-битную систему CentOS с ядром 2.6.18 и gcc 4.1.2.

Спасибо!

Мэтт


person tranzmatt    schedule 19.01.2012    source источник
comment
Публикация псевдокода даст вам только псевдоответы. Дьявол кроется в деталях, и, вероятно, все они будут иметь значение.   -  person Jonathan Leffler    schedule 20.01.2012
comment
Можем ли мы увидеть источник Read_Config_File? Вот где проблема, кажется, в блоке, который вы упустили.   -  person Borealid    schedule 20.01.2012
comment
Вы не тестируете возвращаемое значение calloc(), что может привести к ошибке.   -  person Quentin Casasnovas    schedule 20.01.2012
comment
@tranzmatt, не могли бы вы вставить источник Read_Config_File, как сказал @Borealid, а также код между вызовами Create_Structures и Read_Config_File, если они есть?   -  person ouah    schedule 20.01.2012
comment
Read_Config_File может списывать конец массива.   -  person MRAB    schedule 20.01.2012


Ответы (2)


Вы взорвали стек, разместив в нем одну из этих огромных структур config_t. Два указателя стека в выводе gdb, 0x7fffffffe448 и 0x7ffffdf45428, очень наводят на мысль об этом.

$ gdb
GNU gdb 6.3.50-20050815 ...blahblahblah...
(gdb) p 0x7fffffffe448 - 0x7ffffdf45428  
$1 = 34312224

Вот ваша константа ~34 МБ, которая соответствует размеру структуры config_t. Системы по умолчанию не предоставляют вам столько места в стеке, поэтому либо удалите объект из стека, либо увеличьте пространство в стеке.

person Kyle Jones    schedule 20.01.2012
comment
Есть ли инструмент, похожий на lint, который может сказать мне, сколько стека функция собирается сожрать? Оглядываясь назад, теперь довольно очевидно, в чем заключалась неисправность, но я хотел бы знать заранее, существуют ли такие проблемы. - person tranzmatt; 20.01.2012
comment
gcc имеет -fstack-limit-* для проверок во время выполнения, но я не знаю ничего, что предупреждало бы о чрезмерно больших выделениях стека во время компиляции. - person Kyle Jones; 20.01.2012

Короткий ответ заключается в том, что где-то должна быть объявлена ​​config_t как локальная переменная, которая поместит ее в стек. Вероятно, опечатка: где-то отсутствует * после объявления CONFIG.

person Crashworks    schedule 19.01.2012
comment
В этом была проблема. Я забыл, что хранил временную копию структуры config_t в функции перед тем, как запихнуть внутрь массив. Я переместил массив в другое место, и теперь он не ошибается. Спасибо. - person tranzmatt; 20.01.2012