LINUX.ORG.RU

Пример выпиливания кода или необычного поведения программы из-за undefined behaviour

 ,


4

5

Привет. Я читал несколько статей о том, что undefined behaviour это страшно, что его нельзя игнорировать, потому что оптимизации компиляторов используют undefined behaviour и могут из-за него удалить часть кода, потому что она типа unreachable или что-то типа того. Короче я это в теории знаю, но не знаю сильно подробностей - хотелось бы примеры, как именно undefined behaviour приводит к тому, что оптимизации компилятора делают так, что код делает не то, что днище-программисту кажется, что он должен делать. Буду эти примеры в интернет пояснениях всяких undefined behaviour ситуаций показывать.

Еще мне интересно, считают ли компиляторы signed integer overflow за undefined behaviour и оптимизируют ли программу, учитывая тот факт, что это запрещено? Мне кажется, было бы наиболее разумно им делать вид, что это поведение вполне себе определено, и не выпиливать из-за этого код, потому что по-моему 90% C++ программистов, да и C программистов тоже не знают о том, что это UB.

Ответ на: комментарий от monk

проблемы переносимости решаются исключительно пачками ifdef'ов

А что, есть другой вариант?

И при переходе на другой компилятор

ССЗБ.

или ОС

ССЗБ.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

с ним ситуация аналогична. а за счёт многословности по сравнению с крестами легче написать такое, в чём потом никакой ричи не разберётся.

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

Только из-за «железячных» особенностей.

«Только»? Атомики в 11 стандарте появились. Еще лет 20 и мы дождемся векторных расширений в стандарте, а пока интринсики, ассемблер, ифдефы.

Они уже давным-давно на С переписаны!

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

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

Они уже давным-давно на С переписаны!

осталось ещё их авторам об этом узнать

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

И до сих пор никто не придумал язык лучше С.

В языке с нормальным стандартом невозможна ситуация как здесь: http://kqueue.org/blog/2012/06/25/more-randomness-or-less/

В 1997 в FreeBSD начали использовать UB: для увеличения случайности использовать неинициализированную переменную. А современные компиляторы начали правомерно выкидывать этот код.

В нормальном языке неверный код должен всегда приводить к ошибке или должны быть явно перечислены возможные варианты поведения (как в стандарте на Common Lisp, например).

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

Атомики в 11 стандарте появились

А ты уверен, что ядро их поддерживает? =D

Если бы фортран умер, никто бы не стал оптимизирующие компиляторы пилить.

Ты давно видел используемый рабочий софт на фортране? Я — лет 10 назад...

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от monk

В языке с нормальным стандартом невозможна ситуация как здесь

Чем тебе не нравится использовать неинициализированную область памяти для задания srandom? Вполне нормальное поведение.

А современные компиляторы начали правомерно выкидывать этот код

Правомерно? Мде...

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

А ты уверен, что ядро их поддерживает? =D

все знают что ты дурак... но не настолько же

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

внимательно читали стандарты при создании компилятора под свой шлак?

Тогда это уже не язык Си.

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

А ты уверен, что ядро их поддерживает? =D

ISA их поддерживает. Найдешь современный процессор без атомиков?

Ты давно видел используемый рабочий софт на фортране? Я — лет 10 назад...

Я много чего не видел, но оно объективно существует.

AptGet ★★★
()

потому что по-моему 90% C++ программистов, да и C программистов тоже не знают о том, что это UB.

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

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

Это где ж ты обитаешь, что у вас программисты не знают о UB

Программисты FreeBSD использовали UB (чтение неинициализированной переменной) для sranddev(). Программисты OpenSSL до сих пор его (UB) используют.

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

Правомерно? Мде...

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

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

Чем тебе не нравится использовать неинициализированную область памяти для задания srandom? Вполне нормальное поведение.

Я согласен, но разработчики языка Си так не считают и указали, что это UB.

monk ★★★★★
()

Часто забывают про порядок вычислений, например i++ = i++.

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

Вот, берите пример: истинный фанат языка C. Язык C за много лет так и не выучил.

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

К стати, никаких кулстори гугл по этому запросу не находит.

RiseOfDeath ★★★★
()

считают ли компиляторы signed integer overflow за undefined behaviour

На месте создателей стандарта я бы для подобных случаев ввёл понятие «undefined value», чтобы ограничить возможный вред.

Можно уточнить, что на каком-то железе такая программа может совсем не работать (как при делении на 0), но не может совершать левые действия типа форматирования диска.

anonymous
()

удалить часть кода, потому что она типа unreachable или что-то типа того

Забыли volatile?

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

Тут хватит volatile указателя на то, что собственно и нужно обнулить.

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

В языке с нормальным стандартом невозможна ситуация как здесь: http://kqueue.org/blog/2012/06/25/more-randomness-or-less/

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

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

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

Там было getpid() ^ tv.tv_sec ^ tv.tv_usec ^ junk

Если junk = 0, то остаётся getpid() ^ tv.tv_sec ^ tv.tv_usec и всё хорошо. Но так как это UB, то компилятор решил, что пусть junk == ~(getpid() ^ tv.tv_sec ^ tv.tv_usec) и занулил результат.

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

На месте создателей стандарта я бы для подобных случаев ввёл понятие «undefined value»

