Ошибка сегментации

Examples of common segfaults

  • For example, calling as shown below would cause a program to segfault:

    memset((char *)0x0, 1, 100);
  • The following three cases illustrate the most common types of array-related segfaults:
    Case A
    /* "Array out of bounds" error valid indices for array foo are 0, 1, ... 999 */ int foo; for (int i = 0; i <= 1000 ; i++) foo = i;
    Case B
    /* Illegal memory access if value of n is not in the range 0, 1, ... 999 */ int n; int foo; for (int i = 0; i < n ; i++) foo = i;
    Case C
    /* Illegal memory access because no memory is allocated for foo2 */ float *foo, *foo2; foo = (float*)malloc(1000); foo2 = 1.0;
    • In case A, array is defined for . However, in the last iteration of the loop, the program tries to access . This will result in a segfault if that memory location lies outside the memory segment where resides. Even if it doesn’t cause a segfault, it is still a bug.
    • In case B, integer could be any random value. As in case A, if it is not in the range , it might cause a segfault. Whether it does or not, it is certainly a bug.
    • In case C, allocation of memory for variable has been overlooked, so will point to a random location in memory. Accessing will likely result in a segfault.
  • Another common programming error that leads to segfaults is oversight in the use of pointers. For example, the C function expects the address of a variable as its second parameter; therefore, the following will likely cause the program to crash with a segfault:

    int foo = 0; scanf("%d", foo); /* Note missing & sign ; correct usage would have been &foo */

    The variable might be defined at memory location , but the above function call would try to read integer data into memory location according to the definition of .

  • A segfault will occur when a program attempts to operate on a memory location in a way that is not allowed (for example, attempts to write a read-only location would result in a segfault).
  • Segfaults can also occur when your program runs out of stack space. This may not be a bug in your program, but may be due instead to your shell setting the stack size limit too small.

Что делать если возникла ошибка сегментирования?

Если вы думаете, что это ошибка в программе, то вам остается только отправить отчет об ошибке разработчикам. Но вы все-таки еще можете попытаться что-то сделать.

Например, если падает с ошибкой сегментации неизвестная программа, то мы можем решить что это вина разработчиков, но если с такой ошибкой падает chrome или firefox при запуске возникает вопрос, может мы делаем что-то не так? Ведь это уже хорошо протестированные программы.

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

Если это не помогло, нужно обнулить настройки программы до значений по умолчанию, возможно, удалить кэш. Настройки программ в Linux обычно содержатся в домашней папке, скрытых подкаталогах с именем программы. Также, настройки и кэш могут содержаться в каталогах ~/.config и ~/.cache. Просто удалите папки программы и попробуйте снова ее запустить. Если и это не помогло, вы можете попробовать полностью удалить программу, а потом снова ее установить, возможно, какие-нибудь зависимости были повреждены:

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

Когда вы все это выполнили, скорее всего, проблема не в вашем дистрибутиве, а в самой программе. Нужно отправлять отчет разработчикам. В Ubuntu это можно сделать с помощью программы apport-bug. Обычно Ubuntu предлагает это сделать сразу, после того как программа завершилась с ошибкой сегментирования. Если же ошибка сегментирования Ubuntu встречается не в системной программе, то вам придется самим искать разработчиков и вручную описывать что произошло.

Чтобы помочь разработчикам решить проблему, недостаточно отправить им только сообщение что вы поймали Segmentation Fault, нужно подробно описать проблему, действия, которые вы выполняли перед этим, так чтобы разработчик мог их воспроизвести. Также, желательно прикрепить к отчету последние функции, которые вызывала программа (стек вызовов функций), это может очень сильно помочь разработчикам.

Рассмотрим, как его получить. Это не так уж сложно. Сначала запустите вашу программу, затем узнайте ее PID с помощью команды:

Дальше запускаем отладчик gdb:

Подключаемся к программе:

После подключения программа станет на паузу, продолжаем ее выполнение командой:

Затем вам осталось только вызвать ошибку:

И набрать команду, которая выведет стек последних вызовов:

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

