LINUX.ORG.RU

Хрупкие программы

 


0

2

Почему так бывает, что при -O0 программа работает нормально, а например при -O3 начинает падать, но не всегда. Чей тут косяк, программиста или gcc? Может ли использование static_cast и запрет неявных преобразований типов избежать такого поведения?



Последнее исправление: Zpp (всего исправлений: 1)

Почему так бывает, что при -O0 программа работает нормально, а например при -O6 начинает падать, но не всегда. Чей тут косяк, программиста или gcc?

Программиста. Или gcc. Или и программиста, и gcc.

Может ли использование static_cast и запрет неявных преобразований типов избежать такого поведения?

Может. А может, и не может.

Какие вопросы – такие и ответы.

Siborgium ★★★★★
()

У gcc косяки, если и есть, то попасть на них плохо программисту практически нереально. «-O6» - читайте документацию. По поводу креша - valgrind и санитайзеры в помощь.

anonymous
()

-O6

ЯЗАБАН

anonymous
()

С вероятностью 99% с копейками по моей субъективной оценке это косяк программиста.

ЗЫ

gcc это не только компилятор для C и крестов, так что со статик кастом вообще некорректный вопрос.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)

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

blexey ★★★★★
()
Ответ на: комментарий от peregrine

А вот не фига, если семантика программы не изменилась, то косяк gcc однозначно. Он же генерит глючный код.

Zpp
() автор топика
Ответ на: комментарий от xtouqh

Вот пример косяка компилятора:

https://lwn.net/Articles/342330/

«The linux kernel had a bug where a potentially NULL pointer was being dereferenced before a test for that pointer being null. However, in some cases it was possible to map memory to address zero, thus allowing the dereferencing to succeed. The compiler, upon noticing that the pointer was dereferenced, assumed that it couldn’t be NULL, then removed the NULL test later and all the code in that branch. This introduced a security vulnerability into the code, as the function would proceed to use an invalid pointer containing attacker-supplied data.»

Взял и выкинул целую ветку кода. Нормально так.

Zpp
() автор топика
Ответ на: комментарий от Zpp

Мне интересен пример именно «кривого кода», который работает при -O0, и не работает при -O2, а не баг в компиляторе.

xtouqh
()

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

Поэтому тонны софта резко перестают работать или может проседать стабильность. Настала эра сжв и технофашизма, народ экстримит по полной. Поэтому чтобы не стало плохо - старый код компилируем компилятором, под который он писался. Под новый код тщательно читаем стандарт и те места, где компилятору явно не сказано что делать (UB) обходим стороной, потому что сейчас считается компилятор в этом случае имеет право генерировать неработающий код. Тогда все будет хорошо и со старым кодом и с новым. Даже код под старые стандарты (i.e C89) был переосмыслен, и это уже не спасает.

slapin ★★★★★
()

Чей тут косяк, программиста или gcc?

Программиста.

fsb4000 ★★★★★
()
Ответ на: комментарий от Zpp

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

luke ★★★★★
()
Ответ на: комментарий от xtouqh

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

Zpp
() автор топика
Ответ на: комментарий от Zpp

А вот не фига, если семантика программы не изменилась, то косяк gcc однозначно

Либо у тебя в программе UB, тогда косяк программиста.

theNamelessOne ★★★★★
()
Ответ на: комментарий от Zpp

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

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

theNamelessOne ★★★★★
()
Ответ на: комментарий от slapin

старый код компилируем компилятором, под который он писался

И разве код скопилированный gcc 2* будет работать в современном линуксе с ядром 5.8?

Zpp
() автор топика
Ответ на: комментарий от Zpp

Если там нет ничего специфичного, то будет.

anonymous
()
Ответ на: комментарий от Zpp

Ну и в каком месте тут UB?

В том, что в языке Си нулевой адрес имеет особую семантику.

wandrien ★★
()

Потому что undefined behavior. Код написанный не в соответствии со специализацией может работать не как ожидалось. C/C++ так устроены. Пример. Если не хочется всего этого, надо использовать Pascal, Oberon, Go, Rust.

X512 ★★★★★
()
Ответ на: комментарий от X512

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

UB UB-ом, но такие кейсы надо либо компилировать в какой-то осмысленный код, либо вообще отказываться компилировать и заканчивать работу с осмысленным сообщением об ошибке.

wandrien ★★
()
Ответ на: комментарий от wandrien

Где в стандарте написано, что область памяти не может начинаться с адреса 0. В какой-нибудь другой архитектуре например?

Zpp
() автор топика
Ответ на: комментарий от wandrien

Там по ссылке вообще мрак.

