Telegram Group & Telegram Channel
#dev

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

Начнем с того, что есть нуль, а есть ноль. NULL не обязан иметь адрес 0 и, например, на некоторых архитектурах и интерпретаторах C это не так. Прагматичным людям из POSIX это не нравится, поэтому там NULL всегда имеет адрес 0. Впрочем, не обязательно даже уходить далеко в прошлое: amdgcn определяет NULL как -1, так что встретиться с таким сегодня вполне реально. Типичное определение NULL как (void*)0 на таких машинах все еще работает, потому что (void*)0 стандарт определяет равным NULL, но вот int x = 0; (void*)x портабельно NULL не даст.

Как вытекает из предыдущего, ничего особенного в адресе NULLа нет, и в железе никто не запрещает существовать странице по адресу 0. Железу плевать на то, какие правила накладывает стандарт C, и поэтому, например, на процессорах x86 в real mode по адресам 0256 хранятся таблицы прерываний. Разыменовывать адрес 0 в C все еще UB, но вот 1 разыменовать никто не запрещает.

Впрочем, одно дело — UB по стандарту, другое — поведение на практике. В прошлом стандарт C воспринимался скорее как гайдлайн, чем правила. Старые компиляторы не делали умных оптимизаций, и вообще Ритчи не подразумевал, что за счет UB будут оптимизировать, поэтому на многих платформах разыменование нулевого указателя только и делало, что читало значение в памяти по адресу 0. Компилятор C на HP-UX (это были еще те времена, когда свободных компиляторов C не было, и под каждую платформу были свои компиляторы, зачастую платные), например, давал опцию: мапать страницу по адресу 0, чтобы *(int*)NULL возвращало 0 (гарантированно! без современного понимания UB!), или не мапать, чтобы падало.

В современном мире писать по адресу, совпадающему с адресом NULL, опасно прям совсем. В embedded, где такое периодически приходится делать, у этой проблемы есть два решения: молоток и микроскоп. Во-первых, можно написать код для записи по адресу 0 на ассемблере, железо сожрет. Во-вторых, иногда железо игнорирует старшие биты адреса, поэтому можно записывать не по адресу 0, а, например, по адресу 0x80000000, который железо воспримет так же, а компилятор проинтерпретирует корректно.

Хочется верить, что по крайней мере на современных платформах разыменовывание NULL (если его не выкинет компилятор, конечно) приведет к сегфолту или чему-то подобному. Это не так. Во-первых, Linux поддерживает флаг personality MMAP_PAGE_ZERO, аллоцирующий страницу по адресу 0 на старте программы для совместимости с System V. Во-вторых, даже без этого вы можете с помощью mmap аллоцировать страницу по адресу 0 руками — этим даже пользовались эмуляторы.

Потом эту лавочку прикрыли, и даже не потому, что это скрывает баги в программах на C. Ну, точнее, ровно поэтому, только программой на C здесь выступает само ядро. Достаточно большое количество ядерных эксплоитов того времени заключалось в том, чтобы дата рейсом или иным методом заставить ядро разыменовать нулевой указатель. Поскольку внутри ядра (была) видна память текущего процесса, это приводило к тому, что пользовательская память начинала интерпретироваться как ядерные структуры. Чтобы этого избежать, сейчас Linux не позволяет аллоцировать страницы ниже адреса sysctl vm.mmap_min_addr — 64 кибибайта на большинстве устройств. (Нет бы писать без багов...)

На этом история с разыменованием нуля могла бы закончиться: в Windows ограничение на память на малых адресах было уже давно, в Linux ввели, других операционных систем не существует. Но хипстеры придумали WebAssembly, и поскольку с ним вопрос об изоляции внутри контейнера не встает, по адресу 0 здесь вполне есть доступная память. Некоторых это бесит, некоторых удивляет, меня — радует, ибо нефиг проталкивать ограничения уровней абстракции вниз (впрочем, с этим в WebAssembly проиграли в других местах).

Такие дела.



group-telegram.com/alisa_rummages/215
Create:
Last Update:

#dev

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

Начнем с того, что есть нуль, а есть ноль. NULL не обязан иметь адрес 0 и, например, на некоторых архитектурах и интерпретаторах C это не так. Прагматичным людям из POSIX это не нравится, поэтому там NULL всегда имеет адрес 0. Впрочем, не обязательно даже уходить далеко в прошлое: amdgcn определяет NULL как -1, так что встретиться с таким сегодня вполне реально. Типичное определение NULL как (void*)0 на таких машинах все еще работает, потому что (void*)0 стандарт определяет равным NULL, но вот int x = 0; (void*)x портабельно NULL не даст.