Дальше остается отправить отчет и ждать исправления ошибки. Если вы не уверены, что ошибка в программе, можете поспрашивать на форумах. Когда у вас есть стек вызовов, уже можно попытаться, если не понять в чем проблема, то попытаться узнать, не сталкивался ли с подобной проблемой еще кто-то.

Use debuggers to diagnose segfaults

If you can’t find the problem any other way, you might try a debugger. For example, you could use GNU’s well-known debugger to view the backtrace of a file dumped by your program; whenever programs segfault, they usually dump the content of (their section of the) memory at the time of the crash into a file. Start your debugger with the command , and then use the command to see where the program was when it crashed. This simple trick will allow you to focus on that part of the code.

If using on the g file doesn’t find the problem, you might have to run the program under debugger control, and then step through the code one function, or one source code line, at a time. To do this, you will need to compile your code without optimization, and with the flag, so information about source code lines will be embedded in the executable file. For more, see Step-by-step example for using GDB within Emacs to debug a C or
C++ program.

Пример Segmentation Fault

Рассмотрим пример кода на ANSI C, который приводит к ошибке сегментации вследствие присутствия квалификатора Сonst — type:

 const char *s = "hello world";
 *(char *)s = 'H';

Когда программа, содержащая этот код, скомпилирована, строка «hello world» размещена в секции программы с бинарной пометкой «только для чтения». При запуске операционная система помещает её с другими строками и константами в сегмент памяти, предназначенный только для чтения. После запуска переменная s указывает на адрес строки, а попытка присвоить значение символьной константы H через переменную в памяти приводит к ошибке сегментации.

Компиляция и запуск таких программ на OpenBSD 4.0 вызывает следующую ошибку выполнения:

 
$ gcc segfault.c -g -o segfault
$ ./segfault
 Segmentation fault

Вывод отладчика gdb:

 Program received signal SIGSEGV, Segmentation fault.
 0x1c0005c2 in main () at segfault.c:6
 6               *s = 'H';

В отличие от этого, gcc 4.1.1 на GNU/Linux возвращает ошибку ещё во время компиляции:

 $ gcc segfault.c -g -o segfault
 segfault.c: In function ‘main’:
 segfault.c:4: error: assignment of read-only location

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

 int* ptr = (int*);
 *ptr = 1;

Ещё один способ вызвать ошибку сегментации заключается в том, чтобы вызвать функцию main рекурсивно, что приведёт к переполнению стека:

int main()
 {
    main();
 }

Обычно, ошибка сегментации происходит потому что: указатель или нулевой, или указывает на произвольный участок памяти (возможно, потому что не был инициализирован), или указывает на удаленный участок памяти. Например:

 char* p1 = NULL;  /* инициализирован как нулевой, в чем нет ничего плохого, но на многих системах он не может быть разыменован */
 char* p2;  /* вообще не инициализирован */
 char* p3  = (char *)malloc(20);  /* хорошо, участок памяти выделен */
 free(p3);  /* но теперь его больше нет */

Теперь разыменование любого из этих указателей может вызвать ошибку сегментации.
Также, при использовании массивов, если случайно указать в качестве размера массива неинициализированную переменную. Вот пример:

 int main()
 { 
     int const nmax=10;
     int i,n,an];
 }

Такая ошибка не прослеживается G++ при компоновке, но при запуске приложения вызовет ошибку сегментации.

Видеопример Segmentation Fault на примере C:

Общие понятия

Сегментная адресация памяти  — схема логической адресации памяти компьютера в архитектуре x86. Линейный адрес конкретной ячейки памяти, который в некоторых режимах работы процессора будет совпадать с физическим адресом, делится на две части: сегмент и смещение. Сегментом называется условно выделенная область адресного пространства определённого размера, а смещением — адрес ячейки памяти относительно начала сегмента. Базой сегмента называется линейный адрес (адрес относительно всего объёма памяти), который указывает на начало сегмента в адресном пространстве. В результате получается сегментный (логический) адрес, который соответствует линейному адресу база сегмента+смещение и который выставляется процессором на шину адреса.

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

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

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

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