Это C/C++ каким мы его знаем. Если всего этого не хочется, надо либо забить на комитет и писать другую спецификацию (что например делает Microsoft), либо использовать другие языки.

X512 ★★★★★
()
Ответ на: комментарий от Zpp

А вот еще вопрос.

Такую конструкцию может выкинуть из кода memset(password_buffer, 0, sizeof(password_buffer)); если password_buffer далее не используется?

Zpp
() автор топика
Ответ на: комментарий от X512

Это недочёт в стандарте, а не в компиляторе.

Стандарт не требует, соответственно компиляторы не реализуют.

Писать свой стандарт можно, но можно и без стандарта падать с -Werror по дефолту на таких кейсах. Так как поведение – UB, то такой вариант ничего не нарушает.

wandrien ★★
()
Ответ на: комментарий от Zpp

Да, поэтому есть memset_s() в c11, и куча хаков на разных платформах типа explicit_bzero().

xtouqh
()
Ответ на: комментарий от Zpp

Где в стандарте написано, что область памяти не может начинаться с адреса 0. В какой-нибудь другой архитектуре например?

В Си константа 0, преобразованная в адрес, имеет особую семантику «никуда не указывающего» указателя. Это написано в стандарте, конкретную страницу искать лень.

При этом в железе этот 0 не обязан быть значением «все биты нулевые», а зависит от реализации. Но архитектуры, где бы это пригодилось, мне не известны.

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

Если подумать, под ядро должно быть сделана особая оговорка, и возможно, в режиме freestanding семантика должна отличаться (или должен быть введён отдельный режим), но так как такого не сделано, то формально компилятор прав. Он же не знает, что это ядро.

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от Zpp

Ну и в каком месте тут UB?

Где-то в твоём коде, скорее всего.

theNamelessOne ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Там вроде не от оптимизации же зависит? Я просто хотел пример именно кривого кода, ломающегося от оптимизаций, а не от глупых компиляторов/стандартов, но, похоже, это одно и то же.

xtouqh
()
Ответ на: комментарий от xtouqh
void delay_ms() 
{
int count = 0;
for(int I=0; I < delay; I++) 
{
    count++;
}
}

Если не пометить count как volatile, при любой включенной оптимизации компилятор выкинет этот кусок кода.

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от slapin

Не соглашусь.

Проблема скорее в том, что в руки инженеров попал «мирный атом», но пользоваться им в мирных целях еще не научились.

Читая «дракона», я частенько думал, как и многие наверное, вот будь у меня в руках крутая математика для графов в виде отлаженной библиотеки и достаточно вычислительных ресурсов, я бы сделал крутейший компилятор. Сейчас математику сделали, и ресурсов тоже хватает. Поэтому компилятор теперь знает о коде программы больше, чем сам программист.

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

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

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)

Потому что космические лучи портят программы.

Надо покупать серверные процессоры и память с поддержкой ECC, и использовать ZFS c raidz2.

anonymous
()
Ответ на: комментарий от wandrien

А почему нельзя ввести дополнительные прагмы. Скажем указать, вот этот кусок кода выкидывать нельзя?

Zpp
() автор топика
Ответ на: комментарий от wandrien

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

Zpp
() автор топика
Ответ на: комментарий от Zpp

если password_buffer далее не используется?

Конечно может. Ты не понимаешь как работает С++.

В стандарте написано:

  1. Есть программа

  2. Она что-то делает

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

https://i.imgur.com/THt3PHX.png

вот можешь послушать этого дядьку: https://youtu.be/ZAji7PkXaKY

как пример: https://gcc.godbolt.org/z/ozeEeb

внезапно, нет вызова printf, а откуда-то взялся вызов puts

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Только в тех рамках, когда в коде нет UB. Если в коде UB, то программа после преобразований делает «что угодно».

wandrien ★★
()
Ответ на: комментарий от fsb4000

внезапно, нет вызова printf, а откуда-то взялся вызов puts

Это же бред.

Наблюдаемое поведение изменилось. До оптимизации программа вызывала printf, а после – puts.

Весь вопрос в трактовке «наблюдаемого поведения». Компиляторы заходят слишком далеко.

wandrien ★★
()
Ответ на: комментарий от fsb4000

По крайней мере, в режиме freestanding такой оптимизации быть не должно. Лень проверять.

Но в hosted это тоже бред.

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 2)
Ответ на: комментарий от fsb4000

Ты не понимаешь как работает С++

Понимаю. Компилятор тайком меняет семантику моего кода. За такие подлянки разве не стоит сжигать его разработчиков печах?

Zpp
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.