Как вытекает из предыдущего, ничего особенного в адресе NULLа нет, и в железе никто не запрещает существовать странице по адресу 0. Железу плевать на то, какие правила накладывает стандарт C, и поэтому, например, на процессорах x86 в real mode по адресам 0256 хранятся таблицы прерываний. Разыменовывать адрес 0 в C все еще UB, но вот 1 разыменовать никто не запрещает.

Впрочем, одно дело — UB по стандарту, другое — поведение на практике. В прошлом стандарт C воспринимался скорее как гайдлайн, чем правила. Старые компиляторы не делали умных оптимизаций, и вообще Ритчи не подразумевал, что за счет UB будут оптимизировать, поэтому на многих платформах разыменование нулевого указателя только и делало, что читало значение в памяти по адресу 0. Компилятор C на HP-UX (это были еще те времена, когда свободных компиляторов C не было, и под каждую платформу были свои компиляторы, зачастую платные), например, давал опцию: мапать страницу по адресу 0, чтобы *(int*)NULL возвращало 0 (гарантированно! без современного понимания UB!), или не мапать, чтобы падало.

В современном мире писать по адресу, совпадающему с адресом NULL, опасно прям совсем. В embedded, где такое периодически приходится делать, у этой проблемы есть два решения: молоток и микроскоп. Во-первых, можно написать код для записи по адресу 0 на ассемблере, железо сожрет. Во-вторых, иногда железо игнорирует старшие биты адреса, поэтому можно записывать не по адресу 0, а, например, по адресу 0x80000000, который железо воспримет так же, а компилятор проинтерпретирует корректно.

Хочется верить, что по крайней мере на современных платформах разыменовывание NULL (если его не выкинет компилятор, конечно) приведет к сегфолту или чему-то подобному. Это не так. Во-первых, Linux поддерживает флаг personality MMAP_PAGE_ZERO, аллоцирующий страницу по адресу 0 на старте программы для совместимости с System V. Во-вторых, даже без этого вы можете с помощью mmap аллоцировать страницу по адресу 0 руками — этим даже пользовались эмуляторы.

Потом эту лавочку прикрыли, и даже не потому, что это скрывает баги в программах на C. Ну, точнее, ровно поэтому, только программой на C здесь выступает само ядро. Достаточно большое количество ядерных эксплоитов того времени заключалось в том, чтобы дата рейсом или иным методом заставить ядро разыменовать нулевой указатель. Поскольку внутри ядра (была) видна память текущего процесса, это приводило к тому, что пользовательская память начинала интерпретироваться как ядерные структуры. Чтобы этого избежать, сейчас Linux не позволяет аллоцировать страницы ниже адреса sysctl vm.mmap_min_addr — 64 кибибайта на большинстве устройств. (Нет бы писать без багов...)

На этом история с разыменованием нуля могла бы закончиться: в Windows ограничение на память на малых адресах было уже давно, в Linux ввели, других операционных систем не существует. Но хипстеры придумали WebAssembly, и поскольку с ним вопрос об изоляции внутри контейнера не встает, по адресу 0 здесь вполне есть доступная память. Некоторых это бесит, некоторых удивляет, меня — радует, ибо нефиг проталкивать ограничения уровней абстракции вниз (впрочем, с этим в WebAssembly проиграли в других местах).

Такие дела.

BY Алиса копается


Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260

Share with your friend now:
group-telegram.com/alisa_rummages/215

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

Since January 2022, the SC has received a total of 47 complaints and enquiries on illegal investment schemes promoted through Telegram. These fraudulent schemes offer non-existent investment opportunities, promising very attractive and risk-free returns within a short span of time. They commonly offer unrealistic returns of as high as 1,000% within 24 hours or even within a few hours. The account, "War on Fakes," was created on February 24, the same day Russian President Vladimir Putin announced a "special military operation" and troops began invading Ukraine. The page is rife with disinformation, according to The Atlantic Council's Digital Forensic Research Lab, which studies digital extremism and published a report examining the channel. Meanwhile, a completely redesigned attachment menu appears when sending multiple photos or vides. Users can tap "X selected" (X being the number of items) at the top of the panel to preview how the album will look in the chat when it's sent, as well as rearrange or remove selected media. However, the perpetrators of such frauds are now adopting new methods and technologies to defraud the investors. The regulator said it has been undertaking several campaigns to educate the investors to be vigilant while taking investment decisions based on stock tips.
from ru


Telegram Алиса копается
FROM American