Страницы — это области физической памяти фиксированного размера, мельчайшая и неделимая единица памяти, которой может оперировать ОС.

На аппаратном уровне, неисправность возникает по причине реагирования блока управления памяти (MMU) на неправомерный доступ к памяти.

На уровне операционной системы эта ошибка ловится и сигнал передается в блок «offending process», где эта ошибка обрабатывается:

  • В UNIX-подобных операционных системах процесс, обращающийся к недействительным участкам памяти, получает сигнал «SIGSEGV».
  • В Microsoft Windows, процесс, получающий доступ к недействительным участкам памяти, создаёт исключение «STATUS_ACCESS_VIOLATION», и, как правило, предлагает запустить отладчик приложения Dr. Watson, которая показывает пользователю окно с предложением отправить отчет об ошибке Microsoft.

Суммирую можно сказать, когда пользовательский процесс хочет обратиться к памяти, то он просит MMU переадресовать его. Но если полученный адрес ошибочен, — находится вне пределов физического сегмента, или если сегмент не имеет нужных прав (попытка записи в read only-сегмент), — то ОС по умолчанию отправляет сигнал SIGSEGV, что приводит к прерыванию выполнения процесса и выдаче сообщения “segmentation fault”.

Пример

Вот пример кода ANSI C, который приводит к ошибке сегментации из-за присутствия квалификатора типа :

const char * s = "hello world";
* (char *) s = 'H';

Когда программа, содержащая этот код, скомпилирована, строка размещена в секции программы с бинарной пометкой «только для чтения». При запуске операционная система помещает её с другими строками и константами в сегмент памяти, предназначенный только для чтения. После запуска переменная указывает на адрес начала строки, а попытка присвоить значение символьной константы через переменную в памяти приводит к ошибке сегментации.

Компиляция и запуск таких программ на OpenBSD 4.0 вызывает следующую ошибку выполнения:

$ gcc segfault.c -g -o segfault
$ ./segfault
Segmentation fault

Вывод отладчика gdb:

Program received signal SIGSEGV, Segmentation fault.
0x1c0005c2 in main () at segfault.c:6
6 *s = 'H';

В отличие от этого, GCC 4.1.1 на GNU/Linux возвращает ошибку ещё во время компиляции:

$ gcc segfault.c -g -o segfault
segfault.c: In function 'main':
segfault.c:4: error: assignment of read-only location

Условия, при которых происходят нарушения сегментации, и способы их проявления зависят от операционной системы.

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

int * ptr = (int *) ;
*ptr = 1;

Ещё один способ вызвать ошибку сегментации заключается в том, чтобы вызвать функцию , что приведёт к переполнению стека:

int main()
{
    main();
}

Обычно ошибка сегментации происходит потому, что:

  • указатель нулевой,
  • указатель указывает на произвольный участок памяти (возможно потому, что не был инициализирован),
  • указатель указывает на удалённый участок памяти.

Например,

char * p1 = NULL; /* инициализирован как нулевой; это допускается, но на многих системах он не может быть разыменован */
char * p2; /* вообще не инициализирован (указывает на произвольный адрес в памяти) */
char * p3 = (char *) malloc(20); /* хорошо, участок памяти выделен */
free(p3); /* но теперь его больше нет */

Теперь разыменование любого из этих указателей может вызвать ошибку сегментации.

Или при использовании массивов, если случайно указать в качестве размера массива неинициализированную переменную:

int main()
{
    const int nmax = 10;
    int i, n, an];
}

Компилятор G++ не прослеживает такую ошибку при компоновке, что при запуске скомпилированной программы может вызвать ошибку сегментации.

Overview

A segmentation fault (aka segfault) is a common condition that causes programs to crash; they are often associated with a file named . Segfaults are caused by a program trying to read or write an illegal memory location.

