Дэниэл Бернштейн (Daniel J. Bernstein (http://cr.yp.to/djb.html)), известный эксперт в области криптографии и создания защищённого ПО, разработавший такие проекты, как qmail, djbdns, Ed25519, Curve25519 и ChaCha20-Poly1305, предложил (https://groups.google.com/forum/#!msg/boring-crypto/48qa1kWi... создать новый компилятор для языка Си, рассчитанный на сборку защищённого ПО. По мнению Бернштейна сообществу не хватает компилятора, который мог бы обеспечить предсказуемое поведение и предоставить чётко определённую семантику для всех ситуаций, отмеченных в стандарте Си как неопределённые (undefined (https://ru.wikipedia.org/wiki/%D0%9D%D0%... неспецифицированные (unspecified) или зависящие от конкретных реализаций.
Большинство приложений используют подобные возможности, но существующие компиляторы могут трактовать их по разному, что может привести (https://www.opennet.me/opennews/art.shtml?num=40281) к разным результатам при сборке в разных компиляторах, уязвимостям (https://www.opennet.me/opennews/art.shtml?num=38293) и непредвиденным проблемам (https://www.opennet.me/opennews/art.shtml?num=39992). Новый компилятор мог бы стать эталоном для разработчиков на языке Си и принести большую пользу тем, кто занимается аудитом и верификацией Си-кода.
URL: https://news.ycombinator.com/item?id=10772841
Новость: http://www.opennet.me/opennews/art.shtml?num=43571
OpenNews: В DNS-сервере BIND устранен серьёзный сбой, возникший из-за изменений в оптимизаторе GCC
> Ты ведёшь речь про багу, которые были, есть и будут есть.А бага в чём? в том, что хочет переписать ДЖБ, именно компилятор, а не стандарт языка С.
пс: а перед тем как переписывать стандарт С, нужно сначало переписать стандартную библиотеку С.
> А ты бы этого не хотел?Курс долллара взлетел - хотел бы (ирония)
пс: ДЖБ один из тех кто себя зарекомендовал, и слышать от него такую инициативу лично для меня вызывает чувства восхищения, нашёлся тот кто реально видит к чему всё катится.
Почему только или?
Потому что логическое или для истинности всего выражения не требует истинности только одного из подвыражений
Завидовать нехорошо, тем более адепту микрософт, которое гребет бабло с "патентов".
дополню выдержкой из той новости про бинд***
В частности, начиная с GCC 4.9 по умолчанию включается режим удаления лишних операций сравнения с указателями NULL, при использовании которого из-за удаления из кода важных для работы проверок в BIND начинают проявляться непредсказуемые проблемы в работе.
***ну и собственно эта строчка - "начинают проявляться непредсказуемые проблемы в работе"
Переписыванием гцц он в первую очередь хочет избавится от непредсказуемости компилятора, а потом уже можно говорить о непредсказуемости стандарта.
Все, кому это надо, решают проблему прибитой гвоздями версией компилятора и ключами компиляции. В общем-то, у него и получится ещё одна такая версия - и не более.
> прибитой гвоздями версией компилятораэм, давно пакет делали? deb+rpm под современные версии дистрибутивов (два centos, два debian, убунты от 12.04 до 14.04 хотя бы)?
нет, можно в .spec записать выкачивание нужной версии компилятора, сборки его и сборки им. но такой путь считается не тру. я без ёрничания.
Т.е. он декларирует что его копилятор не будет развиваться (вдруг в С17 напишут не так как у него), каждый баг будет фичей (чтобы не ломать совместимость), оптимизатор будет отключен (а то мало ли что).Простая заморозка версии компилятора в песочнице дает то же самое.
> Т.е. он декларирует что его копилятор не будет развиватьсяКстати, да. Уже сколько лет никакого движения ни в djbdns, ни в qmail…
qmail никакого движения не нужно, как ни странно это звучит. Всё что нужно можно прикрутить самостоятельно, т. к. в qmail широко применяются принципы unix - "одна программа - одно действие", конвейерная обработка и модульность. В итоге на сервере есть всё что нужно: dkim, антиспам и т. д. Поэтому изучи сначала матчасть дружище, прежде чем писать сюда о движении в qmail.
без хэшей, подписанных коммитов и контрольных сумм ...
Правильно говорит. Для проектов уровня ядра Linux, Android, Firefox и Chrome каждый новый релиз GCC - головная боль и предвкушение, что опять что-нибудь при новом уровне оптимизации сломается.
> Правильно говорит. Для проектов уровня ядра Linux, Android, Firefox и Chrome каждый
> новый релиз GCC - головная боль и предвкушение, что опять что-нибудь
> при новом уровне оптимизации сломается.Android и Chromium гуглом собираются clang'ом, что как бы намекает.
У clang ситуация ровно такая же. Если древний код с UB то каждая новая версия - русская рулетка. И с любым компилятором так же будет.
Действительно, надо прекратить выпускать новые релизы GCC.
А каждый новый релиз "защищённого" компилятора, значит, всегда будет праздником?
> А каждый новый релиз "защищённого" компилятора, значит, всегда будет праздником?Именно! Проекты "уровня ядра Linux, Android, Firefox и Chrome"тм не будут собираться этим компилятором чуть более, чем полностью. Поэтому каждый оного компилятора релиз будет приносить этим проектам (и совместно анонимным аналитикам) праздник, укатайку и еду.
Rust, D, или что-то такое — в любом случае если C четко определить семантику кругом, то это уже не будет C.
Отличная идея! Давайте назовём его Java. Хотя, постойте…
Джава это такой электровеник со встроенным веником GC, который постоянно тормозит, жрёт ресурсы и падает, огороженный буллшитом со всех сторон. Не нужно совать некомпетентное мнение туда, где вы не компетентны
> Джава это такой электровеник со встроенным веником GC, который постоянно тормозит, жрёт
> ресурсы и падает, огороженный буллшитом со всех сторон. Не нужно совать
> некомпетентное мнение туда, где вы не компетентныВы определитесь — вам производительность или {undefined, unspecified} behavior, а потом уже раздавайте советы о том, что кому куда совать.
А как предсказуемое поведение повлияет на производительность?Вот это вот:
int i = 5;
i = ++i + ++i;Медленнее работать будет? Велико упущение. Нормальный программист не пишет код, результат которого не может сам смоделировать в голове. Если есть сомнения в приоритетах операций - использует скобки или последовательное выполнение. Или даже ставит скобки просто для ясности, чтобы у других не было сомнений в том, как работает код.
Проверка на выход за пределы массива? Принудительная инициализация переменных нулями? Но ведь можно делать это по-умному - инициализировать только если где-то происходит использование значения до инициализации или размер массива меняется, а конечный индекс массива заранее неизвестен.
Например, в этом случае достаточно сделать одну проверку:
for(i = 0; i < n; i++)
m[i]++;Проверка должна быть одна, до выполнения цикла, что n * sizeof(m[0]) меньше полного размера m. Оптимизация не противоречит безопасности. Компиляторы фортрана раньше умели на этапе компиляции сумму арифметической прогрессии, посчитанную в цикле, оптимизировать до известной формулы. А если там только константы фигурируют, то и вообще - на этапе компиляции результат посчитать. Но можно, конечно, по-тупому делать, каждое значение i проверять. В яве, кстати, не так делается?
> Проверка должна быть одна, до выполнения цикла, что n * sizeof(m[0]) меньше полного размера m.А кто гарантирует, что ( n * sizeof(m[0]) < TYPE_MAX) ?
Да, можно сделать доп. проверку:
if (TYPE_MAX/sizeof(m[0]) < n && (n * sizeof(m[0] < m))
Можно заоптимизить и превратить в битовые маски, про которые
через неделю сам забудешь, что они значат.---
Хотите быстро и безопасно - пишите на Ada
> А как предсказуемое поведение повлияет на производительность?
> Вот это вот:
> int i = 5;
> i = ++i + ++i;
> Медленнее работать будет? Велико упущение. Нормальный программист не пишет код, результат
> которого не может сам смоделировать в голове. Если есть сомнения в
> приоритетах операций - использует скобки или последовательное выполнение. Или даже ставит
> скобки просто для ясности, чтобы у других не было сомнений в
> том, как работает код.Медленнее будет работать проверка указателя перед его первым использованием.
Медленнее будет работать проверка на знаковое целочисленное переполнение.
Медленнее будет работать проверка того, не превышает ли величина сдвига ширину типа данных, которые сдвигают.
Медленнее будет работать проверка того, не выполняет ли программист целочисленного деления на ноль (а деление на ноль с плавающей запятой — не всегда некорректная операция, и потребует ещё одной дополнительной проверки).
Медленнее будет работать проверка того, не выполняет ли программист целочисленного деления INT_MIN на -1.
Медленнее будет работать проверка размера variable length array при каждом обращении.
Медленнее будет работать проверка того, выравнен ли указатель, перед каждым его разыменованием.А ещё в C есть случаи неопределённого поведения, природа которых лежит в особенности реализации аппаратуры. Предлагаете ввести надстандарт над всеми ISA?
А ещё есть C++ со своей кучей интересных случаев.
> Проверка на выход за пределы массива? Принудительная инициализация переменных нулями?
> Но ведь можно делать это по-умному - инициализировать только если где-то
> происходит использование значения до инициализации или размер массива меняется, а конечный
> индекс массива заранее неизвестен.И на времени компиляции такой анализ совсем не отразится?
> Например, в этом случае достаточно сделать одну проверку:
> for(i = 0; i < n; i++)
> m[i]++;
> Проверка должна быть одна, до выполнения цикла, что n * sizeof(m[0]) меньше
> полного размера m.Проверка должна быть одна, но быстрее не делать её вообще.
> Оптимизация не противоречит безопасности. Компиляторы фортрана раньше
> умели на этапе компиляции сумму арифметической прогрессии, посчитанную в цикле, оптимизировать
> до известной формулы. А если там только константы фигурируют, то и
> вообще - на этапе компиляции результат посчитать.Компиляторы C и сейчас это делают.
> Но можно, конечно, по-тупому
> делать, каждое значение i проверять. В яве, кстати, не так делается?
> В яве, кстати, не так делается?А что вы до Явы докопались, она в NASA, точнее уже на Марсе, на марсаходах работает.
откуда инфа?
Вот ты тёмный, братишка. Ещё скажи что не в курсе о том что питон на луне вместе с жабой побывал.
>питон на луне вместе с жабой побывал.Мерзкие, склизкие, земноводные ... гады!! #зависть
Google: nasa mars java
> Джава это такой электровеник со встроенным веником GC, который постоянно тормозит, жрёт
> ресурсы и падает, огороженный буллшитом со всех сторон.Написали helloworld на Java? А сопровождать сотни тысяч строк кода на C++ приходилось? Дружить десяток либов? Или что-то сложнее одной формочки на WinAPI?
Не осилили лабы в политехе, как и все Си-хейтеры? Ассемблер тоже завалили? Паскаль и джава - дружба на века?
Нормальные либы и нормальный код прекрасно сопровождаются и интегрируются в другие решения. Даже если написаны на так не любимом вами С/С++. Более того бОльшая половина софта только так и работает. Чего я не могу сказать о сопровождении кода написанного дивизией мартышек на любом ЯП с низким уровнем вхождения.
Шура, не трогайт белоснежный Си своими кривыми приплюснутыми лапами
"Более того бОльшая половина софта"БОльшая половина - это сильно. А бигдата - это меньшая половина? Она почему-то сплошняком на жабке писана. Видимо, тоже лабы на си не осилили. И банковское серверное ПО аналогично.
Бигдата? Мммм... Я забыл, каждый день он нужен тысячам мильёнов для ежедневных задач. Даже косынка без этих сферических предсказаний в вакууме на основании с потолка взятых данных не работает. Да. И всё равно там рулит не джава, а Python и R
Извращенские банки могут писать своё серверное ПО на любой грабле, на которую они готовы наступать ежедневно. Я даже видел их вакансии по 300$ за штуку. Мне этого даже на квартплату не хватит. Опять же им видимо пользуются сотни тысяч пользователей ежедневно дома и на работе, да? Из какого репозитория можно скачать? А на малинке или телевизоре пойдёт? А на топовом смартфоне? Мы конечно закроем глаза на то, что половина "джава приложений" банков кроме винды вообще нигде не работает, не говоря уже о удобстве и качестве. Многих реально работающих с этим УГ сказочно заололохали так же калечные ключи ЭЦП, которые конфликтуют с любым другим софтом, без которых банковский джавасофт и нафиг не нужен. Короче там всё разваливается постоянно, как карточный домик. Распил такой распил.
> А бигдата - это меньшая половина? Она почему-то сплошняком на жабке писана. Видимо, тоже лабы на си не осилили.Мне вот интересно, что нынче студентота подразумевает под бигдатой? SAP? Или уже и Oracle? А вы слышали, как эти компании имплементируют новые фичи? Да, сначала на жабе, а потом, если фичей начали пользоваться, переписывают на C в следующей версии.
> Джава это такой электровеник со встроенным веником GC, который постоянно тормозит, жрёт
> ресурсы и падаетУ меня первый телефон, 15 лет назад, был с игрушками и приложениями на джаве. С апп. конфой в духе 2МБ ОЗУ. Не очень-то значит и жрёт.
И где ж в это время пропадал великолепный си?! :(
> У меня первый телефон, 15 лет назад, был с игрушками и приложениями
> на джаве. С апп. конфой в духе 2МБ ОЗУ. Не очень-то
> значит и жрёт.Да, такие маленькие убогие кастратики, типа Siemens M55 и Sony X100. Помню носили их тогда вёдрами чинить. На любой чих и багу в приложении система сыпалась и делала морду кирпичём. Причём прилаги хоть и были написаны на Java, но jar от одной модели телефона легко не работал на другой модели телефона в рамках даже одного производителя. Лучшее их применение было - звонилка с функцией подключения в качестве GPRS-модема через датакабель. От Java там одна насмешка.
К счастью весь этот ужас канул в историю.> И где ж в это время пропадал великолепный си?! :(
На топовых смартфонах на тот момент вполне лидировавшей Нокии крутился Qt уже тогда.
На тех же блекберрях хотя и есть возможность накидать на Java, но вполне себе есть SDK с C/C++ для тех кто разрабатывает не веб-мордочки.
Перечитай новость еще раз. Речь идет о поведении компилятора, а не о изменениях в стандарте языка. Более того, если глянуть оригинал, то окажется, что там даже о сохранении ABI говорится.
Он эе по факту пытается стандартизировать undefined, unppecified и impleentation defined behaviour - естественно, это покушение на стандарты. Не факт, что это плохо (C вообще по нынешним временам позволяет слишком уж много неопределённости), но решать это надо не новой реализацией, а именно новым стандартом. Там для большей части implementation defined, например, есть всего два-три варианта, возможных в реальной жизни - можно их формализовать и в хидерах отдавать какой именно используется.
> но решать это надо не новой реализацией, а именно новым стандартом.Во-первых, это нельзя решать ни новой реализацией (очевидно), ни новым стандартом — во втором случае старый код, собранный новым компилятором, может внезапно изменить свою семантику. Во-вторых, стандарты на предмет устранения неопределённого поведения должны быть пересмотрены одновременно для C и C++: пересматривать их только для одного языка из двух просто опасно по той же причине, упомянутой выше.
Ерунда.1) стандарт врубается по соответствующему ключику. Если ты не знаешь, что за код ты суёшь компилятору и с какими ключами - это профнепригодность.
2) Если ты суёшь плюсовому компилятору сишный код - и так будь готов к сюрпризам, совместимость давно не полная. Если нет - то какие поблемы? Надо просто раз в жизни запомнить, что C и C++ - это два разных языка, у них свои компиляторы, у каждому - свой набор ключей компиляции и все совпадения в общем случае случайны и не гарантированы, а не писать как девочка-рекрутер "C/C++".
Да, он покушается именно, что на стандарт: его действительно надо доопределить.Однако новый компилятор вообще говоря идея не плохая. Всё дело в том, что для доопределения стандарта, нужно знать, как именно доопределить его. Стало быть, вопрос встанет в выборе между несколькими вариантами доопределения. И выбор одного или другого может повлечь некоторое количество подводных камней, сразу не очевидных. Так что лучший вариант -- это действительно написать компилятор, и расбираться с этими неочевиными проблемами по мере их обнаружения.
Об этом, кстати, писал Реймонд в "Соборе и базаре": когда ты начинаешь писать нечто, ты на 100% заранее не знаешь, что должно получиться в результате твоей работы. Поэтому, чтобы понять, как именно надо доопределить стандарт, надо создавать тестовый образец компилятора: это приблизит к пониманию того, что надо сделать.
> Более того, если глянуть оригинал, то окажется,
> что там даже о сохранении ABI говорится.Некоторые любят решать уже решённые проблемы.
http://ispras.linuxbase.org/index.php/ABI_compliance_checker
https://www.sourceware.org/libabigail/
Некоторые любят придумать чушь за автора и научить его, как эту чушь исправить, вместо того, чтобы все-таки узнать, что на самом деле говорил автор. Ведь для этого придется напрячь мозг и прочитать много(аж целая страничка) букв.
> Некоторые любят придумать чушь за автора и научить его, как эту чушь
> исправить, вместо того, чтобы все-таки узнать, что на самом деле говорил
> автор. Ведь для этого придется напрячь мозг и прочитать много(аж целая
> страничка) букв.У языка как такового нет ABI. ABI есть у программ и библиотек, диктуемое отчасти спецификацией на программно-аппаратную платформу, отчасти — исходным кодом этих программ и библиотек. Говорить об ABI в контексте языка не имеет смысла, так что высказываемые автором опасения ничем не отличаются от рядовой ситуации обеспечения совместимости ABI между программами, собранными двумя произвольным компиляторами (Microsoft C++ Compiler и clang на Windows), или при обновлении исходного кода произвольной библиотеки с void foo(const char* x) на void foo(char* x).
Раз в полгода в списке рассылки gcc или clang появляется какой-нибудь чудак с подобной идеей. Думаю, ясно, что ему отвечают. Странно, что теперь и Бернштейн докатился до такого.Напомню, ранее он выступал с идеей потребовать от разработчиков процессоров постоянного времени выполнения команд сравнения, не зависящего от операндов.
> Раз в полгода в списке рассылки gcc или clang появляется какой-нибудь чудак с подобной идеей.Вы сравниваете теплое с фиолетовым – т.е. выкладки и "инновационные" предложения очередного диванного теоретика, с соображениями вполне известного и уже доказавшего, что он "небалабол" DJB?
> Напомню, ранее он выступал с идеей потребовать от разработчиков процессоров постоянного времени выполнения команд сравнения, не зависящего от операндов.
"Не Мерседес, а Запорожец, не в лотто а в карты ..." oO
cr.yp.to/antiforgery/cachetiming-20050414.pdf
> Advice for CPU designers...
> CPU designers should also consider adding AES support to their instruction
> sets. For example, a CPU could support fast constant-time instructions that
> compute T0[b], T1[b], T2[b], and T3[b] given b. The requisite circuit area is very
> small and easily justif i ed by the current benef i ts for AES users.И в чем тут "фейл"?
> Вы сравниваете теплое с фиолетовым – т.е. выкладки и "инновационные" предложения очередного диванного теоретика, с соображениями вполне известного и уже доказавшего, что он "небалабол" DJB?Это вещи одного порядка. И если уж вы так любите апеллировать к авторитетам, подумайте, почему в стандарте C вообще (и до сих пор) существуют undefined behavior и unspecified behavior.
> Не Мерседес, а Запорожец, не в лотто а в карты ..." oO
Полностью разделяю ваше восхищение (вашим умением передёргивать).
http://blog.cr.yp.to/20140517-insns.html
Фэйла здесь нет, я о нём и не говорил. Просто те, кому *действительно* нужна защита от timing attacks, находят пути её реализовать[1], для остальных это — пустая потеря производительности (и времени автономной работы от батарей).[1] https://git.kernel.org/cgit/linux/kernel/git/next/linux-next...
> Это вещи одного порядка.Угу, угу.
> Полностью разделяю ваше восхищение (вашим умением передёргивать).
Ну, не все умеют в телепатию.
> . Просто те, кому
> *действительно* нужна защита от timing attacks, находят пути её реализовать[1],GCC-онляи костыль, в виде повторяющегося OPTIMIZER_HIDE_VAR(neq), для – это да, оптимальнейшее решение!
>> Это вещи одного порядка.
> Угу, угу.Угу, угу.
>> Полностью разделяю ваше восхищение (вашим умением передёргивать).
> Ну, не все умеют в телепатию.А и не надо. Непонятно — переспросите. Только, если бы вы действительно иногда читали блог так рьяно защищаемого вами Бернштейна, вам было бы понятно.
>> . Просто те, кому
>> *действительно* нужна защита от timing attacks, находят пути её реализовать[1],
> GCC-онляи костыль, в виде повторяющегося OPTIMIZER_HIDE_VAR(neq), для – это да,
> оптимальнейшее решение!1. http://lxr.free-electrons.com/ident?i=OPTIMIZER_HIDE_VAR
Это к вопросу у gcc-only.2. http://lxr.free-electrons.com/source/include/linux/compiler-...
#define OPTIMIZER_HIDE_VAR(var) \
__asm__ ("" : "=r" (var) : "" (var))В отличие от вас, я умею в телепатию. Моя телепатия говорит мне, что вы не знаете, что такое суперскалярный процессор с внеочередным исполнением. Ещё моя телепатия говорит мне, что вы гуманитарий: саркастически называя код __asm__ ("" : "=r" (var) : "" (var)) оптимальнейшим решением, вы вступаете в противоречие с самим собой, так как ранее вставали на сторону Бернштейна в его требовании обеспечить постоянное время выполнения команд сравнения (что, как раз-таки, неоптимально). В отличие от этого требования Бернштейна, конструкция __asm__ ("" : "=r" (var) : "" (var)) действительно оптимальна в том смысле, что является лишь указанием компилятору, не внося дополнительных задержек времени исполнения в сгенерированный код, и весь негативный эффект от её применения — запрет компилятору переупорядочивать в сгенерированном коде команды, зависимость по данным между которыми компилятору неочевидна. Фактически, на этапе выборки команд процессор всё равно может переупорядочить их, хотя это переупорядочивание потенциально гораздо менее опасно, чем то, что мог бы выполнить компилятор.
> 1. http://lxr.free-electrons.com/ident?i=OPTIMIZER_HIDE_VAR
> Это к вопросу у gcc-only.О, кстати!
«Расширенный» синтаксис ассемблерных вставок gcc, помимо самого gcc, поддерживают, как минимум, clang, Sun Studio и Intel C Compiler[1].
И компилятор из состава Freescale CodeWarrior[2].
Это всё к вопросу о gcc-only.
[1] https://gcc.gnu.org/ml/gcc/2015-12/msg00124.html
[2] https://gcc.gnu.org/ml/gcc/2015-12/msg00123.html
Повторю слова автора по ссылке:
Ok, I admit it: I'm surprised.Не знал, что кроме шланга еще кто-то в это может.
> 1. http://lxr.free-electrons.com/ident?i=OPTIMIZER_HIDE_VAR
> Это к вопросу у gcc-only.И что, это уже не костыль?
> В отличие от вас, я умею в телепатию. Моя телепатия говорит мне,
> что вы не знаете, что такое суперскалярный процессор с внеочередным исполнением.Ну-ну. О великий просветитель, зачем вы вообще приплели сюда суперскалярность?
> Ещё моя телепатия говорит мне, что вы гуманитарий:
Хоспади, д'Артаньян, Вы?
> вступаете в противоречие с самим собой, так как ранее вставали на
> сторону Бернштейна в его требовании обеспечить постоянное время выполнения команд сравненияАноним не читатель?
> consider adding AES support to their instruction
> sets. For example, a CPU could support fast constant-timeдля анонимов: специальные комманды под это дело.
И ведь таки потом AES-NI запилили, не?> том смысле, что является лишь указанием компилятору,
Ну, если побочный эффект – указание, то да.
> запрет компилятору переупорядочивать в сгенерированном коде команды,
> зависимость по данным между которыми компилятору неочевидна.Н-да, а сколько пафоса то было.
В первую очередь, это запрет на оптимизацию.
Т.к. для конечный пользователь вызывает crypto_memneqstatic inline int crypto_memneq(const void *a, const void *b, size_t size)
{
return __crypto_memneq(a, b, size) != 0UL ? 1 : 0;
}
> return __crypto_memneq(a, b, size) != 0UL ? 1 : 0;т.е. получает 0 или 1.
Я не знаю, как конкретно называется используемая техника в гцц, но тa же классическая "abstract interpretation" при первом же проходе засветит только на раз, что:
( как только neq != 0 => нефиг далее маятся дурью, можно делать ret, т.к. ответ более "не меняется").
А так – там эдакая костыльная вставка на асме, означающая (на сей момент) для компилятора: "неведомая магия с neq в качестве in/out", оптимизировать низзя.Разъясняю на пальцах:
> __asm__ ("" : "=r" (var) : "" (var))
Темплейт-то пустой. Просто здесь объявляется, что "neq" идет в in/out, а сам темплейт гцц (пока) не парсится.
А теперь, вопрос на засыпку: что будет, когда гцц/шланг научатся парсить темплейт – и соответсвенно "узнают" о том, что neq там не применяется?
И чем это лучше UD?
>> 1. http://lxr.free-electrons.com/ident?i=OPTIMIZER_HIDE_VAR
>> Это к вопросу у gcc-only.
> И что, это уже не костыль?Нет.
>> В отличие от вас, я умею в телепатию. Моя телепатия говорит мне,
>> что вы не знаете, что такое суперскалярный процессор с внеочередным исполнением.
> Ну-ну. О великий просветитель, зачем вы вообще приплели сюда суперскалярность?Суперскалярный процессор с внеочередным исполнением волен исполнять в произвольном порядке команды, если между ними нет барьеров или зависимостей по данным. Компилятор для суперскалярного процессора с внеочередным исполнением использует это его свойство, самостоятельно, *уже на этапе компиляции* переупорядочивая некоторые команды там, где это может быть выгодно, что потенциально может спровоцировать процессор на ещё одно переупорядочивание во время выполнения (например, из-за различного расстояния до данных, адресуемых a или b, в иерархии кэшей).
Если бы тот код писался для тривиального конвеера, в функции __crypto_memneq_16() не было бы ни одной строки OPTIMIZER_HIDE_VAR(neq);. Задача разработчиков функции — постоянное время её исполнения вне зависимости от входных данных, и часть этой задачи они решают строгим упорядочиванием составляющих эту функцию команд.
>> Ещё моя телепатия говорит мне, что вы гуманитарий:
> Хоспади, д'Артаньян, Вы?Да.
>> вступаете в противоречие с самим собой, так как ранее вставали на
>> сторону Бернштейна в его требовании обеспечить постоянное время выполнения команд сравнения
> Аноним не читатель?
>> consider adding AES support to their instruction
>> sets. For example, a CPU could support fast constant-time
> для анонимов: специальные комманды под это дело.
> И ведь таки потом AES-NI запилили, не?Аноним не читатель. Я понятия не имею, зачем вы приплели AES-NI в свой второй пост и повторяете здесь. Речь шла о командах сравнения. AES-NI — не команда сравнения.
>> том смысле, что является лишь указанием компилятору,
> Ну, если побочный эффект – указание, то да.Указание — единственный эффект этой конструкции.
>> запрет компилятору переупорядочивать в сгенерированном коде команды,
>> зависимость по данным между которыми компилятору неочевидна.
> Н-да, а сколько пафоса то было.
> В первую очередь, это запрет на оптимизацию.Набор слов.
>[оверквотинг удален]
>> return __crypto_memneq(a, b, size) != 0UL ? 1 : 0;
> т.е. получает 0 или 1.
> Я не знаю, как конкретно называется используемая техника в гцц, но тa
> же классическая "abstract interpretation" при первом же проходе засветит только на
> раз, что:
> ( как только neq != 0 => нефиг далее маятся дурью,
> можно делать ret, т.к. ответ более "не меняется").
> А так – там эдакая костыльная вставка на асме, означающая (на
> сей момент) для компилятора: "неведомая магия с neq в качестве in/out",
> оптимизировать низзя.Эта вставка вводит фиктивную зависимость по данным между обращениями к neq. Вы правильно поняли часть её назначения: гарантировать, что сгенерированный компилятором код будет в точности, без изъятий соответствовать коду, написанному на C. Это вторая часть решения задачи обеспечения постоянного времени выполнения функции.
Однако ваше опасение справедливо лишь для случаев, когда значения аргументов a и b, а также константные смещения относительно них известны компилятору во время компиляции. На практике, в ядре таких случаев нет. Вы правильно поняли назначение конструкции, но исходите из ошибочной посылки.
> Разъясняю на пальцах:
>> __asm__ ("" : "=r" (var) : "" (var))
> Темплейт-то пустой. Просто здесь объявляется, что "neq" идет в in/out, а сам
> темплейт гцц (пока) не парсится.Совершенно верно, это фиктивная зависимость по данным. Но предотвращает она не dead code elimination (тогда здесь было бы достаточно спецификатора volatile при объявлении neq), а переупорядочивание выражений друг относительно друга.
> А теперь, вопрос на засыпку: что будет, когда гцц/шланг научатся парсить темплейт
> – и соответсвенно "узнают" о том, что neq там не
> применяется?
> И чем это лучше UD?gcc или clang никогда не научатся разбирать содержимое ассемблерных вставок by design. Это контракт. Вопрос не имеет смысла.
>> И что, это уже не костыль?
> Нет.Ну-ну. Особенно приведенный вами вами код.
> __asm__ ("" : "=r" (var) : "" (var))соберите с ним ядро и наслаждайтесь скоростью (намек: шланг хоть ругается на "invalid constraint", а гцц "кушает" и не обляпывается, генерируя
cmp rdx, 16
jne .L5
ret
Но ладно, не будем придираться. Просто "некостыльность" обычно несколько иначе выглядит.> строгим упорядочиванием составляющих эту функцию команд.
Гм, намекну:
Берем оригинальный код и делаем так:
unsigned long neq = 0;
unsigned long tmp1, tmp2;
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
if (sizeof(unsigned long) == 8) {
tmp1 = *(unsigned long *)(a) ^ *(unsigned long *)(b);
tmp2 = *(unsigned long *)(a + 8) ^ *(unsigned long *)(b + 8);
neq |= tmp1;
OPTIMIZER_HIDE_VAR(neq);
neq |= tmp2;
OPTIMIZER_HIDE_VAR(neq);
Для gcc49 -O2 -DCONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS на выходе у нас будет:
cmp rdx, 16
jne .L5
mov rdx, QWORD PTR [rdi+8]
xor rdx, QWORD PTR [rsi+8]
mov rax, QWORD PTR [rdi]
xor rax, QWORD PTR [rsi]
or rax, rdx
ret
Вот это вам выдаст шланг37 -O2# BB#1: # %sw.bb
mov rax, qword ptr [rsi]
mov rcx, qword ptr [rsi + 8]
xor rax, qword ptr [rdi]
xor rcx, qword ptr [rdi + 8]
#APP
#NO_APP
or rax, rcx
#APP
#NO_APP
pop rbp
ret
ВНЕЗАПНО – и там и там – совершенно неотличимо от оригинала!1
Делаем такой же фокус для ветки else:
movzx edx, BYTE PTR [rdi+1]
movzx eax, BYTE PTR [rdi]
xor dl, BYTE PTR [rsi+1]
xor al, BYTE PTR [rsi]
movzx edx, dl
movzx eax, al
or rax, rdx
Опять же, как и ожидалось, сгенерированный код одинаков в обоих случаях, но и не соответствует порядку "составляющих эту функцию команд".
Да и чего ему соответствовать, когда костыляние OPTIMIZER_HIDE_VAR(var) не дает убрать лишние OR (и заодно "закрепляет" их очередность, да, хотя от этого мало что меняется – вариации в тайминге будут полюбому наамного больше зависить от кэшмисов).
> Указание — единственный эффект этой конструкции.
> Эта вставка вводит фиктивную зависимость по данным между обращениями к neq.Нет.
neq = neq | expr = (a ^ b);
OPTIMIZER_HIDE_VAR(neq);
neq = neq | expr2 = (a2 ^ b2);
ничто не мешает компилятору вынести expr1,2 и т.д отдельно
Вот это вводит фиктивную зависимость по данным:tmp = a ^ b;
OPTIMIZER_HIDE_VAR(neq);
OPTIMIZER_HIDE_VAR(tmp);
neq |= tmp;
Только она там нужна, как рыбе зонтик, ибо еще раз повторюсь – разброс в тайминге из-за кэша будет куда выше, чем из-за очередности выполнения (X)OR r, mem .
А вот преждевременное "or foo,bar; jnz quit" после сравнения первого байта – будет, как бы, очень заметно.> правильно поняли часть её назначения: гарантировать, что сгенерированный компилятором
> код будет в точности, без изъятий соответствовать коду, написанному на C.Там, наверху, можно посмотреть на "соответствование".
> Это вторая часть решения задачи обеспечения постоянного времени выполнения функции.
На сферическо-вакуумных суперскалярах – вполне. А так, см. тайминги для xor/or r/m и "сколько стоит кэшмисс".
> Однако ваше опасение справедливо лишь для случаев, когда значения аргументов a и
> b, а также константные смещения относительно них известны компилятору во время
> компиляции.Компилятору достаточно того, что любое значение neq !=0 возвращает в итоге единицу.
Это, и зависимость neq от (a xor b) позволяет вообще выкинуть neq и сразу проверять результат XORа.> Совершенно верно, это фиктивная зависимость по данным. Но предотвращает она не dead
> code elimination (тогда здесь было бы достаточно спецификатора volatile при объявлении
> neq), а переупорядочивание выражений друг относительно друга.Хоть в ядро бы глянули, что там по этому поводу писали и чего опасались:
https://github.com/torvalds/linux/commit/6bf37e5aa90f18baf5a...
> crypto_memneq is declared noinline, placed in its own source file,
> and compiled with optimizations that might increase code size disabled
> ("Os") because a smart compiler (or LTO) might notice that the return
> value is always compared against zero/nonzero, and might then
> reintroduce the same early-return optimization that we are trying to
> avoid.https://github.com/torvalds/linux/commit/fe8c8a126806fea4465...
> Instead of disabling compiler optimizations, use a dummy inline assembly
> (based on RELOC_HIDE) to block the problematic kinds of optimization,
> The dummy inline assembly is added after every OR, and has the
> accumulator variable as its input and output. The compiler is forced to
> assume that the dummy inline assembly could both depend on the
> accumulator variable and change the accumulator variable, so it is
> forced to compute the value correctly before the inline assembly, and
> cannot assume anything about its value after the inline assembly.Хотя, о чем это я – этож опеннет.
> gcc или clang никогда не научатся разбирать содержимое ассемблерных вставок by design.
> Это контракт. Вопрос не имеет смысла.В вашей вселенной – возможно и контракт. В моей об этом речи нет:
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Assembl...
Да и ваш "пример" с
> __asm__ ("" : "=r" (var) : "" (var))отлично показал "надежность" этого "контракта".
Лучше на D перейти.
В нём уже есть вся эта защита и код на нём намного красивей получается.
> Лучше на D перейти.
> В нём уже есть вся эта защита и код на нём намного
> красивей получается.Тут смысл в другом. Изобрести золотую пулю. "И рыбку съесть...".
Типа и код не надо переписывать, и глюков связанных с behaviour не будет, и слово "защищенность" - магическое и хорошо монетизируемое.
То что на выходе будет замороженный компилятор, с отключенным оптимизатором, без возможности развития (новый С стандарт сломает совместимость, багфикс в оптимизаторе может сломать совместимость) - умалчиваем.
> Лучше на D перейти.
> В нём уже есть вся эта защита и код на нём намного
> красивей получается.эм, у меня под боком есть серверный линукс. программы на c и на go в нём есть, а вот на d -- ни одной. подскажете хоть одну?
Что за бред, простите. Оптимизатор - это одно. UB - совсем другое. Я, в общем-то, не совсем понимаю, как разумный человек может предлагать специфицировать UB, когда его ловить в статике надо и давать по рукам допустившим.Ну ладно, определили всё, что он захотел. И смысл в этом? Софт прибить к этой штуке гвоздями?
Смысл в том, чтобы ввести предсказуемое поведение в компилятор, для того чтобы не выгребать граблей и не изобретать потом костыли навроде SecureZeroMemory() потому-что компилятор щщетает что "memset() тут has no effect". A то что оно косвенный эффект имеет ему поуху.
Ну, то есть соответствующий софт ничем больше будет не собрать.
Пример такого компилятора в студию.Я вот тоже считаю, определить UB - это бред, да к тому же еще и слабо соотносящийся с реальностью. Как, например, определить поведением в случае ошибок в арифметике указателей? В случае разыменовывания NULL? В случае использования освобожденной/неаллоцированной памяти? В случае переполнения знаковых целых? И так далее. Чтобы для всех таких моментов определить поведение, нужно создать новый язык, с C имеющий очень мало общего. И результат, наверняка, будет прибит гвоздями либо к конкретному железу, либо к VM.
> Пример такого компилятора в студию.Любой современный. Dead store elimination считается безопасной оптимизацией и применяется всеми распространёнными компиляторами C даже на нижнем уровне оптимизации.
>> Пример такого компилятора в студию.
> Любой современный. Dead store elimination считается безопасной оптимизацией и применяется
> всеми распространёнными компиляторами C даже на нижнем уровне оптимизации.Удаление присваивания возможно _только_ в случае, если компилятор может _доказать_, что оно ни на что не влияло. Например, присваивание локальной переменной в функции, после которого присвоенное значение никуда более не попадало. Включая указатели на эту переменную.
Практически ни один компилятор не удалит memset во внешнем или глобальном буфере, например, потому что результат этой операции может быть виден в другой части программы. Чтобы доказать обратное, всю программу надо собирать с LTO и знать, что используется стандартный memset, а не переопределенная реализация.
>[оверквотинг удален]
>> Любой современный. Dead store elimination считается безопасной оптимизацией и применяется
>> всеми распространёнными компиляторами C даже на нижнем уровне оптимизации.
> Удаление присваивания возможно _только_ в случае, если компилятор может _доказать_, что
> оно ни на что не влияло. Например, присваивание локальной переменной в
> функции, после которого присвоенное значение никуда более не попадало. Включая указатели
> на эту переменную.
> Практически ни один компилятор не удалит memset во внешнем или глобальном буфере,
> например, потому что результат этой операции может быть виден в другой
> части программы. Чтобы доказать обратное, всю программу надо собирать с LTO
> и знать, что используется стандартный memset, а не переопределенная реализация.Чтобы доказать прямое, программу тоже надо собирать с LTO. Но, как её не собирай, компилятор не занимается доказательствами.
Типичный шаблон работы с буфером, предназначенным для ценных данных:
(1) Выделение страницы памяти в локальном контексте. mlock(), mprotect().
(2) Получение ценных данных, их запись в буфер, выделенный в (1).
(3) Обработка ценных данных.
(4) memset(). goto (2)И в этом цикле memset() с точки зрения компилятора — dead store.
>> Удаление присваивания возможно _только_ в случае, если компилятор может _доказать_, что
>> оно ни на что не влияло. Например, присваивание локальной переменной в
>> функции, после которого присвоенное значение никуда более не попадало. Включая указатели
>> на эту переменную.
>> Практически ни один компилятор не удалит memset во внешнем или глобальном буфере,
>> например, потому что результат этой операции может быть виден в другой
>> части программы. Чтобы доказать обратное, всю программу надо собирать с LTO
>> и знать, что используется стандартный memset, а не переопределенная реализация.
> Чтобы доказать прямое, программу тоже надо собирать с LTO.Не всегда. Я привел пример, когда это не требуется, и компиляторы так работают именно когда это допустимо.
> Но, как её
> не собирай, компилятор не занимается доказательствами.Ошибаетесь.
> Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0.Почему в 0 а не в INT_MAX? Почему они вообще должны инициализироваться? Полагаю что в стандарте эти вещи помечены как Undefined behaviour не чтобы дать свободу разработчикам компиляторов, а в ситуациях, когда единственно правильного поведения нет.
>Почему в 0 а не в INT_MAX?Т.е. ты действительно не умеешь в биты?
>Почему они вообще должны инициализироваться?
Потому, что не круто плодить неиспользуемые переменные.
> Полагаю что в стандарте эти вещи помечены как Undefined behaviour не чтобы дать свободу разработчикам компиляторов, а в ситуациях, когда единственно правильного поведения нет.
Ну вообще-то потому же, почему переменные можно объявлять по ходу кода. Системный же язык.
gcc уже давным давно выдает варнинг при попытке использования неинициализированной локальной переменной.
Без всяких Бернштейнов.
>gcc уже давным давно выдает варнинг при попытке использования неинициализированной локальной переменной.Только в самых тривиальных случаях.
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv)
{
int b;
printf("%lx",b);
}
gcc -Wuninitialized *.c
«b» is used uninitialized in this function
Предупреждение есть.
#include<stdio.h>
#include<stdlib.h>void take_ptr(int *ptr)
{
printf("YOLO World!");
}int main(int argc, char **argv)
{
int b;
take_ptr(&b);
printf("%lx",b);
}
Предупреждения нет.
"А вы на шкаф залезьте" (c)Естественно нет. У компилятора С просто нет информации чтобы в любом случае определить что происходит при передаче указателя на переменную в другую функцию.
Тут какой-то другой язык нужен :)
gcc -O2 -Wall unused.c
unused.c:12:3: предупреждение: <b> is used uninitialized in this function [-Wuninitialized] printf("%lx",b);
А -O3 находит такие варианты, что фиг поймёшь каким образом она может быть неинициализированой. А выставлять в таких случаешь заранее NULL, это далеко не всегда лучшее решение. Потом этот NULL всплывёт в какой нибудь структуре и уже концов не найдёшь.
$gcc --version
gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)во втором случае
gcc -Wuninitialized unused.c
предупреждений нет.
> во втором случае
> gcc -Wuninitialized unused.c
> предупреждений нет.Ключевое слово -O2, анализатор намертво прибит к оптимизатору.
Меня смущают люди, которые используют неинициализированные переменные, кроме как для инициализации оной.Объяснишь в чем сакральный смысл?
> Меня смущают люди, которые используют неинициализированные переменные, кроме как для инициализации
> оной.
> Объяснишь в чем сакральный смысл?Добавил: я к тому, что я уже на автомате делаю, что-то типа int i=0;
Не делайте так больше никогда. Рискуете пропустить важный варнинг от компилятора.
Иногда инициализация заключается в передаче указателя на переменную в функцию.Например вот такой широко распространенный в программах для UNIX кусок кода:
struct stat st;
int ret;ret = stat(filename, &st);
Бывают особые случаи.https://www.schneier.com/blog/archives/2008/05/random_number...
Это не "особые случаи", а ССЗБ, заслуженно наступивший на грабли.
> Полагаю что в стандарте эти вещи помечены как Undefined behaviour не чтобы дать свободу разработчикам компиляторов, а в ситуациях, когда единственно правильного поведения нет.UB это некорректная программа, а не 'нет единственно правильного поведения'. Unspecified ~ в основном был сделан ради узаконивания уже существующих компиляторов и для оптимизаций (на разных платформах могут быть удобны разные варианты).
очень интересно было бы понять что такое предсказуемый язык и что понимается под предсказуемостью ...
одно можно сказать - это как минимум функциональный язык, привет Rust!
все дело в разруливании указателей - в ржавчине можно явно утверждать (а не гадать на кофейной гуще), что данный участок unsafe
Безнадежная идея. Лучше уж сразу взять язык со ссылочной прозрачностью)) Проблемы с оптимизациями от отсутствия таковой. Любое неосторожное движение, неосторожный чих, неосторожная модификация кода может привести в Си к неэквивалентному коду. По-моему разработчики GCC уже ловились на том, что неправильно трактовали чистоту функции (ссылок не будет, потому как специально не фиксировал сей эпик фейл). Между прочим, упомянутые здесь ассерты к чистым функциям явно не относятся, потому их удалять из кода во время "оптимизации" совсем не дело.
> Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0Настолько что сразу начали бы писать гoвнокод который будучи скомпиленным другим комилятором не работал бы вообще.
Компилятор для защищённого ПО должен не ослаблять, а ужесточать правила. Напихать ассертов на все undefined/unspecified behavior, проверять переполнения после каждого изменения переменной и неопределённые значения, выход за границы буффера при каждом обращении к нему и т.д.
> выход за границы буффера при каждом обращении к немуПолучила функция на вход const char * и откуда компилятору знать где тут границы буфера ?
>> выход за границы буффера при каждом обращении к нему
> Получила функция на вход const char * и откуда компилятору знать где
> тут границы буфера ?Описывайте указатель на буфер, чтобы через sizeof() размер вычислялся, и чтобы нельзя вообще было указатель другого типа туда передать. И проблемы кончатся.
p = alloc_foo(100500);
do_bar(p);
Давайте расскажите мне как КОМПИЛЯТОР узнает размер буфера выделенного в p.
> Давайте расскажите мне как КОМПИЛЯТОР узнает размер буфера выделенного в p.Ладно ты технически безграмотен, но неужели совсем нет воображения чтобы подумать и догадаться?
p = alloc_foo(100500); // SaveGlobalAllocation((void*)p, (size_t)100500);
do_bar(p); // ASSERT(CheckGlobalAllocationsTable(p));
Откуда компилятору знать что alloc_foo вообще выделяет память ?
Откуда компилятору знать что 100500 это размер, а не некие битовые флаги ?Или вы предлагаете все эти костыли руками городить ? Зачем тогда нужен магический компилятор Бернштейна ?
И кстати вдогонку рантайм кост всего этого дерьма вы себе представляете ?
> И кстати вдогонку рантайм кост всего этого дерьма вы себе представляете ?Еще один указатель на p+sizeof(DATA) и все. См реализацию управления памятью чуть менее чем всех языков после С/С++
Тем более все компиляторы хранят размеры выделенной памяти для того, чтоб free(p) знала сколько ее освобождать.
Штука в том, что по (невалидному) указателю все эти метаданные можно повредить и в результате рано или поздно ты получишь то, что в языке называется UB. Или, например, как ты защитишься от double free? Запретить арифметику указателей? Сделать указатели auto-null при освобождении? Подсчет ссылок? GC? Тогда это не C, и не морочьте голову.
> Штука в том, что по (невалидному) указателю все эти метаданные можно повредить
> и в результате рано или поздно ты получишь то, что в
> языке называется UB. Или, например, как ты защитишься от double free?Проверкой сигнатуры в блоке памяти до указанного указателя. Проверкой указателя по списку выделенных блоков. Это не проблема. Конечно, тот же блок может быть выделен повторно, но даже с такой примитивной проверкой можно отсеять 99% повторных попыток освободить уже освобожденный блок. С помощью операционной системы можно использовать всё доступное адресное пространство (все 64 бита) так, чтобы дополнительно снизить вероятность повторного выделения блока, адрес которого уже выделялся ранее.
> Запретить арифметику указателей? Сделать указатели auto-null при освобождении? Подсчет
> ссылок? GC? Тогда это не C, и не морочьте голову.Не морочьте голову мне.
>> Штука в том, что по (невалидному) указателю все эти метаданные можно повредить
>> и в результате рано или поздно ты получишь то, что в
>> языке называется UB. Или, например, как ты защитишься от double free?
> Проверкой сигнатуры в блоке памяти до указанного указателя. Проверкой указателя по списку
> выделенных блоков. Это не проблема. Конечно, тот же блок может быть
> выделен повторно, но даже с такой примитивной проверкой можно отсеять 99%
> повторных попыток освободить уже освобожденный блок.Т.е. вас устроит "безопасность по пятницам"? Я-то думал, мы хотим повысить надежность.
> С помощью операционной системы можно
> использовать всё доступное адресное пространство (все 64 бита) так, чтобы дополнительно
> снизить вероятность повторного выделения блока, адрес которого уже выделялся ранее.Что, mmap'ить страницы повторно? Это не поможет от невалидных указателей, смотрящих в старые зеркала. И потом, у меня и так есть куда тратить адресное пространство, если что. Даже в 64-битных системах.
Миллион способов однозначно получить информацию о размерах буффера по адресу, начиная с банальной глобальной таблицы.
Глобальной таблицы чего ? Кто в нее будет писать ?p = mmap(NULL, 100500, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0);
Узнайте мне из другой функции размер выделенного блока памяти по адресу p.
P.S. Вы бы сначала хотя бы русского языка выучили. Буффер блин.
А как free(p); знает сколько памяти освобождать? Учите матчасть.
> А как free(p); знает сколько памяти освобождать? Учите матчасть.Вы бы сами подучили. Не malloc/free единым, как говорится.
Прочитай про разницу между компиляций и исполнением. Подумай, в какой момент выполняется free.
выделением/освобождением памяти занимается ядро, а free() - это syscall
Что? O_o
> выделением/освобождением памяти занимается ядро, а free() - это syscallСадись, два. free - это библиотечная функция, она работает с кучей. А уже место под кучу выделяется операционкой по запросу библиотеки. Размер кучи можно только увеличить или урезать. Вернуть операционной системе неиспользуемый фрагмент из середины кучи нельзя.
> Вернуть операционной системе неиспользуемый фрагмент
> из середины кучи нельзя.Можно вернуть страницу целиком.
>> Вернуть операционной системе неиспользуемый фрагмент
>> из середины кучи нельзя.
> Можно вернуть страницу целиком.malloc и free дают/возвращают страницы???
> malloc и free дают/возвращают страницы???Теоретически, mmap - реализации это могут
Речь о том, что free может вернуть страницу, если в ней больше не осталось используемых областей.
>> выделением/освобождением памяти занимается ядро, а free() - это syscall
> Садись, два. free - это библиотечная функция, она работает с кучей. А
> уже место под кучу выделяется операционкой по запросу библиотеки.Не пиши больше, иди ёжиков культивируй.
>>> выделением/освобождением памяти занимается ядро, а free() - это syscall
>> Садись, два. free - это библиотечная функция, она работает с кучей. А
>> уже место под кучу выделяется операционкой по запросу библиотеки.
> Не пиши больше, иди ёжиков культивируй.Мануалы не читаем, команды пишем.
> Глобальной таблицы чего ? Кто в нее будет писать ?
> p = mmap(NULL, 100500, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0);
> Узнайте мне из другой функции размер выделенного блока памяти по адресу p.
> P.S. Вы бы сначала хотя бы русского языка выучили. Буффер блин.Вообще, тут можно сделать библиотечную обвязку для mmap, которая будет запоминать в отдельной области размер каждого блока, который отображался в память. При желании - это не проблема. Всё равно большинство программ не пользуются напрямую системными вызовами, а используют libc. Вот там для каждого системного вызова и можно сделать такую обвязку. При желании.
>> Глобальной таблицы чего ? Кто в нее будет писать ?
>> p = mmap(NULL, 100500, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0);
>> Узнайте мне из другой функции размер выделенного блока памяти по адресу p.
>> P.S. Вы бы сначала хотя бы русского языка выучили. Буффер блин.
> Вообще, тут можно сделать библиотечную обвязку для mmap, которая будет запоминать в
> отдельной области размер каждого блока, который отображался в память. При желании
> - это не проблема. Всё равно большинство программ не пользуются напрямую
> системными вызовами, а используют libc. Вот там для каждого системного вызова
> и можно сделать такую обвязку. При желании.Боюсь, что прокатит ваш вариант. Начиная с того, что появляется дополнительный источник возможных ошибок в виде выделения памяти под эту "отдельную область". Эти ошибки не предусмотрены POSIX. Еще, например, видятся проблемы с signal safety. Так что обертку такую замутить теоретически возможно, но называться она будет не mmap.
> Боюсь, что прокатит ваш вариант.*не прокатит
>> Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0
> Настолько что сразу начали бы писать гoвнокод который будучи скомпиленным другим комилятором
> не работал бы вообще.Больше хорошего и разного гoвнокода.
> Компилятор для защищённого ПО должен не ослаблять, а ужесточать правила. Напихать ассертов
> на все undefined/unspecified behavior, проверять переполнения после каждого изменения
> переменной и неопределённые значения, выход за границы буффера при каждом обращении
> к нему и т.д.«Лучшее враг хорошего.»
Хотя, это смотря с какой стороны посмотреть.
Считаю, что лучший компилятор должен не ужесточать а ослаблять правила (в вашей интерпретации).
Что бы средний интеллектуальный уровень программистов работающих с данным компилятором рос (а компилятор «тупел»). Но нет же «Лень двигатель прогресса».А еще есть Pascal (и прочие).
> Например, насколько бы более удобной стала разработка, если бы разработчики
> точно знали, что все новые переменные всегда автоматически должны
> инициализироваться в значение 0."Насколько Ермолова играла бы лучше вечером, если бы она днём, понимаете, работала у шлифовального станка" © х/ф "Берегись автомобиля"
А разве -Wall -Wextra -Werror -pedantic-errors не будет достаточно для исключения ситуаций с UB?
За это Бернштейну грант не дадут.
Если нужно именно исключение из кода возможности UB, то нужен не компилятор, а статический анализатор.
Но этот товарищ, похоже, как раз хочет оставить UB в коде, но при компиляции разрешать неопределенность заранее известным образом.
Подозреваю, если бы это было хорошей идеей, все разработчики компиляторов давно уже успели бы до нее додуматься...
> А разве -Wall -Wextra -Werror -pedantic-errors не будет достаточно для исключения ситуаций
> с UB?UB далеко не всегда можно определить в compile time. Кстати, сюда добавь ещё clang'овский -Weverything - этот определяет действительно много проблем, в отличие от -Wall
> А разве -Wall -Wextra -Werror -pedantic-errors не будет достаточно для исключения ситуаций
> с UB?Ололо? Наивные юноши думают что компилятор спасет их от необходимости думать?
int main(void)
{
int x = 5;
int* y = &x;
x[y] = 125;
y[x] = 125;
return 0;
}
$gcc -Wall -Wextra -Werror -pedantic-errors fail.c
//нет предупреждений.
$gcc fail.c && ./a.aout
segmentation fault
Еще один любитель залезать на шкаф.
> Еще один любитель залезать на шкаф.Цитата: А разве -Wall -Wextra -Werror -pedantic-errors не будет достаточно для исключения ситуаций с UB?
Ответ - Да!**Для программ не больше 3-х строк.
Если "Залезание на шкаф" в вашем понимании - вызов функции или арифметические операции над указателями, то все не хелоуворды смотрят на вас со шкафа с недоумением.
если убрать из С неопределённое поведение, это уже будет не С
>По мнению Бернштейна сообществу не хватает компилятора, который мог бы обеспечить предсказуемое поведение и предоставить чётко определённую семантику для всех ситуаций, отмеченных в стандарте Си как неопределённые (undefined), неспецифицированные (unspecified) или зависящие от конкретных реализаций.Мужик не слышал про CompCert?
Напиши ему.
> Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0.какая разница, если всё равно это будет иметь эффект лишь только на этом эталонном компиляторе..
опасность что завтра сделают новую опетимизацию UB (на не эталонном компиляторе) -- не улетучивается
кажется речь про LLVM/RUST
> кажется речь про LLVM/RUSTНет, речь про C компилятор
> Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0.Go уже придуман.
Трололо?
У Go значительная часть криптографии, включая реализацию curve25519 от того же Бернштейна, нафигачена на языке АССЕМБЛЕРА.https://github.com/golang/crypto/blob/master/curve25519/cswa...
Больше 70 комментариев с вопросами и поучениями, которые Бернштейн предусмотрел и заранее ответил. Неужто страничка английского текста оригинала настолько сложна для понимания?
> Больше 70 комментариев с вопросами и поучениями, которые Бернштейн предусмотрел и заранее
> ответил. Неужто страничка английского текста оригинала настолько сложна для понимания?Так он и ответил в стиле местных комментариев - "мне кажется, это было бы круто, и я думаю, это возможно".
Гёдель смотрит с недоумением.
> Гёдель смотрит с недоумением.да-да, и в то же самое время грустно и свирепо.
вы про неполноту или про всякую программу, которую можно сократить на одну команду?
>Например, насколько бы более удобной стала разработка, если бы разработчики точно знали, что все новые переменные всегда автоматически должны инициализироваться в значение 0.Да-да, мы помним как в Debian именно так и улучшили именно криптуху :)))
IMHO, в плане безопасности сейчас большую пользу принесёт статический анализатор кода.Также, есть мнение, что перспективнее исследовать текущие реализации компиляторов, предложить свои "безопасные" и вместе с тем эффективные варианты оптимизации, например, для gcc (-Osecu).
а потом через Х лет мы на этот компилятор должны будем наложить 100 сторонних патчей, чтобы оно работало :)
Вот интересно, ему заняться больше нечем, кроме как велики изобретать? В наше время языков - плюнуть некуда. Выбирай! Для каждой задачи - свои инструменты. Нет в это ничего плохого. Разве что - это необходимость изучения нового языка. На С еще свет не сошелся клином...
> В наше время языков - плюнуть некуда. Выбирай! Для каждой задачи - свои инструменты.Ага, и код всех немного староватых сишных программ на них переписывай.
"Больше всего я не люблю, когда мне говорят, что делать и чего не делать. Я полностью отвергаю людей, которые полагают, что имеют право влиять на мои решения. (Кроме, возможно, моей жены.)"
/Торвальдс/p.s. Ну вот, мне даже добавить нечего.
p.s.2. Хотя почему нечего: тем, кто пытается остальных заставить делать что-то, отвергая любые альтернативы, нужно бить лопатой по башке. Да, вот просто так - лопатой по башке. По-другому такие не лечатся.
но это тоже ведь попытка "заставить делать" !!!
А потом получается жутко неудобный и костыльный epoll(), чисто потому, что делать как в kqueue() ему не хотелось - чо, имеет же право делать по-своему, а не как в BSD ? Имеет! И пофиг, что получается не прогресс, а "назло мамке уши отморожу".
Уважаемые эксперты данной темы форума - извиняюсь за оффтопик, но посоветуйте пожалуйста годный качественный материал по практическому и быстрому освоению указателей в C/C++Буду признателен.
Освоение указателей в С/С++ - это 5% теории, которую можно вычитать в любом учебнике (причем лучше всего - в учебнике по ассемблеру) и 95% реального использования во всяких разных случаях, особенно в функциях и классах, а также между ними.
Туториалы тут бесполезны - нужно самому потоптаться по граблям, только тогда понимаешь, с какой стороны у них ручка.
Их у меня есть - сколько не пытался - приходится погружаться в какие-то дебри и мыслить как компилятор. Хотелось бы больше ясности и простоты. Уж слишком много тут неоднозначностей.5% - допустим ОК. Но это очень малая цифра. С трудом верится.
Читал вот этот:
http://boredzo.org/pointers/Сначала понравилось, потом, дойдя до массивов, понял что представления ошибочны. Прямо как в классике: знаю только то, что ничего не знаю (про указатели)
Но хотелось бы без усилий читать код и понимать правильность указателей...
Когда-то читал советский учебник про среду си и манипулятор "колобок" - надо отдать должное там про указатели довольно хорошо растолковано. Где то встречал общий синтаксис и семантику указателей. Довольно удачно все разложено. Конечно сейчас сложно разыскать. Если только в библиотеку зайти или в "старую книгу" ...
> Их у меня есть - сколько не пытался - приходится погружаться в
> какие-то дебри и мыслить как компилятор. Хотелось бы больше ясности и
> простоты. Уж слишком много тут неоднозначностей.Чтобы грамотно и эффективно пользоваться языком, вам придется в какой-то мере "мыслить как компилятор". Никаких неоднозначностей при этом не возникает, как раз наоборот.
> Читал вот этот:
> http://boredzo.org/pointers/
> Сначала понравилось, потом, дойдя до массивов, понял что представления ошибочны. Прямо
> как в классике: знаю только то, что ничего не знаю (про
> указатели)Что именно вам не понятно? Всё по ссылке не читал, но на первый взгляд написано там очень по-простому, даже слишком. Из-за излишних упрощений многие тонкости могут теряться.
> Но хотелось бы без усилий читать код и понимать правильность указателей...
Без усилий осваивается разве что Basic. И то...
Если C упорно не дается, смотрите в сторону чего по-проще. В Java, например, нет указателей.
меня интересуют именно указатели
Тогда вот вам хорошая старая книга: http://lib.ru/CTOTOR/IBMPC/abel.txt_with-big-pictures.html
Первые три главы избавят вас от иллюзий о каком-то волшебстве в памяти и процессоре, и вы поймете, что такое указатели. Как с ними жить - вот тут понадобится практика, книги не помогают.
шутку оценил сенкс ... еще bredly.pak забыли и нортонгайды, спасибо конечноКогда то почитывал желтую большую книжку tasm 2.0
Мне бы хотелось "играть с листа" - читать сходу указатели и понимать на что они ссылаются в данном учатке кода.
Никаких шуток. Чтобы понять указатели, нужно понять адресную арифметику и RTTI, а главное - вот эту самую ужасную правду: что кроме адресной арифметики и RTTI, в указателях ничего нет, и ты должен думать об их проблемах сам.
OK - спасибо, мне знакома эта книженция. В общем понятно - копать в сторону ассемблера и книг.С этим нет вопросов.
Вопросы остаются в понимании еще и синтаксиса указателей Си, особенно комплексных случаев.
> Их у меня есть - сколько не пытался - приходится погружаться в
> какие-то дебри и мыслить как компилятор. Хотелось бы больше ясности и
> простоты.Си - низкоуровневый язык. Некоторые называют это "системный". Почти ассемблер. И ты мыслишь "как компилятор" потому, что пишешь почти на ассемблере. Надо как-то бороться со сложностью -- api, инкапсуляция, дисциплина кодирования [да, =работы компилятором].
> Сначала понравилось, потом, дойдя до массивов, понял что представления ошибочны. Прямо
> как в классике: знаю только то, что ничего не знаю (про указатели)
> Но хотелось бы без усилий читать код и понимать правильность указателей...И абстракции ("больше ясности", "без усилий читать") далеки от выразительной части (указатнли, да). Более того, у каждого свои само-ограничения (если вообще), и именно чтобы читать чужой код, нужно сначала просечь абстракции и [само]ограничения кодирования автора(-ов). В некоторых проектах пишут кодинг стайл гайды, апи документацию, требования к добавляемому коду. Даже комментарии в коде пишут! Понятно, что и не везде, и слишком мало, и слишком поздно. Какие-то проекты "ломятся" от унаследованного кода, который или прочитать, или изменить, не поломав пользователям всё-всё-всё, уже никто не может. И пр., и т.п., и дл.
Возьми питон. Или бейсик. Или джавву. Или <вставить по вкусу>.
Это проще.
Сам - на баше, авке, седе и ко. Не программист, админ. Кернигана "в детстве" читал, однострочники на Си патчую, но не "пишу". Таков мой "ценный" опыт "понимания указателей".
С абстракциями, ООП, SOLID, DRY проблем как раз нет, просто есть сложности с освоением синтаксиса указателей.Например конструкции вида:
int* ptr;
и
int *ptr;Меня быстро ставят в тупик. Я понимаю что первые 5 лет тяжело потом привыкаешь, но все-же есть доходчивые материалы для занятых по синтаксису указателей.
Мне си нужен для написания различных расширений к другим языкам и задачам. Наконец просто хотелось бы в нем свободно ориентироваться.
Опыт написание расширений есть - когда-то очень давно писал сишные модули для clipper.
Если речь об обычных указателях, то с синтаксисом там как раз всё просто.> int* ptr;
> int *ptr;
> int * ptr;Все три строки значат ровно одно и то же - указатель на int. Но надо знать, что в объявлении переменной "указатель" относится к конкретной переменной, а не типу. Т.е. здесь:
> int *ptr, n;
ptr - это указатель на int, а n - это int. Поэтому многие предпочитают писать * рядом с именем переменной, несмотря на то, что это все еще часть типа. Этот, на мой взгляд, нелогичный момент надо просто запомнить и стараться объявлять переменные на разных строках.
> int* ptr;
> int n;Так вопросов не возникает. Другой вариант - это использовать typedef.
> typedef int* int_ptr;
> int_ptr ptr1, ptr2;Поскольку int_ptr - это псевдоним для типа int*, обе переменные ptr1 и ptr2 - это указатели на int.
Также надо понимать, как правильно квалифицировать типы в указателях. Ведь тип указателя на самом деле содержит квалификаторы типа, на который указатель указывает (например, можно ли менять значение по указателю), так и квалификаторы указателя (можно ли менять сам указатель). Например:
> const int* ptr1; // указатель на const int, т.е. значение по указателю менять нельзя
> int const* ptr2; // то же самое
> int* const ptr3; // указатель на int, значение по указателю менять можно, но сам указателю менять нельзя
> const int* const ptr4; // ничего менять нельзяТ.е. всё, что находится до *, относится к типу объекта, на который указывает указатель. Всё, что после - к самому указателю. Это правило действует и при более высоких уровнях индирекции.
> int** pptr1; // указатель, который указывает на указатель на int
> const int** pptr2; // указатель, который указывает на указатель на const int
> const int* const* pptr2; // указатель, который указывает на константный указатель на const int
> const int* const* const pptr2; // константный указатель, который указывает на константный указатель на const int
>[оверквотинг удален]
>> int const* ptr2; // то же самое
>> int* const ptr3; // указатель на int, значение по указателю менять можно, но сам указателю менять нельзя
>> const int* const ptr4; // ничего менять нельзя
> Т.е. всё, что находится до *, относится к типу объекта, на который
> указывает указатель. Всё, что после - к самому указателю. Это правило
> действует и при более высоких уровнях индирекции.
>> int** pptr1; // указатель, который указывает на указатель на int
>> const int** pptr2; // указатель, который указывает на указатель на const int
>> const int* const* pptr2; // указатель, который указывает на константный указатель на const int
>> const int* const* const pptr2; // константный указатель, который указывает на константный указатель на const intпремного благодарен за растолкованные нюансы и ваше время, особенно в случае const - теперь все более проще и понятнее
Действительно, int* защита фактически от случайной ошибки - довольно удобно. Насчет const - фактически это указание компилятору, что данный указатель не должен меняться. Несколько озадачивает в силу динамического выделения памяти.
Осталось разобраться в указателях на функции, структуры и массивах, "раз-адресации"
Мне понравились выкладки в этом комментарии:
http://stackoverflow.com/a/8615494/1633548
Странный, однако, способ бороться с архитектурными (дизайнерскими) проблемами самого Си.
Как можно с бесстыжей рожей называть себя "экспертом" и при этом предлагать полную ахинею в свете существующего языка D? Ну ладно, го-расты тоже в счёт. То есть чувак не знает, что "альтернатива Си" существует лет так 15??
Как можно с бестыжей рожей утверждать, что всякие Дэ и Го со своими сборщиками мусора хоть когда-нибудь смогут достичь производительности и потребления памяти Си
Просто он предлагает выпустить компилятор с дополнительными замечаниями типа:
Daniel J. Bernstein warns that ...
Хочешь - избавляйся от них, хочешь - нет.Я так понял.
Такой компилятор, а точнее язык, уже есть - C99 :) Левое какое-то предложение. Unspecified или undefined означает, что не нужно рассчитывать на реализацию архитектуры, используемой разработчиком. На другой версии или архитектуре может быть не так. Так что лучше просто инициализировать переменные и избегать любых неоднозначных моментов по стандарту.
"Дураку" дай хоть танк - он его угробит.По факту проблема не в языках, а в отсутствии грамотных СИСТЕМНЫХ программистов, способных писать алгоритмы с нуля и понимать каждую написанную строку кода.
Недавно посещал универ - искал ребят на проект - из потока в 30 человек - алгоритмы бинарного поиска знают и понимают 5ро!! (facepalm), остальные дубы дубами - пребывающие в заблуждениях что КТО-ТО для них должен сделать все, как то - разработать язык так сказать, без "архитектурных проблем" связанных с с указателями и т.п. И сядут они ЛАБАТЬ свои поделки...
Про Ассемблер тут я вообще молчу.
ТОлько не надо опять горло драть про скорости разработки и т.п. Скорость нужна при ловле блох, разработке пользовательских интерфейсов и конечного софта для клиента. Но никак не в разработке концептуальных/основополагающих библиотек.
Да и спешка сейчас привела к деградации качества программного обеспечения, как со стороны обучения программистов, так и со стороны разработки реально новых концепций. Хотя заглядывая чуток в будущее ИМХО - сверхприбыли из IT уйдут, и все вернется на круги своя.
все, кто там вякает, что типа "язык СИ хуже моего любимого бренфака" - назовите хоть одну нормальную прогу, написанную НЕ НА СИ. И чтобы она ещё решала задачку нормальной сложности, типа openssl, ffmpeg, qemu, а не просто набор скриптов типа docker.