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: |

In a statement, the regulator said the search and seizure operation was carried out against seven individuals and one corporate entity at multiple locations in Ahmedabad and Bhavnagar in Gujarat, Neemuch in Madhya Pradesh, Delhi, and Mumbai. The next bit isn’t clear, but Durov reportedly claimed that his resignation, dated March 21st, was an April Fools’ prank. TechCrunch implies that it was a matter of principle, but it’s hard to be clear on the wheres, whos and whys. Similarly, on April 17th, the Moscow Times quoted Durov as saying that he quit the company after being pressured to reveal account details about Ukrainians protesting the then-president Viktor Yanukovych. On December 23rd, 2020, Pavel Durov posted to his channel that the company would need to start generating revenue. In early 2021, he added that any advertising on the platform would not use user data for targeting, and that it would be focused on “large one-to-many channels.” He pledged that ads would be “non-intrusive” and that most users would simply not notice any change. Telegram users are able to send files of any type up to 2GB each and access them from any device, with no limit on cloud storage, which has made downloading files more popular on the platform. Artem Kliuchnikov and his family fled Ukraine just days before the Russian invasion.
from vn


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