LINUX.ORG.RU
ФорумTalks

curl уязвим, но я вам не скажу, какие версии

 


0

4

Разраб (?) curl оповестил о том, что в curl найдена серьёзная уязвимость, жутчайшая за много лет. Мейнтейнеры дистрибутивов оповещены, детали 11 октября.

https://github.com/curl/curl/discussions/12026

I cannot disclose any information about which version range that is affected, as that would help identify the problem (area) with a very high accuracy so I cannot do that ahead of time. The "last several years" of versions is as specific as I can get.

We have notified the distros mailing list allowing the member distributions to prepare patches. (No one else gets details about these problems before October 11 without a support contract and a good reason.)

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

★★★★★

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

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

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

Нормальная кооперативная многозадачность это как минимум event loop с легковесными обработчиками, который из коробки имелся в том же Turbo Vision (ну, надо учитывать что вся библиотека ориентирована исключительно на пользовательский интерфейс).

Вот это async/await во многих современных ЯП. Правда, в современных ЯП «легковесные обработчики» сами генерируются по месту вызова await.

Erlang не видел. То как в Go принято - это ужас.

В чем ужас? В том, что надежно работает из коробки? Согласен, безобразие.

То, что ты записал в минусы Си - показывает исключительно твоё непонимание зачем он нужен. Это не язык манипулирования данными, это язык выдачи инструкций процессору, и он даёт программисту полную возможность это делать.

Конструкции исходного Си опять-таки где-то к концу 80-х/середине 90-х перестали быть близкими к процессору. Для этого достаточно скомпилировать программу, например, с -Og, и посмотреть на ее производительность. В том числе благодаря некорректному превращению указателей в ссылку оптимизатор получает возможность делать оптимизации, которые без этого были бы невозможны — но это уже получается другая программа, отличная от оригинальной.

А учитывая развитие SIMD — ты можешь раздавать процессору инструкции «вычислять по чайной ложечке», но беспардонная автовекторизация обгонит такой алгоритм в разы. Если ты применяешь всякие там strlen/strcat, то можешь с удивлением обнаружить, что аналогичная программа на Go значительно обгоняет твою на Си, поскольку родные строки Go хранят свою длину. Я это пишу, потому что я с этим сталкивался. Некоторые так же думают, что если напишут функцию на ассемблере, то она будет работать быстрее аналогичной сишной функции.

Я про то что «Джаву сделали из Паскаля» - в то время как центральной её идеи в Паскале не было, а вот в Турбо-Паскале - была.

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

И где же в джаве перегрузка операторов? Где шаблоны до джавы5 в 2004 году? По-моему на С++ ООП ни разу не похоже. А вот на Паскалевское - вполне.

В C++ шаблоны появились не сразу. В вики ты можешь почитать, что изначально Oak задумывался как переработка C++, но по итогу решили задействовать новую модель исполнения Pascal/Mesa.

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

async/await приводят к тому, что невозможно нормально управлять порядком сохранения состояния, то есть сохраняется тупо весь контекст функции и программист даже не думает о том, что можно всё это сделать оптимальнее - не перестановкой местами разных кусков кода между синтаксическими единицами (это может и оптимизатор сделать), а усовершенствованием алгоритма, чтобы снизить его требования. Это полностью приемлемо для скриптовых языков, вся суть которых - дать программисту возможность думать только о потоках данных и не грузить его подробностями реализации, но совершенно неприемлемо в Си, где реализацию придумывает как раз программист.

В чем ужас? В том, что надежно работает из коробки? Согласен, безобразие.

В том что это всё та же скриптота (=пхп/питон/жс), только теперь претендующая на системный язык.

Конструкции исходного Си опять-таки где-то к концу 80-х/середине 90-х перестали быть близкими к процессору

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

посмотреть на ее производительность

У меня сложилось впечатление, что реально полезные оптимизации лежат между -O0 и -O1, а всё, что дальше - корректировка дефектов работы программиста. По крайней мере в большинстве случаев.

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

Насколько я знаю, это регулируется -fstrict-aliasing и кодовым словом restrict в списке аргументов функции. Если первое отключить (т.е. не использовать -O2 либо использовать, но не забывать дополнять его -fno-strict-aliasing), и не писать где не нужно restrict - то компилятор таким не будет заниматься. Ну а там где нужно - указывай restrict чтоб разрешить компилятору делать эти оптимизации. Возможно, есть другие факторы, но я ни разу не сталкивался.

Если ты применяешь всякие там strlen/strcat, то можешь с удивлением обнаружить, что аналогичная программа на Go значительно обгоняет твою на Си, поскольку родные строки Go хранят свою длину

Ну вот и разница между нормальным языком программирования и Go. В Си ты тоже можешь хранить длину рядом, если это нужно. А можешь не рядом, а вообще в другом месте, но тоже хранить. И во всех случаях это можно сделать красиво и понятно. А в скриптах побайтовое представление данных - только по канонам, всё остальное сразу костыли. strcat я не помню когда пользовался, это функция из 70-80-х, провоцирующая баги.

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

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

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

Зачем в последний то? Новую уязвимость дали время искать.

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

Главное что стандарт телнета не на 100% совместим с запросами, т.к. октет 0xFF телнетом и в протоколах таких как TCP, POP3 и т.д., где его иногда используют для отладки воспринимается по-разному.

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

Переполнение буфера в куче. Классика жанра.

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

Скучно.

Походу восстание пылесосов отменяется.

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

async/await приводят к тому, что невозможно нормально управлять порядком сохранения состояния, то есть сохраняется тупо весь контекст функции и программист даже не думает о том, что можно всё это сделать оптимальнее

Я полностью согласен с тем, что тупой async/await очень убог, но то, что часто лепят на коленке при помощи Си, получается еще хуже.

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

Если у тебя цикл и компилятор его векторизовал, то получается мягко говоря не понятный и не закономерный асм. Да что там векторизация — ты пробовал обычные циклы почитать? Оно весь поток управления перетряхивает уже на -O1.

У меня сложилось впечатление, что реально полезные оптимизации лежат между -O0 и -O1, а всё, что дальше - корректировка дефектов работы программиста. По крайней мере в большинстве случаев.

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

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

Насколько я знаю, это регулируется -fstrict-aliasing и кодовым словом restrict в списке аргументов функции

Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile. -fstrict-aliasing — это просто терминальная стадия рака, которая делает невозможным выявление бага в отладочной сборке.

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

s1 = s + "asd" можно сделать в Си? Отличные истории.

strcat я не помню когда пользовался, это функция из 70-80-х, провоцирующая баги.

Да, функция говно, но задача остается та же — конкатенирование нуль-терминированных строк без лежащей рядом длины не масштабируется.

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

Если у тебя цикл и компилятор его векторизовал, то получается мягко говоря не понятный и не закономерный асм. Да что там векторизация — ты пробовал обычные циклы почитать? Оно весь поток управления перетряхивает уже на -O1.

Векторизованные вроде не видел (наверно это связано с тем что на всех десктопах у меня 32 бита), а насчёт потока управления - не уверен что понял о чём речь. Разворачивание циклов, индивидуальные реализации первой/последней итерации, оптимизация переменной цикла? Если ты напишешь что-то типа

for(i=0; i<j; i++) {
  x = 0;
  f(a[k-i], b[m-i]);
}

то оно может превратиться в что-то типа

x = 0;
for(i=a+k; i>a+k-j; i--) {
  f(*i,*(b+m-k+(i-a)));
}

ну норм, читабельности это не вредит. К тому же я часто сам второй вариант сразу и пишу. Или ты про другое?

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

Значимость функций это понятие относительное. Ну да, не везде надо оптимизировать. Где не надо - там незачем и что-то кроме -O0 использовать, и можно писать на пхп вообще. Ну и в целом если тебя интересует только логика, то наверно Си не для тебя, да, есть другие языки где постарались уберечь программиста от низкоуровневых артефактов.

Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile.

Есть конкретные примеры? Я кажется понял о чём речь, но вроде такого не должно происходить без указанных мной опций. strict-aliasing, повторюсь, дефолт для -O2 поэтому велик шанс что оно было включено там где ты с этим столкнулся.

s1 = s + «asd» можно сделать в Си? Отличные истории.

Да, можно, если s - число, а s1 - указатель. А если серьёзно, то я уже писал - сишная поддержка строк состоит из синтаксиса строковых литералов, и только из него. Конкатенация строк реализуется уже кодом. Перегрузить оператор плюс в Си нельзя, автоматически следить за тем, когда надо освободить память, занятую аллоцированными строками - тоже. Поэтому конкатенация в любом случае будет, во-первых, функцией, а во-вторых, скорее всего, придётся вручную расставлять подсказки для освобождения памяти, занятой аргументами. Если это всё - проблема, то есть С++, где есть и перегрузка плюса и автоматическое освобождение памяти когда надо, но это уже другая тема. А Си рассчитан именно на максимально явное задание алгоритма во всех аспектах, и в некоторых случаях это приводит к очень громоздкому коду, да.

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

Ну да, но Си тут почти ни при чём. Повторюсь, можно писать на Си и использовать строки с хранимой длиной. И даже более-менее спрятать детали реализации за функции и макросы, но да, на С++ всё равно формулы подобных операций будут красивее.

Что касается твоего прошлого заявления что вот в языках, где строки нативно и безальтернативно хранятся как { size_t length; char data[]; }, конкатенация быстрее - это не совсем так. Да, в некоторых случаях это будет оптимально, может быть даже в многих из возникающих на практике, но не забывай, что как минимум один из аргументов (а скорее всего - оба, если исходные значения надо сохранить, либо если предыдущему коду не повезло угадать с выделением памяти про запас) придётся копировать в другое место. Это быстрее чем вычисление длины, но все же сравнимо по времени (представь строки по гигабайту каждая). А вот в Си (или С++) ты можешь написать например такую реализацию строк, которая копировать ничего не будет (а просто запомнить, что первая половина новой строки лежит там-то, а вторая - там-то, разумеется с подсчётом рефов и cow). Либо не первая-вторая в например сотня сегментов, каждый из которых можно cow по отдельности. Либо ещё что-нить. В этом и преимущество Си - ты можешь низкоуровневый алгоритм тонко настроить под нужды одного конкретного места в коде, где ты им пользуешься. Более того, ты можешь не просто тюнить этот алгоритм как отдельную сущность, а заинлайнить его в вызывающий код и оптимизировать дальше уже то, что получилось. В языках, прячущих низкий уровень от программиста, всё это получится только костылями, а большинство просто не станут париться и задействуют неэффективную штатную реализацию. Но да, в среднестатистическом применении у них бывают удобные наборы готовых абстракций.

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

Векторизованные вроде не видел (наверно это связано с тем что на всех десктопах у меня 32 бита), а насчёт потока управления - не уверен что понял о чём речь. Разворачивание циклов, индивидуальные реализации первой/последней итерации, оптимизация переменной цикла?

Да, поток управления (который имеет свое конкретное представление у компилятора) — это всё перечисленное, кроме «оптимизация переменной цикла». Компилятор может настолько перемешать все инструкции, что ты с первого взгляда свой цикл не узнаешь. А еще может изменить счетчик, трактовав его как common expression.

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

Я как бы согласен, но с другой стороны немношк получается, что Си никому не нужен. Потому что в 2023 мало кому и очень редко нужно давать команды процессору. В частности, это не нужно эмбеду в 90% их кода, поскольку их код — прикладная логика. Это даже не нужно операционной системе где-то в 50-70% выполняемого на конкретной системе кода, поскольку драйвера на одном устройстве выполняются не так много, больше половины работает код координации задач и промежуточных механизмов перед самим драйвером (но разных систем много, потому по числу строк в репозитории драйвера занимают больше половины).

Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile.

Есть конкретные примеры? Я кажется понял о чём речь, но вроде такого не должно происходить без указанных мной опций. strict-aliasing, повторюсь, дефолт для -O2 поэтому велик шанс что оно было включено там где ты с этим столкнулся.

Есть конкретные примеры сломанных в 80-х годах операционных системах и приложениях, которые работали с указателями, а потом внезапно компилятор доступ к указателю заоптимизировал. «Слава богу», что часто компилятор не может заоптимизировать доступ по указателю, потому проблем получается меньше, чем можно было бы создать.

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

«Максимально явное задание алгоритма» не противоречит созданию возможности статического анализа ее корректности, вроде предупреждений «вы не высвободили строку по окончанию одной из веток». Вроде тут уже писали, я просто повторю: никто не заставлял Си быть настолько убогим и увешанным UB, это никак не связано с близостью к железу, это сугубо отсутствие архитектуры языка, которая, будучи помноженной еще и на близость к железу, дает восхитительно непредсказуемые результаты.

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

Это же можно сделать в JS — другое дело, что это будет многословно и ненадежно. Как и в Си.

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

кроме «оптимизация переменной цикла»

А еще может изменить счетчик, трактовав его как common expression.

Это не про одно и то же?

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

Если они были алгоритмически не связаны друг с другом и это очевидно компилятору - то может. Но по-моему такие циклы редкость и обычно в глобальном плане последовательность инструкций либо важна, либо они такие развесистые и раскиданные по разным *.o / *.so что компилятор залезть внутрь не может и оставляет всё как есть. А если там реально пачка действий которые гарантированно всё равно в каком порядке выполнять - пусть переставляет, опять же не думаю что это повредит.

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