Program memory is divided into different segments: a text segment for program instructions, a data segment for variables and arrays defined at compile time, a stack segment for temporary (or automatic) variables defined in subroutines and functions, and a heap segment for variables allocated during runtime by functions, such as (in C) and (in Fortran). For more, see About program segments.

A segfault occurs when a reference to a variable falls outside the segment where that variable resides, or when a write is attempted to a location that is in a read-only segment. In practice, segfaults are almost always due to trying to read or write a non-existent array element, not properly defining a pointer before using it, or (in C programs) accidentally using a variable’s value as an address ().

Пример

Вот пример кода ANSI C, который приводит к ошибке сегментации из-за присутствия квалификатора типа :

const char * s = "hello world";
* (char *) s = 'H';

Когда программа, содержащая этот код, скомпилирована, строка размещена в секции программы с бинарной пометкой «только для чтения». При запуске операционная система помещает её с другими строками и константами в сегмент памяти, предназначенный только для чтения. После запуска переменная указывает на адрес начала строки, а попытка присвоить значение символьной константы через переменную в памяти приводит к ошибке сегментации.

Компиляция и запуск таких программ на OpenBSD 4.0 вызывает следующую ошибку выполнения:

$ gcc segfault.c -g -o segfault
$ ./segfault
Segmentation fault

Вывод отладчика gdb:

Program received signal SIGSEGV, Segmentation fault.
0x1c0005c2 in main () at segfault.c:6
6 *s = 'H';

В отличие от этого, GCC 4.1.1 на GNU/Linux возвращает ошибку ещё во время компиляции:

$ gcc segfault.c -g -o segfault
segfault.c: In function 'main':
segfault.c:4: error: assignment of read-only location

Условия, при которых происходят нарушения сегментации, и способы их проявления зависят от операционной системы.

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

int * ptr = (int *) ;
*ptr = 1;

Ещё один способ вызвать ошибку сегментации заключается в том, чтобы вызвать функцию , что приведёт к переполнению стека:

int main()
{
    main();
}

Обычно ошибка сегментации происходит потому, что:

  • указатель нулевой,
  • указатель указывает на произвольный участок памяти (возможно потому, что не был инициализирован),
  • указатель указывает на удалённый участок памяти.

Например,

char * p1 = NULL; /* инициализирован как нулевой; это допускается, но на многих системах он не может быть разыменован */
char * p2; /* вообще не инициализирован (указывает на произвольный адрес в памяти) */
char * p3 = (char *) malloc(20); /* хорошо, участок памяти выделен */
free(p3); /* но теперь его больше нет */

Теперь разыменование любого из этих указателей может вызвать ошибку сегментации.

Или при использовании массивов, если случайно указать в качестве размера массива неинициализированную переменную:

int main()
{
    const int nmax = 10;
    int i, n, an];
}

Компилятор G++ не прослеживает такую ошибку при компоновке, что при запуске скомпилированной программы может вызвать ошибку сегментации.

Почему возникает ошибка сегментации?

И зачем бы это порядочной программе лезть, куда ей не положено? Да в принципе, незачем. Это происходит из-за ошибки при написании программ или несовместимых версиях библиотек и ПО. Часто эта ошибка встречается в программах на Си или C++. В этом языке программисты могут вручную работать с памятью, а язык со своей стороны не контролирует, чтобы они это делали правильно, поэтому одно неверное обращение к памяти может обрушить программу.

Почему может возникать эта ошибка при несовместимости библиотек? По той же причине — неверному обращению к памяти. Представим, что у нас есть библиотека linux (набор функций), в которой есть функция, которая выполняет определенную задачу. Для работы нашей функции нужны данные, поэтому при вызове ей нужно передать строку. Наша старая версия библиотеки ожидает, что длина строки будет до 256 символов. Но программа была обновлена формат записи поменялся, и теперь она передает библиотеке строку размером 512 символов. Если обновить программу, но оставить старую версию библиотеки, то при передаче такой строки 256 символов запишутся нормально в подготовленное место, а вот вторые 256 перезапишут данные программы, и возможно, попытаются выйти за пределы сегмента, тогда и будет ошибка сегментирования linux.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector