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

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

всякое действие имеет свою цену.

Ну расскажи мне цену зануления указателя после free(). Её нет.

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

Вообще ни разу. Сишечка построена вокруг системы команд PDP-11. Всё остальное – домыслы.

что необходимо - решает не язык, а разработчик.

Прости, но тебя обманули. В стандарте прописано очень много требований. Только большинство программистов на C его не читали.

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

Всё хорошо будет. А что?

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

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

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

Вот так вы и колитесь. Если «но и значение самого указателя ... представляет собой жуткое undefined behaviour», то «а значит единственное что можно сделать – занулить его» — полное отсутствие логики и какого-либо понимания.

vodz ★★★★★
()

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

В стандарте ничего такого нет. Функция free() не может изменить значение указателя, так как он передается в нее по значению.

а значит единственное что можно сделать – занулить его

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

Так вот, почему даже таких банальных вещей нет?

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

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

Просто навскидку.

Вот в такой ситуации это будет создавать иллюзию ложной защищенности:

struct my_data {
  ...
  another_data * data;
  ...
};

void use_then_free_if_necessary(another_data * ptr) {
  ...;
  if(!is_data_still_needed(...)) SAFE_FREE(ptr); // (0)
  ...
}

my_data d;
d.data = allocate_another_data(...);
...
use_then_free_is_necessary(d.data); // (1)

После точки (1) в d.data будет старое значение не смотря на использование SAFE_FREE в точке (0).

eao197 ★★★★★
()

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

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

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

У меня в lua скрипты падают потому что в данных nil попался, что lua виноватая? Ой ли? Да любой код можно уронить.

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

Да, я всё понимаю и часто было бы лучше если бы (десятки) моментов были бы иными, но они не иные. Почему у танка нет мягких колёс, ведь на современных дорогах он портит трассы. Ну так не ездий по трассам на танке, ехай по грунтовке. Или не ехай на таке, а ехай на бибике. =)))))))))))) Тупая аналогия, ну и пофиг

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от annulen

В стандарте ничего такого нет. Функция free() не может изменить значение указателя, так как он передается в нее по значению.

А если найду? Стандарт C, дополнение J.2:

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).
<...>

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

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

Зато обосраться с указателями на ровном месте – это очень осмысленно. Если ты присваиваешь значение сразу же в пределах функции, то компилятор выкинет зануление, потому что SSA. Если же где-то ещё, то лучше перестраховаться, а то код меняется и можно use-after-free словить, прямо как перцы из Xorg.

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

В стандарте ничего такого нет. Функция free() не может изменить значение указателя

Да причём тут стандартны на free() вообще? Изменить передаваемое значение не может ни какая функция, это стандарт языка - в функцию передаётся копия значения, в том числе и указатель на что-то, но тоже копией. Во времена рождения C не было mmap, потому после free() память по указателю не менялась, потому хоть и считалось ужасным стилем работать с памятью после вызова free(), но если можно, то и пользовались, ибо делать бесконечные goto на всякие разные free(); перед return для внутренней временной памяти функции выходило иногда ещё отвратительнее.

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

У тебя указатель копируется что ли?

Да, передача указателя по значению. Соответственно, SAFE_FREE занулит локальную копию.

Ну тут ничем не поможешь уже, остаётся только страдать.

Если ввод SAFE_FREE не избавляет от страданий, то нафиг нужно?

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

Если ввод SAFE_FREE не избавляет от страданий, то нафиг нужно?

От части страданий таки избавит. Но да, какого-нибудь shared_ptr явно не хватает.

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

Да причём тут стандартны на free() вообще?

При том, что стандартом пользуются разработчики компиляторов при реализации своих поделок. А так не причём, да.

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

От части страданий таки избавит.

Зато добавит геморроя в плане объяснений почему SAFE_FREE не всегда помогает.

В C++ вон ввели ради благих намерений uniform initialization syntax. Теперь хрен поймешь лучше стало или совсем наоборот :(

Но да, какого-нибудь shared_ptr явно не хватает.

Скорее unique_ptr с педантичным контролем за перемещением как в Rust.

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

скажи мне как читатель читателю - ты читать умеешь?

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).

здесь тебе популярным английским разъясняют, что «Поведение не определено … (если) используется значение указателя, ссылающегося на пространство, освобожденное вызовом функции free или realloc.»

а никакое не «значение указателя после вызова free() является неопределённым согласно стандарту.»

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

а никакое не «значение указателя после вызова free() является неопределённым согласно стандарту.»

А в чём разница? Если ты не можешь прочитать значение и использовать его, то это оно и есть. Ты бы меня правильно цитировал хотя бы, потому что дальше я написал «и работа с ним представляет собой жуткое undefined behaviour».

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

А в чём разница?