Так в нормальных стандартах так и есть. «Функция xxx возвращает implementation-dependent значение», «порядок вычисления может быть произвольным», «если функция не поддерживается реализацией, то возвращается -1» и т.д.

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

Если компилятор делает такое даже без оптимизаций, то это просто свинство. А если авторы подобного софта меньше "-O3" или "-Os" не используют, то это уже неправильный выбор инструмента.

anonymous
()

Тред на пальцах о том, откуда берутся вскукареки что O3 ништабильно.

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

Если компилятор делает такое даже без оптимизаций, то это просто свинство

Следовать стандарту - не свинство. Свинство - это создавать стандарт языка, который не осиливает 95% пишущих на этом языке.

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

Свинство - это создавать стандарт языка, который не осиливает 95% пишущих на этом языке.

Не осиливают — фигня. Полную спецификацию postscript тоже мало кто осиливает, но язык неплохой.

А вот то, что стандарт явно указывает, что отдельные конструкции языка делают что угодно — это свинство.

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

Атомики в 11 стандарте появились

А ты уверен, что ядро их поддерживает? =D

Atomic facepalm.

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

А вот кому в голову пришла мысль в стандарт вводить понятие UB как разрешения делать ЧТО УГОДНО — это вопрос.

Твои предложения? Вот есть конкретный signed integer overflow.

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

UB на любых уровнях оптимизации проявляется (пример slovozap - даже на O1), на O3 только шансов на это больше.

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

Вот есть конкретный signed integer overflow.

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

На крайний случай можно писать «При переполнении целого со знаком, может быть возвращено любое число в допустимом диапазоне. Реализация должна гарантировать, что для любых a, b, c, если a == b, то c+a-b == c, в том числе если результат c+a сохраняется в промежуточную переменную».

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

Насколько я помню, всего существует 3 варианта представления отрицательных чисел.

Ну не надо упрощать и сводить все железо вы мире к этим трем вариантам. Но даже если - положим есть железо, которое при переполнении выкидывает trap (уверен, что есть). И есть ось, которая его не передает приложению, а тупо завершает прогу. Дальше что? Да, пример далеко не самый красивый, но иллюстрирует общую суть вопроса.

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

Ога. И теперь простое _быстрое_ сложение на указанной мной платформе превратилось в медленнющую предварительную проверку, «а не выпадет ли trap». Пойми, сделать _эффективное_ вычисление на зоопарке платформ без UB в особых случаях нельзя. Просто нельзя. Все платфомы разные и их нельзя эффективно подогнать под одно правило. Даже широкое. Live with that. А для неэффективного подгона под правило - добро пожаловать в любые другие языки. С/С++ без производительности нафиг не нужен - это их единственное преимущество.

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

Ну не надо упрощать и сводить все железо вы мире к этим трем вариантам.

C99 6.2.6.2:2:

If the sign bit is one, the value shall be modified in one of the following ways:

— the corresponding value with sign bit 0 is negated (sign and magnitude);

— the sign bit has the value −(2N ) (two’s complement);

— the sign bit has the value −(2N − 1) (ones’ complement).

положим есть железо, которое при переполнении выкидывает trap (уверен, что есть). И есть ось, которая его не передает приложению, а тупо завершает прогу.

В возможные варианты действий при переполнении можно добавить завершение программы.

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

Для unsigned указали, что переполнение обязано циклиться по модулю. На твоей гипотетической платформе с трапом по переполнению его тоже нельзя сделать эффективно.

Более того, в стандарте есть отдельно понятия «implementation defined behavior» и «unspecified behavior». Например, записать в union одну переменную и прочитать другую. Или вычислить "-1 >> 1". Обязано работать, но на разных компиляторах может по-разному.

К слову, "-1 << 1" уже UB. И отсутствие переноса строки в конце исходника — тоже UB. Хотя зачем?

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

В возможные варианты действий при переполнении можно добавить завершение программы.

И выйдет UB. Потому что полагаться на «любое значение, или terminate» нельзя, так же как и на UB.

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

Было что-то про мелкомягких дебилов и еще каких-то быдлокодеров, у которых «серьезный» софт грохнулся по причине невнимательного чтения манов.

Ты что, пропустил эпичнейший срач? :)

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

И выйдет UB. Потому что полагаться на «любое значение, или terminate» нельзя

Выйдет документированное поведение. То есть функция

bool check_overflow(int x, int y)
{
    return x + y > x;
}

при переполнении упадёт или вернёт ложь. И её можно например использовать как assert(check_overflow(x, y)). А при существующем UB компилятор оптимизирует check_overflow в return true.

Вообще, как писать арифметику на Си, если знаковое переполнение невозможно отследить? Писать вместо int x «struct { uint base, bool sign}» и реализовать арифметику вручную?

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

Выйдет документированное поведение.

Которое ни в чем не отличается от UB, т.к. его всё так же нельзя применить. За исключением твоего assert, от которого в продакшене ни холодно ни жарко.

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

Которое ни в чем не отличается от UB, т.к. его всё так же нельзя применить.

Можно. Программа будет либо правильно работать, либо падать. А сейчас она при UB просто работает неправильно. И обнаружить это очень сложно.

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

Для вычислительных задач этого достаточно. Для остальных — достаточно, если за программой следит watchdog.

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

Для остальных — достаточно, если за программой следит watchdog.

Здесь вам не эрланг!

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