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 – это язык культа страданий во имя страданий.

После прочтения обсуждения осталось непонятным почему работа именно со значением указателя – это UB, а значит ужас и кошмар. Подчеркну, что не разыменование указателя, а работа со значением указателя (вроде печати значения указателя). Например, сравнение указателя, который был передан во free c NULL – это UB, и если да, то почему?

Скажем, был такой код:

void data_cleanup(data * ptr) {
  if(ptr != NULL) {
    ... // Какие-то действия по очистке.
    free(ptr);
  }
  else {
    ... // Какие-то другие действия. Допустим, просто
        // печать в лог о том, что data_cleanup был вызван.
  }

  ... // Еще какие-то действия, которые не зависят от
      // значения ptr.

  // А здесь нужно еще что-то сделать если ptr не был NULL.
  if(ptr != NULL) { // (1)
    ... 
  }
}

Если любое действие с указателем после передачи его во free есть UB, то тогда показанный выше код некорректен (в точке (1) есть UB). Следовательно он должен быть переписан. Скажем, вот так:

void data_cleanup(data * ptr) {
  int not_null = ptr != NULL;
  if(not_null) {
    ... // Какие-то действия по очистке.
    free(ptr);
  }
  else {
    ... // Какие-то другие действия. Допустим, просто
        // печать в лог о том, что data_cleanup был вызван.
  }

  ... // Еще какие-то действия, которые не зависят от
      // значения ptr.

  // А здесь нужно еще что-то сделать если ptr не был NULL.
  if(not_null) {
    ... 
  }
}

Но как-то это маразмом попахивает.

В конце-концов значение указателя – это просто набор бит. Роль указателя этот набор бит начинает играть только когда его (набор то есть) используют для обращения к данным по указателю, т.е. во время операции разыменования. А если операции разыменования нет, то откуда же UB?

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

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

Двухсвязный список выкидываем на свалку?

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

После прочтения обсуждения осталось непонятным почему работа именно со значением указателя – это UB

Потому что так написано в стандарте языка.

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

Двухсвязный список выкидываем на свалку?

Да, начинай. Потому как в случае с двухсвязным списком при удалении элемента надо сначала убрать его из собственно списка и поменять указатели и только потом освобождать память (когда указатель на элемент останется только один). Если у тебя наоборот сделано, то ты говнокодер и тебя надо облить смолой. Становись в очередь.

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

Потому что так написано в стандарте языка.

Или же дело в интерпретации того, что написано.

Или же дело в том, что в стандарт еще не успели внести уточнения, которые легализуют то, что по факту применяется (как это происходило со стандартом C++, к примеру).

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

Или же дело в интерпретации того, что написано.

Чо? Там всё написано весьма однозначно: работа со значением указателя после вызова free() является UB. Это тебе не библии чтобы интерпретировать как захочется.

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

А термин «работа» там как определен?

Вот это работа: p != NULL. И вот это работа: i = *p;. Но есть нюанс.

Ну и напомню вторую часть:

Или же дело в том, что в стандарт еще не успели внести уточнения, которые легализуют то, что по факту применяется (как это происходило со стандартом C++, к примеру).

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

А термин «работа» там как определен?

Там написано «использование значения указателя». Что тебе непонятно-то?

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

Что значит «не успели»? 30 лет уже не успевают? Ей богу, тебе бы в семинарию преподавать.

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

В конце-концов значение указателя – это просто набор бит.

или trap representation.

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

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

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

«Низкий уровень – это когда низкий уровень». Это не определение, чувак.

Какая жалось. Придётся Вам искать кого-то другого, кто объяснит Вам, что такое низкий уровень. Я потерпел крах. Но Вы ведь можете, не понимая этого, хотя бы просто поверить, что Си – низкий уровень. Аминь.

В Джаве можно открыть файл, просрать объект и остаться с открытым файлом в процессе. Или попытаться прочитать из уже закрытого файла. Жаба – низкоуровневый язык?

Бед наделать на любом языке можно. Это не показатель. Низкий и высокий уровень в общем случае не про то, что компилятор с рантаймом будут подтирать зад разработчику.

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

Просто выделение региона и работа с ним – обычное дело. Включая множество указателей на это регион. И обращение по этим указателям со смещением.

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

Придётся Вам искать кого-то другого, кто объяснит Вам, что такое низкий уровень. Я потерпел крах.

Отлично. Проваливай давай из этого треда и вообще с моего ЛОРа. Тебя тут никто не любит.

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

Ты сам себе противоречишь. Раньше ты писал что «Низкий уровень – это когда высокоуровневый возни нету.»

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

А чо полыхать-то? Ты просто не умеешь программировать на C. Это печально.

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

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