Слишком категоричное утверждение про «никому». Может, мало (да, большинство пишут на пхп-питонах-жс), ну и что? Лично мне вот нужен например, я привык именно давать инструкции процессору, а не абстрактные алгоритмы составлять. Исследований количества таких же людей я не проводил, но думаю всё-таки их заметное количество.

доступ к указателю заоптимизировал

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

Наверно я не понял о чём речь. Можно таки пример и в чём разница?

вы не высвободили строку по окончанию одной из веток

Всё дело в том, что аллокатор - тоже не часть языка. Это просто функция библиотеки, которая в общем случае неизвестно что делает внутри. Можно делать статические анализаторы, которые делают предположение о предсказуемости её поведения и дальше из этого что-то выводят, но, строго говоря, это уже не чистый Си, а конкретная платформа. В С++ ещё есть new/delete которые именно операторы языка, и хотя они тоже реализованы библиотекой, но к ним уже есть семантические требования со стороны языка.

увешанным UB

Увешан UB стандарт, а не реальные реализации. Реальные UB компилятора настраиваются его опциями и используемой платформой.

Это же можно сделать в JS — другое дело, что это будет многословно и ненадежно. Как и в Си.

Я ж написал - получится везде, но в чрезмерно высокоуровневых только костылями. Да, многословно как и в Си, но в Си всё-таки красивее и быстрее, и никакой «ненадёжности» (это будет его нативная и привычная многословность, а не костыли с оверхедом). А в С++ можно будет убрать многословность, оставить сишную скорость работы, пожертвовав прозрачностью трансляции в асм.

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

Но по-моему такие циклы редкость и обычно в глобальном плане последовательность инструкций либо важна, либо они такие развесистые и раскиданные по разным *.o / *.so что компилятор залезть внутрь не может и оставляет всё как есть.

Абще-т инлайн является одним из вариантов оптимизаций, в том числе она возможна между объектниками при включенной LTO.

Может, мало (да, большинство пишут на пхп-питонах-жс), ну и что? Лично мне вот нужен например, я привык именно давать инструкции процессору, а не абстрактные алгоритмы составлять.

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

Наверно я не понял о чём речь. Можно таки пример и в чём разница?

Для оптимизации работы с указателями компилятор заменяет чтение-запись по разыменованному указателю доступом к некому скрытому локальному значению, вплоть до вычисления его как константого выражения. Например, функция void transform_int(int *arg), которая в промежуточном представлении начинает выглядеть как int transform_int(int in_arg). Это то, что можно назвать «доступ по ссылке», то есть, передается именно значение в обе стороны, а не указатель на память. И в том числе при инлайне вся передача аргумента оптимизируется в ноль.

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

Всё дело в том, что аллокатор - тоже не часть языка.
В С++ ещё есть new/delete которые именно операторы языка, и хотя они тоже реализованы библиотекой, но к ним уже есть семантические требования со стороны языка.

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

Увешан UB стандарт, а не реальные реализации. Реальные UB компилятора настраиваются его опциями и используемой платформой.

Серьезно, ты хочешь мне рассказать про какой-то компилятор, в котором почти нет стандартного сишного UB? Насколько лично мне известно, отключать возможно совсем уж трешевый треш, вроде того же strict-aliasing, а вот что-то промежуточное включается уже безусловно.

Да, многословно как и в Си, но в Си всё-таки красивее и быстрее, и никакой «ненадёжности» (это будет его нативная и привычная многословность, а не костыли с оверхедом)

На фоне довольно развитой JIT-оптимизации в JS — спорное заявление с твоей стороны.

А в С++ можно будет убрать многословность, оставить сишную скорость работы, пожертвовав прозрачностью трансляции в асм

Прозрачность трансляции в асм опять-таки почти никому не нужна, намного более важным фактором является скорость разработки, в том числе скорость компиляции, с которыми у C++ есть оч большие проблемы.

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

Для оптимизации работы с указателями компилятор заменяет чтение-запись по разыменованному указателю доступом к некому скрытому локальному значению

Ты выше писал про «к указателю» и «по указателю», заявив что второе компилятор не оптимизирует а первое - оптимизирует и портит этим что-то. По-моему, то что ты сейчас описываешь - это как раз «по указателю». Всё ещё непонятно чем отличаются эти два случая.