«значение является неопределенным» - это значит, что мы не можем сказать, что там за значение (оно хз какое). а мы можем.

Если ты не можешь прочитать значение

можешь. читай на здоровье.

и использовать его

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

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

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

В стандарте ничего такого нет.

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime

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

«Раз в огороде нет бузины, значит дядька не в Киеве»

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

читать учимся )

Я рад, что ты читать учишься. Но лучше научись сначала до конца, а потом пиши на форум.

у тебя принтф диск отформатирует, если ты произвольно взятый с потолка адрес в хекс напечатаешь? ) а если в кои-8? ))

Undefined behaviour – это такой очень интересный термин в C. Если вкратце, компилятор, когда делает разные оптимизации, исходит из того, что у тебя в программе его нет. Если в программе есть UB, значит твоя программа некорректна, и все предположения компилятора идут лесом. Так что случиться могут совершенно разные лулзы. Но ты же умнее разработчиков компилятора, правда?

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

Изменить передаваемое значение не может ни какая функция

Например, если бы у free прототип был void free(void **ptr), то вызов free(&p) мог бы и освободить память, на которую указывает p, и изменить значение p в вызывающей функции.

Во времена рождения C не было mmap, потому после free() память по указателю не менялась, потому хоть и считалось ужасным стилем работать с памятью после вызова free(), но если можно, то и пользовались, ибо делать бесконечные goto на всякие разные free(); перед return для внутренней временной памяти функции выходило иногда ещё отвратительнее.

Причем тут mmap, когда изменить значение «освобожденной» памяти может любой вызов malloc? goto на free это типичный паттерн, оправдывать use after free тем, что лень писать goto - это вообще какая-то дичь.

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

можешь. читай на здоровье.

Ну, если бы вы это писали 35+ лет назад, это было правдой и реально этим пользовались. Теперь — нельзя, free может вызвать munmap и того куска может больше не быть у программы. Были и раньше некоторые реализации free() с sbreak(-память), где можно было вернуть память системе, но они были у специальной библиотеки malloc.

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

))

да не значение, а адрес. вот есть у тебя адрес, скажем 0xFFFFFF. расскажи мне, что тебе мешает его притнф-ом распечатать.

еще раз - не значение, которое лежит по этому адресу, а сам адрес. компрендо?

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

Вопрос в том, откуда его взять. Если ты берёшь его из переменной, работа с которой является UB, то собственно вот это вот и мешает. Если тебе так хочется печатать циферки, печатай их до вызова free().

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

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

Стандарт не предполагает обязательное использование компилятором SSA-представления и произведение им каких-либо оптимизаций. Во времена, когда C был актуален, оптимизирующие компиляторы вообще были редким явлением.

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

Конечно не предполагает. Но стандарты выходят по сей день. Есть ли сейчас компилятор C без SSA? Я не думаю.

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

В Firefox лет десять назад психанули и решили во free() затирать память. Насколько я помню, влияние на показатели производительности было незначительно.

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

расскажи мне, что тебе мешает его притнф-ом распечатать.

Ну тебе уже написали выше:

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). <…>

The value of a pointer. Инглиш, мазафака, до ю спик ит?

cumvillain
()
Ответ на: комментарий от hateyoufeel
#include <stdio.h>
#include <stdlib.h>

int main(void){

    int *j = malloc(1024);
    printf("%p\n", j);
    free(j);
    printf("%p\n", j);

    return 0;
}
0x10e22a0
0x10e22a0
Program returned: 0
olelookoe ★★★
()
Последнее исправление: olelookoe (всего исправлений: 1)
Ответ на: комментарий от olelookoe

И чо? Это может поменяться в следующей же версии компилятора. Или при другом наборе флагов. Или вообще в зависимости от погоды на Марсе.

Ты, похоже, меня своей тупостью затроллировать пытаешься :(

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

а какая именно психологическая проблема мешает тебе распечатывать адрес?

Есть стандарт, а есть реализация компилятора.

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

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

На практике, естественно, с полным соблюдением всего undefined и unspecified никто не пишет, все полагаются на здравый смысл, ибо запомнить всё ub и ub в сишке не возможно. Его настолько много и порой оно настолько неадекватное (как данный пример, в который ты до сих пор не можешь поверить), что просто не умещается в нормальной голове.

Но если есть возможность не использовать ub, лучше его не использовать, оно порой стреляет.

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

А теперь будь умничкой и собери с -Wall:

$ gcc -Wall fail.c
fail.c: In function ‘main’:
fail.c:9:5: warning: pointer ‘j’ used after ‘free’ [-Wuse-after-free]
    9 |     printf("%p\n", j);
      |     ^~~~~~~~~~~~~~~~~
fail.c:8:5: note: call to ‘free’ here
    8 |     free(j);
      |     ^~~~~~~
cumvillain
()