Ты сам себе противоречишь. Раньше ты писал что «Низкий уровень – это когда высокоуровневый возни нету.»

А по Вашему подтирание разработчику зада – высокоуровневая возня? Как интересно. Но я не это имел ввиду, да и с чего бы это.

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

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

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

Получается, что C – высокоуровневый язык, поскольку от железа и системы он весьма и весьма далёк.

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

Там написано «использование значения указателя»

Непонятно какое именно использование. Сравнение с NULL – это использование. Разыменование – это использование.

Мне непонятно, почему сравнение с NULL должно считаться UB.

Что значит «не успели»? 30 лет уже не успевают? Ей богу, тебе бы в семинарию преподавать.

Ну а вот, к примеру,std::launder в C++ стандарт когда добавили? В С++17, почти через 20 лет после C++98. До этого штука, которую делает std::launder, использовалась и формально считалась нелегальной. Но использовалась.

Так же в C++20 (или в C++23) добавили изменения, связанные с началом времени жизни объекта. Грубо говоря, до C++20 вот это не было легально:

alignas(my_data) char buffer[sizeof(my_data)];
load_data(buffer);
my_data * ptr = reinterpret_cast<my_data*>(buffer);
ptr->...

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

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

Следовательно он должен быть переписан.

формально да. по факту незачем.

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

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

Непонятно какое именно использование.

Любое.

Мне непонятно, почему сравнение с NULL должно считаться UB.

Потому что авторы стандарта так придумали. Я уже выше писал, что C – бред поехавших инопланетных шизофреников. На самом деле, сам стандарт написан хер знает чем, но точно не руками, а, как минимум, членом. И это одна из причин, почему C – дерьмовый язык. Его можно сделать лучше, но для этого надо всех членов комитета повесить за яйца, а стандарт переписать с нуля, выкинув из него все упоминания UB, strict aliasing и прочих неочевидных способов прострелить себе жопу.

Но этого никто никогда не сделает. Те, кого эти проблемы волновали, перешли на другие средства разработки: Rust, Nim, Zig, даже Дрю Дюваль свой быдлоязычок HaRe запилил. В итоге, на C остаётся либо сраное легаси типа ведра, с которого просто так не слезть, либо поделки кучки долбанутых.

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

или trap representation.

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

Все это хорошо, но это выглядит как какое-то теоритизирование, т.к. тогда вот это вообще никак не поправить:

void consume_then_cleanup(my_data * p) // Передача по значению.
{
  ...
  free(p);
}

void consume(some_another_data * p)
{
  if(p->my_data_ptr) {
    ...
    consume_then_cleanup(p->my_data_ptr); // Передается копия указателя.
  }
  ...
  if(!p->my_data_ptr) { // (1)
    ...
  }
}

В точке (1) мы имеем старое значение, которое никак не может быть исправлено. И если мы это старое значение не можем сравнить хотя бы с NULL, то…

Херня какая-то, как по мне.

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

И если мы это старое значение не можем сравнить хотя бы с NULL, то…

Херня какая-то, как по мне.

Ты, кажется, начинаешь познавать истину. И это не единственная такая штука в стандарте языка! А авторы компилятора это радостно реализуют, потому что пошёл нахер вот почему.

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

Потому что авторы стандарта так придумали.

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

Я уже выше писал, что C – бред поехавших инопланетных шизофреников.

Жизнь вообще несовершенна. А от людей, создавших С, пользы для разработчиков ПО в разы больше, чем от всех засветившихся в данной теме вместе взятых.

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

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

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

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

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

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

The behavior is undefined in the following circumstances:
<...>
- The value of a pointer that refers to space deallocated by a call to the free or realloc function is used (7.20.3).
<...>

Как это можно иначе прочитать? Поясни, пожалуйста. Я вот вообще не представляю.

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

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

Какое отношение возня с файлами и сетевыми соединениями имеет к бизнес процессам? Никакого! Вывод: Haskell и Java – низкоуровневые языки. И это не учитывая того, что в них тоже ВНЕЗАПНО есть указатели и иногда надо руками память выделять и потом чистить.

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

Как это можно иначе прочитать?

Так, что все это имеет значение только во время разыменования указателя. Слово refers на это указывает.

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

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

Так, что все это имеет значение только во время разыменования указателя. Слово refers на это указывает.

Нет, не указывает. Это вот реально попытка натянуть сову на глобус.

Буквально это значит, что если ты вызвал free() и освободил память, то все указатели, указывающие на эту память (refers), сразу становятся невалидными. А не только тот один, который ты в free() скормил.

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

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

Невалидный указатель и значение невалидного указателя – это разные вещи.