Что касается самой описанной оптимизации, то она все-таки регулируется словом restrict и, в некоторых частных случаях (когда компилятор видит весь код до конца) strict-aliasing. Если где-то делается без него - то это наверно баг в компиляторе - несоответствие официально заявленному поведению. Или речь не про gcc была?

Есть конкретные примеры сломанных в 80-х годах

Да, видимо всё-таки не gcc. Ну, был плохой компилятор значит, хотя я удивлён что в 80-х такие оптимизации уже были где-то.

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

Серьезно, ты хочешь мне рассказать про какой-то компилятор, в котором почти нет стандартного сишного UB? Насколько лично мне известно, отключать возможно совсем уж трешевый треш, вроде того же strict-aliasing, а вот что-то промежуточное включается уже безусловно.

В -O0 безусловно ничего не включается. Да и в -O1 практически всё, что может навредить, отключено.

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

Ты выше писал про «к указателю» и «по указателю», заявив что второе компилятор не оптимизирует а первое - оптимизирует и портит этим что-то. По-моему, то что ты сейчас описываешь - это как раз «по указателю». Всё ещё непонятно чем отличаются эти два случая.

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

Что касается самой описанной оптимизации, то она все-таки регулируется словом restrict и, в некоторых частных случаях (когда компилятор видит весь код до конца) strict-aliasing. Если где-то делается без него - то это наверно баг в компиляторе - несоответствие официально заявленному поведению. Или речь не про gcc была?

Это похоже на restrict или на strict-aliasing?
https://godbolt.org/z/Kn7G5GEsn

int func(int *a)
{
    return *a * *a;
}

int main(int argc, char **argv)
{
    return func(&argc);
}
Странно слышать, что человек, для которого ассемблерный выхлоп компилятора «понятен и закономерен», не в курсе умолчательной оптимизации указателей во всех компиляторах Си даже на очень низких уровнях оптимизации.

Есть конкретные примеры сломанных в 80-х годах

Да, видимо всё-таки не gcc. Ну, был плохой компилятор значит, хотя я удивлён что в 80-х такие оптимизации уже были где-то.

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

Серьезно, ты хочешь мне рассказать про какой-то компилятор, в котором почти нет стандартного сишного UB? Насколько лично мне известно, отключать возможно совсем уж трешевый треш, вроде того же strict-aliasing, а вот что-то промежуточное включается уже безусловно.

В -O0 безусловно ничего не включается. Да и в -O1 практически всё, что может навредить, отключено.

Ознакамливайся:
https://gist.github.com/Earnestly/7c903f481ff9d29a3dd1
Да, половина здесь относится к самому компилятору-препроцессору, но опции компилятора относятся лишь к 5-10% из всех UB.

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

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

А, это я в одном месте невнимательно прочёл. Сейчас прочёл ещё раз и всё понятно стало.

int func(int *a)
{
    return *a * *a;
}

А тебе обязательно нужно чтобы компилятор race condition аккуратно сделал? Не вижу проблемы, тем более с умножением у которого даже порядок чтения операндов не специфицирован. Если б там было не умножение а && то порядок уже задан, но всё равно это чтение одного и того же адреса два раза подряд в соседних инструкциях, без побочных эффектов (если они есть надо указать volatile - и будут два чтения) - вполне норм читать только один раз.

Сделай вот так: *a && read(0,0,0) && *a и он будет читать два раза т.к. между чтениями стоит read() который мог что-то изменить в памяти. А вот если int * __restrict a то даже read() в середине не помешает ему выкинуть второе чтение.

Ознакамливайся: https://gist.github.com/Earnestly/7c903f481ff9d29a3dd1

Очень длинная простыня, и сложно найти в ней что-то релевантное среди мусора либо про синтаксис, либо про подсовывание битых данных стандартной библиотеке, либо ещё чего-то подобного.

----------------

Кстати, вспомнил у себя обратный случай

assert(!obj->status2 || obj->status2==4 && obj->data);
(ассерт был под локом) компилятор коварно не стал кешировать status2 (по крайней мере в -O0) и из-за этого падал на этом ассерте. Пришлось вручную ему подсказывать
assert(!(tmp=obj->status2) || tmp==4 && obj->data);
Там суть была в том, что переход откуда-то в status2==4 делался только вместе с заполнением data под локом, а вот переход status2==4 -> status2==0 делался без лока т.к. 0 менее ограничивающий чем 4 и пофиг если его кто-то не сразу заметит.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)