Странный сегфолт.

Модератор: Модераторы разделов

sciko
Сообщения: 1744
Статус: Ъ-участник
ОС: Debian/Ubuntu/etc

Странный сегфолт.

Сообщение sciko »

Копался тут в своей программке на С и обнаружил весьма занятную вещь.

Вот тестовая программа:

Код: Выделить всё

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

int func (double gamma[8])
{
  gamma[1] = 1;
  gamma[9] = -1;
  return (0);
}

int main(int argc, char **argv)
{
  double gamma [7];

  gamma [0] = 2;
  gamma [8] = -2;
  printf ("%lf %lf %lf\n",gamma [0], gamma[1], gamma [8]);
  func (gamma);
  printf ("%lf %lf %lf %lf\n",gamma [0], gamma[1], gamma [8], gamma[9] );

  return (0);
}


Собирал gcc 4.4.3:
$ cc -Wall -g -c -o test.o test.c
$ cc test.o -o test
$ ./test
2.000000 0.000000 -2.000000
2.000000 1.000000 -2.000000 -1.000000
Ошибка сегментирования

По идее сегфолт должен быть уже на строчке gamma [8] = -2; Но программа выполняется фактически до конца!

Если же предположить, что gcc использовал оптимизацию и в printf подставлял сразу констаты, то непонятно откуда сегфолт.

Что за фигня?
Спасибо сказали:
Аватара пользователя
Фантом
Сообщения: 462
ОС: openSUSE

Re: Странный сегфолт.

Сообщение Фантом »

sciko писал(а):
03.01.2012 16:32
По идее сегфолт должен быть уже на строчке gamma [8] = -2; Но программа выполняется фактически до конца!

Если же предположить, что gcc использовал оптимизацию и в printf подставлял сразу констаты, то непонятно откуда сегфолт.

Что за фигня?

При обращении к отсутствующим элементам были переписаны поверх какие-то другие данные, относящиеся к программе, и обнаружилось это при выгрузке программы из памяти. В принципе, картина достаточно стандартная.
Спасибо сказали:
sciko
Сообщения: 1744
Статус: Ъ-участник
ОС: Debian/Ubuntu/etc

Re: Странный сегфолт.

Сообщение sciko »

Фантом писал(а):
03.01.2012 17:12
В принципе, картина достаточно стандартная.
И как с эти бороться?
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Странный сегфолт.

Сообщение NickLion »

При записи gamma[8] = -2 вы просто перезаписали часть стека - никакого сегфолта тут не будет. А вот при завершении функции как раз сегфолт и вылезет - например при этом был перезаписан адрес возврата и управление будет передано неизвестно куда.

sciko писал(а):
03.01.2012 17:28
И как с эти бороться?

Проверять границы, valgrind memcheck и другие средства.
Спасибо сказали:
sciko
Сообщения: 1744
Статус: Ъ-участник
ОС: Debian/Ubuntu/etc

Re: Странный сегфолт.

Сообщение sciko »

NickLion писал(а):
03.01.2012 17:43
А вот при завершении функции как раз сегфолт и вылезет - например при этом был перезаписан адрес возврата и управление будет передано неизвестно куда.
Тогда получается, что, если дописать после double gamma [7]; объявление ещё одной переменной, то даже сегфолта не будет.

NickLion писал(а):
03.01.2012 17:43
Проверять границы, valgrind memcheck и другие средства.
Это-то понятно.

Непонятны условия возникновения сегфолта. Просто я уже несколько раз ловил и исправлял в FOSS проектах сегфолты, связанные как раз с выходом за границы. Причём gdb по bt точно показывал место ошибки. Чем же тот выход за границу отличался от моего?
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Странный сегфолт.

Сообщение NickLion »

sciko писал(а):
03.01.2012 18:01
объявление ещё одной переменной, то даже сегфолта не будет.

Двух:

Код: Выделить всё

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

int func (double gamma[8])
{
  gamma[1] = 1;
  gamma[9] = -1;
  return (0);
}

int main(int argc, char **argv)
{
  double gamma [7];
  double x, y;
  gamma [0] = 2;
  gamma [8] = -2;
  printf ("%lf %lf %lf\n",gamma [0], gamma[1], gamma [8]);
  func (gamma);
  printf ("%lf %lf %lf %lf\n",gamma [0], gamma[1], gamma [8], gamma[9] );
  printf ("%lf %lf\n", x, y);
  return (0);
}

И никакого сегфолта.

sciko писал(а):
03.01.2012 18:01
Непонятны условия возникновения сегфолта. Просто я уже несколько раз ловил и исправлял в FOSS проектах сегфолты, связанные как раз с выходом за границы. Причём gdb по bt точно показывал место ошибки. Чем же тот выход за границу отличался от моего?

Ну, давайте посмотрим что вообще такое ошибка сегментации - это попытка записи(чтения, исполнения) в(из) память, где данная операция недопустима. Соответственно поведение когда возникнет и возникнет ли сегфолт вообще очень сильно зависит от расположения участка в памяти, что рядом лежит, насколько далеко выход за границы происходит и т.д. В случае стека и незначительного выхода за границы чаще всего страдают либо соседние переменные, либо адрес возврата, либо переменные вызвавшей функции. При значительном отклоненнии (gamma[8000]=-2) сегфолт сразу будет. Если в динамической памяти, то там как получится. Статическая память - либо другую статическую переменную (без сегфолта, если переменная доступна для записи) либо в RO секцию обычно попадаем (тут сегфолт при записи будет сразу).
Спасибо сказали:
MiK13
Сообщения: 1281
ОС: Linux Debian

Re: Странный сегфолт.

Сообщение MiK13 »

Решил проверить этот пример у себя. Собственно, сегфолт меня не удивляет -- тут уже описали причины, по которым он может возникнуть. Меня удивляет другое:
Почему компилятор, несмотря на -Wall ничего не говорит о присвоении значений элементам с номерами на 2 больше, чем можно?
И, кстати, когда вынес объявление gamma из блока main, то тогда никакого сегфолта не происходит. Правда, компилятор стал ворчать, что "встроенная функция gamma объявлена как не-функция. Заменил gamma на gama -- программа отработала без проблем (это меня не удивило)

P.S. Сделал ещё одну проверку: при компиляции указал оптимизацию.
При ключах -O2 и выше компилятор стал ворчать:
warning: array subscript is above array bounds
Без -O и с -O0 и -O1 транслирует молча
Спасибо сказали: