LINUX.ORG.RU

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

 


0

2

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



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

Это ты ещё не пытался гцц для МК использовать. Там самый ад и содомия. Поэтому некоторые куски быстрее переписать на асме, вместо траха с компилятором.

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

Нет, с -O0 работает без ошибок. Значит бага нет

Баг есть, просто при -О0 нет условий для его проявления. А при -О3 эти условия существуют.

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

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

Если в коде есть UB, то оптимизатор может сделать что угодно.

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

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

Вот я, наивная девушка, почитала man memset в своей версии линукса. И там нифига не написано, «слышь, подруга вообще то эта шняга на нашем gcc работает, но на самом деле нет».

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

тут просто разные взгляды на трактовку слова ИЗМЕНЕНИЯ. Текст выводится? Выводится, значит изменений нет (Вариант 1). Текст выводится? Выводится, но не так как прежде, а через другую функцию (Вариант 2). Кто прав?

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

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

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

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

То есть в теории, туда можно ничего не писать. А на практике – нельзя, так как это очистка sensitive data в данном случае.

В стандарте должны быть не костыли типа memset_s(), а способ явно указать, что «вот этот кусок кода – имеет другое определение наблюдаемого поведения» (в свете секьюрности, а не скорости). Но его нет.

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

В нашей freebsd есть:

Like explicit_bzero(3), memset_s() is not removed through Dead Store Elimination (DSE), making it useful for clearing sensitive data.  In contrast memset() function may be optimized away if the object modified by the function is not accessed again.  To clear memory that will not subsequently be accessed it is advised to use memset_s() instead of memset().
xtouqh
()
Последнее исправление: xtouqh (всего исправлений: 1)
Ответ на: комментарий от Zpp

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

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

Понимаешь, так можно очень далеко зайти.

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

Это нужно, чтобы не было возможно провести атаку по времени.

Так вот. Имея доступ к коду функции и коду, который эту функцию вызывает, компилятор, теоретически, достаточно умён, чтобы понять смысл кода и заменить его на обычный strcmp(). Если текущий не достаточно умён, то будет достаточно умён через 10 лет.

Потому что «наблюдаемое поведение» не изменилось.

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

А в реальном коде оптимизация попроизводительности может нарушать фактическое (а не теоретическое) наблюдаемое поведение за здорово живёшь. Потому что программы исполняются в железе, а не в математических моделях.

Целый ворох аппаратных уязвимостей в CPU, дающих возможность анализа системы через паразитные каналы данных – тому пример.

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

Сначала они требуют программы которые будут писать программы, а потом на них жалуются… Ууууу кожаные мешки!

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

Да в общем, что требовали, то и получили.

Комп –железка тупая, он делает ровно то, что задано. Надо быть осторожнее со своими желаниями: они сбываются так, как заданы, а не так, как хотелось в мечтах.

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

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

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

Ладно. Вот тривиальный примерчик

if (p+len < p || p+len > MAX) return 1;

Какую ошибку на это раз внесет компилятор?

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

Если я правильно понимаю, компилятор выкинет любую функцию, которая не имеет побочных эффектов

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

Поэтому только отладочка и имеет какой-то вес в мире сишки, все остальные критерии правды в виде стандартов и прочей чепухи - вторичны.

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

Значит бага нет

Бага может и нет, а UB есть. Просто по-разному проявляется при оптимизациях и без.

no-such-file ★★★★★
()
Ответ на: комментарий от Zpp

Какую ошибку на это раз внесет компилятор?

Если p это signed тип, а len положительный, а MAX это максимальное значение типа, то верно:

if (p+len < p || p+len > MAX) return 1;

=>

if (0+len < 0 || false) return 1;

=>

if (len < 0) return 1;

=>

if (false) return 1;

=>

<удалили всё>

потому что переполнение signed типа это UB. а в программе не может быть UB.

Человек бы оптимизировал точно также…

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

они есть либо так

#pragma GCC push_options #pragma GCC optimize («O0»)

your code

#pragma GCC pop_options

либо через

attribute((optimize(«O0»)))

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

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

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

Я предположил, что p – это указатель, а len – unsigned. А MAX, возможно, граница доступной прикладному коду памяти.

Переполнение указателя – UB или нет? Не помню… Надо конвертнуть в uintptr_t и в нём считать.

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

Плохо, когда уже не триггерится, печально.

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

p+len < p

=>

0+len < 0

Это как выводится? Можно показать пункты стандарта (аксиомы теории, допустим что стандарт - это непротиворечивая теория)

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

Вот пример косяка компилятора:
https://lwn.net/Articles/342330/

But Herbert’s patch added a line which dereferences the pointer prior to the check. That, of course, is a bug.

Речь про баг программиста, а не компилятора, если что. От компилятора максимум что можно требовать – ворнинг на дереференс перед чеком.

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

MAX это максимальное значение типа

Неудачно обозванная граница массива, видимо.

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

Переполнение int — UB. С добрым утром.

Проверяй значения без переполнений.

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

p+len < p - это проверка на переполнение. Складывает два положительных числа и получаем отрицательное.

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

а в программе не может быть UB.

Тут тоже нужна ссылка на стандарт.

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

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

Складывает два положительных числа и получаем отрицательное.

Это на одной архитектуре так, а на другой может быть совершенно другой смысл.

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

чтобы уж с++ совсем стал языком магии, отличные идеи )

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

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

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

Это на одной архитектуре так, а на другой может быть совершенно другой смысл.

Архитектуры без 2-compliment целочисленных чисел не нужны. Уже сложились единые стандарты и нет причины их не соблюдать: байт - 8 бит, little endian, 2-compliment, IEEE-754.

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

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

anonymous
()
Ответ на: комментарий от Zpp
  if (((a > 0) && (b > (INT_MAX - a))) ||
      ((a < 0) && (b < (INT_MIN - a)))) {
    /* Handle error */}
Morin ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Плохо, что в си нет возможности конкретизироваться под платформу.

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

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

Любое значение не равно конкретному значению - 0 (ноль).

Тут он применил «очевидное» свойство равенст-неравенств - сложение-вычитание одного и того же числа с обеих сторон равенства a + p < p => a + p - p < p - p => a < 0

Но сперва на доказать и показать что такое свойство есть в стандарте (теории)

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

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

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