LINUX.ORG.RU

Если вам не хватало UB в C, то вам принесли ещё

 ,


1

3

Привет, мои дорогие любители сишки!

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

— zero-sized reallocations with realloc are undefined behavior;

То есть вот это валидный код:

void *ptr = malloc(0);
free(ptr);

А вот это – UB:

void *ptr = malloc(4096);
ptr = realloc(ptr, 0); <-- хаха UB

И это несмотря на то, что в манах уже давно написано следующее:

If size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr)

Изменение вносится задним числом, наделяя кучу корректного (согласно документации glibc) кода способностью полностью изменить логику работы программы. Ведь это то, чего нам так не хватало!

В тред призываются известные эксперты по C: @Stanson и @alex1101, возможно они смогут нам объяснить, зачем разработчики стандарта C постоянно пытаются отстрелить себе обе ноги самыми нелепыми способами.



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

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

UB при разработке компилятора можно воспринимать как ID.

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

Ей богу, я офигиваю с сишников. С таким подходом, реально не удивительно, что CVE считается хит-парадом сишных багов.

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

То есть в случае с ID код делает ровно то что написано без отсебятины и магии.

Нет. ID это когда может быть одно, а может другое, и это зависит от реализации.

Implementation-Defined Behavior, пример (C11):

Propagation of the high-order bit when a signed integer is shifted right.

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

Аналоги?

Аналоги сишного мирка? Вьетнам. В целом довольно похоже:

  • Хм, запишу-ка я строку в масси… О ГОСПОДИ ДЖОННИ ОНИ В СТЕКЕ ЧЕРТОВЫ ГУКИ УЖЕ ПРЯМО В НАШЕМ СТЕКЕ
cumvillain
() автор топика
Ответ на: комментарий от firkax

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

Ну расскажи мне, где именно в доках к clang описано почему именно код здесь вызовет rm -rf:

#include <cstdlib>
typedef int (*Function)();
static Function Do;
static int EraseAll() {
 return system(“rm -rf /”);
}
void NeverCalled() {
 Do = EraseAll; 
}
int main() {
 return Do();
}
hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от anonymous

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

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

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

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

UB при разработке компилятора можно воспринимать как ID.

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

Где логика?

Если UB стандарта в документации компилятора прописана, то это уже ID для компилятора.

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

Если UB стандарта в документации компилятора прописана, то это уже ID для компилятора.

ну вот пример выше уже привели: gcc валится в сегфолт, а clang уничтожает твой /.

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

Алгоритм оптимизации досконально не документирован

Это проблема, да. Его наверное и не имеет смысла документировать, т.к. на практике ты это применить не сможешь (нужно будет буквально компилять «на бумаге» чтобы получить контекст в котором применяются правила). На месте разработчиков компилятора я бы подумал о том, что с этим можно сделать. Например, что первое приходит на ум, кидать предупреждение всегда когда меняется структура кода. Выбросить RET из функции это как бы не мышь чихнула, такое должно явно сообщаться.

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

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

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

cumvillain
() автор топика

ЛОР, С тред.

Прошло уже несколько страниц а и никто так и не обозвал никого анскильной лалкой.

Внесли какие-то изменения в стандарт С тредов на ЛОРе или что?

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

А потом твой код портируется на freebsd и ты огребаешь

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

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

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

Заборол потому что он работает только на конкретной версии компилятора? И эти люди втирают нам про «переносимый ассемблер»?

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

Это проблема, да.

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

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от no-such-file

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

Паскаль в своё время был переносимее любых Си. Для бутстрапа можно было использовать платформо-незавимымый P-code и элементарную виртуальную машину.

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

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

А если это библиотека и в документации к функции написано, что скажем вызов с n=0 - это UB, так что никогда не вызывайте функцию с n=0? Всё равно кидать предупреждение?

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

Каждый раз при использовании библиотеки выполнять анализ потоков исполнения, и выдавать предупреждение только если возможен вызов с n=0? Сильно замедлит компиляцию, да и ложные срабатывания будут, которые тоже захочется подавить и см. выше.

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

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

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

Зато как бы это дисциплинировало программиста! Сишники бы точно оценили, если учесть, как сильно они любят BDSM.

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

Он тоже не представляет

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

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

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

Он совершенное не представляет что ты хочешь чтобы он делал.

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

Это ошибка в коде компилятора, к текущей теме это отношения не имеет

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

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

Мне и не надо. Достаточно чтобы он сообщил, что конкретно он делает.

Включая реордеринг, выкидывание функций, мерж функций, оптимизацию циклов и прочее безумие? Удачи, чо.

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

Я не читал доки к шлангу. Но тут разыменование NULL.

Ну и? А почему rm -rf / вызывается-то? Мне тут говорили, что если разыменовывать NULL, будет тупо segfault а не rm -rf /. Сишники врут, выходит?

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

Паскаль в своё время был переносимее любых Си

И как на Паскале определить указатель на конкретный адрес? Т.е. это возможно конечно, но в разных диалектах Паскаля по-разному. Вот в этом и проблема. Я уж не говорю о прибитости гвоздями к рантайму, и модули, которые ни с чем не совместимы, что делает Паскаль закрытой системой.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от red75prim

просматривать километровую портянку

А ты пишешь сразу 1000kLOC и компиляешь всё разом? Ну и как-бы всё не надо, только если какой-то код выкинули, или поменяли местами.

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

Ну расскажи мне, где именно в доках к clang описано почему именно код здесь вызовет rm -rf:

А точно вызовет? Проверил свежим clang - у меня только segfault. На какой версии вызывало rm -rf?

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

Ну и как-бы всё не надо, только если какой-то код выкинули, или поменяли местами.

Я выше уже писал. Нужно какое-то средство, чтобы помечать некоторые оптимизации как корректные. Если эти пометки будут слетать с функции и всех вызывающих её функций после каждого редактирования и их придётся заново проставлять, то 99% программистов пошлют такую систему туда-же куда послали checked exceptions.

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

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

У Си тоже прибитость к рантайму (libc) и модулям (A-Out, COFF, ELF) и на ранних этапах он был ни с чем не совместимой закрытой системой. Это уже потом в виду помулярности Си, рантайм и модули изначально разработанные специально для Си начали использоваться в других языках. Печальное наследие этого наблюдается и чейчас в убогом и крайне хрупком механизме связывания символов ELF. В DLL (NE, LE, PE) это сделано намного лучше потому что формат ориентировался на Паскаль у которого есть явные модули с отдельными областями видимости.

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

А точно вызовет? Проверил свежим clang - у меня только segfault. На какой версии вызывало rm -rf?

А ты собери с -O2 :D

$ clang -O0 -Wall -Wextra -o test test.cc && ./test
zsh: segmentation fault (core dumped)  ./test
$ clang -O2 -Wall -Wextra -o test test.cc && ./test 
хаха UB
cumvillain
() автор топика
Ответ на: комментарий от X512

на ранних этапах он был ни с чем не совместимой закрытой системой

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

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

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

Лолшто? ELF придумали специально под эти ваши юниксы:

First published in the specification for the application binary interface (ABI) of the Unix operating system version named System V Release 4 (SVR4)

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

Какой ещё ELF в 70-м году? Прими таблетки.

Откуда ты вытащил 70-й год?

У Си тоже прибитость к рантайму (libc) и модулям (A-Out, COFF, ELF)

Внезапно, но объектные файлы можно было получить из ассемблера

Объектные файлы можно было, а вот стандартизированного ABI не было.

cumvillain
() автор топика