LINUX.ORG.RU

Навеяно свежей дырой в Xorg

 , ,


9

7

Привет, ЛОР!

Ты, наверное, уже видел свежую дыру в Xorg, патч для которой выглядит буквально вот так:

-        else
+        else {
             free(to->button->xkb_acts);
+            to->button->xkb_acts = NULL;
+        }

В связи с этим у меня возник вопрос: а почему в стандартной библиотеке C нет макроса SAFE_FREE()?

#define SAFE_FREE(ptr) do{free(ptr);(ptr)=NULL;}while(0)

Напомню, что значение указателя после вызова free() является неопределённым согласно стандарту. Не только значение памяти, на которое он указывает, но и значение самого указателя, и работа с ним представляет собой жуткое undefined behaviour, а значит единственное что можно сделать – занулить его.

Так вот, почему даже таких банальных вещей нет? Я уже не говорю про строковый тип, а то даже Эдичка тут строки не осилил.

Моя гипотеза тут: C – это язык культа страданий во имя страданий.

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

Она занимается самим менеджером памяти.

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

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

UB не у любой, а именно у free.

Осталось увидеть, где написано, что меняется аргумент, а не память по этому указателю и самое главное - ЗАЧЕМ? Чтобы падало по NULL? Без дополнительного ключа? Чтобы загнать проблему под ковёр?

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

Сегфолт не просто так придумали назло.

Его не то чтобы специально придумывали, это такой же «железячный» сигнал, как SIGFPE. Просто MMU не знает, во что транслировать виртуальный адрес, который ему дали, и ОС оповещает об этом сигналом. На некоторых платформах вместо SIGSEGV может прилетать SIGBUS.

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

Меняется не только аргумент, а вообще все причастные указатели

Классный у них подход, я так тоже могу писать стандарты: смешали в одну кучу «static, thread, automatic, and allocated», а потом объявили, что всё неопределено, но в виде описания bla-bla к общему тексту в этой куче, но, заметьте, в описании free() никакого предупреждения, что «все причастные указатели» ничего не сказано. Ну вот я заведу флаг=NULL, в конце функции проверяю его, а оно возьми и оптимизатором как копия переменной и обнули по free() и прочими «все причастные указатели». И как узнать, это была ошибка или вообще ничего не делалось и переменная не менялась с начального значения?

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

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

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

Сегфолт не просто так придумали назло.

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

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

И я ответил - падение при обращении по нулю именно что придумали.

Не так всё было. «Придумали» как раз не так уж и давно как стало это в принципе возможно не жалеть адресное пространство. На железках, где придуман был C и Unix было всего 8 страниц по 8 Кб и заюзать защиту убрав из жалких 64k целых 8к было расточительно и там чтение до адреса=brk()+стек вполне себе не приводило ни к какому сигналу. С записью тоже было еще интересней и долго писать. Впрочем, сейчас вообще не о том речь, даже не замечаете, что хамите и не по делу.

Чтоб отстреливать говнокодеров.

Сокрее пользователей. А если по делу, то менять аргумент у free - это не отстреливать, а заметать под ковер. Вот если бы там был специальный адрес, на который был специальный сигнал - для отладки прекрасное решение. (Впрочем как обычно биты под сигналы всегда все заюзаны). Но NULL же - это и не шмогла malloc() и free(NULL) - ничего и никак. То есть маскировка. Чтобы отстреливать на самом деле надо защищать ВСЮ область освобождаемую free, но это не сделать на существующем железе (разве что БЭСМ это умело).

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

6.2.4/2 of C99 The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.

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

на многочисленные вопросы насчет «а где такое есть ну или хотя бы как (и нафейхоа бы) такое запилить» поясняющие в ответ мычат что-то вроде «текущих имплементаций канпеляторов, таких штоп прям вот так, мы не знаем… но мы оставляем свободу создателям херачить шо им в голову взбредет, ну а мало ли что…»

«Вам, вероятно, придется представить себе реализацию/аппаратное обеспечение, которое выполняет много дополнительной работы, чтобы каждый раз, когда значение указателя загружается в регистр, оно каким-то образом проверяло, действителен ли адрес.»(с)

«вам придется представить себе» ))

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

И?

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

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