Использовать невалидный указатель по прямому назначению нельзя. Что очевидно.

Почему нельзя проводить манипуляции со значением невалидного указателя мне непонятно. Разве что у кого-то есть сильно желание в очередной раз обосрать Си.

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

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

Почему нельзя проводить манипуляции со значением невалидного указателя мне непонятно.

Потому что в стандарте так написано. И потому что разработчики компилятора руководствуются этим текстом, а не твоим пониманием.

Разве что у кого-то есть сильно желание в очередной раз обосрать Си.

Си обсирает сам себя. Смотри, мне даже делать ничего не надо, я просто кидаю выдержки из стандарта, и у сишников уже жопы полыхают. Тут твой коллега firkax выше уже и про неправильные компиляторы рассказывал, и про всё остальное. Разве это не офигительно?

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

Какое отношение возня с файлами и сетевыми соединениями имеет к бизнес процессам? Никакого! Вывод: Haskell и Java – низкоуровневые языки. И это не учитывая того, что в них тоже ВНЕЗАПНО есть указатели и иногда надо руками память выделять и потом чистить.

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

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

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

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

если ты дашь себе труд сходить и почитать тонны вопросов, ответов, пояснений и словоблудий на эту тему, то убедишься в том, что все так и есть - читать значение указателя это UB. (хотя и так известно, какое там значение). до освобождения обращаться к значению указателя можно, а после освобождения - нет. хотя кастанув на unsigned char* можно прочесть, но это, пожалуй, все.

стыдливо потупив взоры комитетчики и к ним причастные сообщают нам что «все известные нам компиляторы разрешают чтение значения указателя после освобождения, но в теории…».

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

разработчики gcc отморозились - «требование стандарта». clang сказал «нахер надо, у нас таких архитектур нет и не предвидится, идите в жопу. для стандарт-наци есть опция -Wparanoid»

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

clang сказал «нахер надо, у нас таких архитектур нет и не предвидится, идите в жопу. для стандарт-наци есть опция -Wparanoid»

Вообще, -Wpedantic. И очень много кода с этим флагом собирается. А -Wparanoid опции нет лол.

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

Ты каргокультируешь сейчас. «Низкоуровневым» языком лет тридцать назад называли то, что близко к машинным архитектурам. Сейчас таких актуальных языков просто не существует (кроме asm, разумеется).

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

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

Могу пошутить, но как бы и не оказалось печальной правдой: в стандартизаторы прорвались такие как вы, которые не то чтоб сознательно его губят (но и это вполне может быть), но уж точно не понимают C и вокруг его применения в области программирования, то есть задач и роли. Вот и имеем на выходе — вместо того, чтобы выработать в стандарте поведение языка во всех ортогональных ситуациях, давайте объявим максимально количество UB. Ах, теперь всё стало криво и работоспособность под вопросом? Ну мы же говорили, что C-- говно...

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

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

Это какие? Я наоборот не хочу никакого UB и считаю, что если бы в C его вообще не было, язык был бы только лучше.

Ну мы же говорили, что C– говно…

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

И поэтому вместо того, чтобы воевать с пришибленными на всю голову комитетчиками и разработчиками компиляторов, проще просто на Rust писать. Серьёзно, я в основном поэтому на него и перешёл с сишечки. Это оказалось проще, чем заучивать весь список Undefined Behaviour из стандарта и пытаться не отстрелить себе случайно жопу.

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

Ты каргокультируешь сейчас. «Низкоуровневым» языком лет тридцать назад называли то, что близко к машинным архитектурам. Сейчас таких актуальных языков просто не существует (кроме asm, разумеется).

Ну разумеется нет. Ассемблер не является языком программирования. Это великое заблуждение.

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

Ну разумеется нет. Ассемблер не является языком программирования. Это великое заблуждение.

Если что-то крякает как язык программирования – это язык программирования. Сорри, не засчитывается.

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

Если что-то крякает как язык программирования – это язык программирования. Сорри, не засчитывается.

0xff – не является языком программирования же. Вот и не засчитывается.

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

Это какие? Я наоборот не хочу никакого UB и считаю, что если бы в C его вообще не было

Вот такие как вы. Не было никогда такого и вот опять — скопом, либо бездумно, либо со злым умылом, свалив в кучу static/auto/etc и storage раз и в описательном месте стандарта всё перевернули с ног на голову, сделав UB которого там отродясь не могло возникнуть — портить значение через его копию как аргумента библиотечной функции. Не удивлюсь, что оригинальные авторы просто забыли добавить сноску, что утверждение о о невалидности указателя — это слишком сильно, в конце концов даже на static совсем всё по другому, чем на auto.

vodz ★★★★★
()