Отказ сегментации в strcpy

Традиционный подход заключается в тестировании вашего проекта в виртуальной среде:

  1. создайте виртуальную среду
  2. запустите ваш проект и установите необходимые зависимости, например > conda install
  3. сохранить зависимости в requirements.txt с помощью команды OP

Это создаст изолированный requirements.txt.

См. Также pipenv и poetry . Эти инструменты автоматически создают виртуальные среды вашего проекта и надежно отслеживают ваши зависимости.

6
задан Mikeage 6 April 2009 в 04:20
поделиться

8 ответов

Существует три типа поведения стандартов, которым необходимо интересоваться.

1/Определенное поведение. Это будет работать над всеми соответствующими реализациями. Используйте это свободно.

2/определенное Реализацией поведение. Как указано, это зависит от реализации, но по крайней мере это все еще определяется. Реализации требуются, чтобы документ, что они делают в этих случаях. Используйте это, если Вы не заботитесь о мобильности.

3/Неопределенное поведение. Что-либо может произойти. И мы имеем в виду что-либо, до и включая Ваш весь компьютер, выходящий из строя в явную особенность и глотающий себя, Вас и значительную долю Ваших коллег. Никогда не используйте это. Когда-либо! Серьезно! Не заставляйте меня приехать туда.

Копирование больше, что 4 символа и нулевой байт к a char[5] неопределенное поведение.

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

Увеличьте размер массива к чему-то более подходящему (char[14] в этом случае с доступной информацией) или использование некоторая другая структура данных, которая может справиться.


Обновление:

Так как Вы кажетесь настолько обеспокоенными обнаружением, почему дополнительные 7, символы не вызывают проблемы, но 8 символов, делают, давайте предусмотрим возможное расположение стека на вводе main(). Я говорю "возможный", так как фактическое расположение зависит от соглашения о вызовах, которое использует Ваш компилятор. Так как C запускают вызовы кода main() с argc и argv, стек в начале main(), после выделения места для a char[5], мог быть похожим на это:

+------------------------------------+
| C start-up code return address (4) |
| argc (4)                           |
| argv (4)                           |
| x = char[5] (5)                    |
+------------------------------------+

Когда Вы пишете байты Hello1234567\0 с:

strcpy (x, "Hello1234567");

кому: x, это перезаписывает argc и argv но, по возврату из main(), это хорошо. Конкретно Hello заполняет x, 1234 заполняет argv и 567\0 заполняет argc. Если Вы на самом деле не пытаетесь использовать argc и/или argv после этого Вы будете хорошо:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4)                           |   '567<NUL>'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

Однако, если Вы пишете Hello12345678\0 (отметьте дополнительное "8") к x, это перезаписывает argc и argv и также один байт обратного адреса так, чтобы, когда main() попытки возвратиться к C запускают код, он уходит в волшебную землю вместо этого:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |   '<NUL>'
| argc (4)                           |   '5678'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

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

Это - то, под чем они подразумевают неопределенный: Вы не знаете то, что собирается произойти.

31
ответ дан 8 December 2019 в 02:08
поделиться

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

Некоторые компиляторы могли бы произвести код, который откажет только с единственным байтом по размеру буфера - это не определено, каково поведение.

Я предполагаю, что размера 13 достаточно для перезаписи обратного адреса или чего-то подобного, которое отказывает, когда функция возвращается. Но другой компилятор или другая платформа могли / катастрофический отказ с другой длиной.

Также Ваша программа могла бы отказать с другой длиной, если бы она работала в течение более длительного времени, если что-то менее важное перезаписывалось.

7
ответ дан 8 December 2019 в 02:08
поделиться

Это зависит от того, что находится на стеке после массива "ул.". Вы просто, оказывается, не топчете что-либо критическое, пока Вы не копируете это много символов.

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

13 5 + 8, предполагая, что существует два некритических слова после массива ул., затем что-то критическое (возможно, обратный адрес)

1
ответ дан 8 December 2019 в 02:08
поделиться

Для 32-разрядной платформы Intel объяснение следующее. Когда Вы объявляете символ [5] на стеке, компилятор действительно выделяет 8 байтов из-за выравнивания. Затем это типично для функций, чтобы иметь следующий пролог:

push ebp
mov ebp, esp

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

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

Таким образом, у Вас есть следующее расположение (стек растет вниз на Intel): 8 байтов для Вашего массива, затем 4 байта для ebp, затем обычно обратный адрес.

Поэтому необходимо перезаписать по крайней мере 13 байтов для катастрофического отказа программы.

5
ответ дан 8 December 2019 в 02:08
поделиться

Добавить к вышеупомянутым ответам: можно протестировать на ошибки как они с инструментом, такие как Valgrind. Если Вы находитесь в Windows, взглянули на это, ТАК распараллельте.

2
ответ дан 8 December 2019 в 02:08
поделиться

Это - чистая красота неопределенного поведения (UB): это не определено.

Ваш код:

char str[5];
strcpy(str,"Hello12345678");

Записи 14 байтов/символы к str который может только содержать 5 байтов/символы. Это вызывает UB.

1
ответ дан 8 December 2019 в 02:08
поделиться

Q: Итак, почему это не отказывает для "Hello1234567" и только отказывает для "Hello12345678" т.е. строки с длиной 13 или больше, чем 13.

  • Поскольку поведение не определено. Используйте strncpy. Посмотрите эту страницу http://en.wikipedia.org/wiki/Strcpy для получения дополнительной информации.
0
ответ дан 8 December 2019 в 02:08
поделиться

Поскольку поведение не определено. Используйте strncpy. Посмотрите эту страницу http://en.wikipedia.org/wiki/Strcpy для получения дополнительной информации.

strncpy небезопасен, так как он не добавляет ПУСТОЕ завершение, если исходная строка имеет длину> = n, где n является размером целевой строки.

char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash

Мы всегда используем strlcpy для облегчения этого.

0
ответ дан 8 December 2019 в 02:08
поделиться