и там чтение до адреса=brk()+стек вполне себе не приводило ни к какому сигналу

Сейчас-то приводит? Причем, не только в юниксах. Я хз, к чему этот экскурс в историю.

даже не замечаете, что хамите

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

Чтобы отстреливать на самом деле надо защищать ВСЮ область освобождаемую free

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

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

Сейчас-то приводит?

Ну офигеть. Да, 0 адрес попадает в диаппазон 0 - brk()+стек. Но так как адресного пространства дофига, сделали первую страницу с защитой.

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

Да блин, вы вообще стартовое сообщение читали? Патч видели? Я начал всю эту бодягу с железным логическим аргументом: если free будет менять аргумент, значить и занулить его нельзя, его больше нет, его поменяли! А в ответ что я слышу? Впрочем, что я хотел, это же ЛОР...

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

Да, 0 адрес попадает в диаппазон 0 - brk()+стек

Вопрос был не в этом. Как тот абзац про ранний юникс опровергает мою мысль?

А в ответ что я слышу?

Явно что-то своё. Впрочем, твой железный аргумент я тоже осилить не смог. Это ЛОР, что ты хотел.

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

Ну не всем же нравятся минные поля. Сколько было тредов типа «Почему новая версия cc сломала мой код? Ну и что что UB, очевидно же что должно работать». Даже на ЛОРе было. Компиляторы бывают умнее и внезапнее, чем мы о них думаем.

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

Как тот абзац про ранний юникс опровергает мою мысль?

Попробуйте перечитать, может дойдёт. «Придумали для» железно опровергается историческим эксурсом. NULL - это не для убийcтва говнокодеров, это был и есть флаг, начальное значение. Могло бы быть и 0xffffffff, но во-первых это валидный адрес стека и потому не сильно отличается от присвоенного во время работы значения, во-вторых зависит от разрядности, а (-1) говорят не везде это же самое (хотя я только читал о таком железе и в живую не видел).

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

Сколько было тредов типа «Почему новая версия cc сломала мой код? Ну и что что UB, очевидно же что должно работать»

Мой любимый обосрамс на эту тему: Это вообще законно? Провал выполнения в нижележащую «мёртвую» функцию

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

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

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

В защищённом то же самое, только адреса не физические, а виртуальные. И происходит обращение к виртуальному адресу, которому не поставлен в соответствие физический, через таблицы страничной трансляции. И MMU ничего не остаётся как вызывать соответствующий обработчик прерывания. Дальше ОС либо пытается исправить ситуацию (например, загрузив страницу из подкачки и сделав адрес валидным), либо убивает процесс (возможно, предварительно уведомив его через сигналы и дав уже пользовательскому коду шанс что-то сделать).

KivApple ★★★★★
()

Моя гипотеза тут: C – это язык культа страданий во имя страданий.

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

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

напиши свою макру или функцию safe_free и пользуй если надо.

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

Перекидываем указатель из предыдущего блока на следующий и всё,

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

В защищённом то же самое, только адреса не физические, а виртуальные.

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

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

Сначала тебе кажется всякое, а потом баги с дырами в коде.

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

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

Мне показалось, что ”use the value of a pointer” — это именно про разыменование.

Насколько я понял, это UB нужно, чтобы в вышеупомянутом примере https://gcc.godbolt.org/z/ar8Pr7T1P компилятор имел право выдать ошибку. Никакой адекватный компилятор, естественно, не будет переписывать значение указателя.

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

Моё ЧСВ? Чувак, я вообще в шоке, если честно! Эти же люди будут писать код, которым мне пользоваться! Это же ужас, на самом деле, полный.

Существует как миниму три разных языка C:

  • Язык C как он описан в стандарте;
  • Язык C как его реализовали авторы компиляторов;
  • Язык C в фантазиях большинства программистов на нём.

И это три мать его разных языка!

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

Ну тут только два пути: 1) считать код рабочим, пока не доказано обратное, и решать проблемы по мере их возникновения; 2) писать весь код самому, не используя сторонние библиотеки, как это делал djb

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

Занулять переменную, которая больше не будет использоваться - бессмысленная трата циклов.

Не в случае указателя. Представь, что его после free разыменовывают еще раз, что тогда?

LongLiveUbuntu ★★★★★
()