Подтвердить что ты не робот

Является ли malloc детерминированным?

Является malloc детерминированным? Скажем, если у меня есть разветвленный процесс, то есть реплика другого процесса, и в какой-то момент они оба называют функцию malloc. Будет ли выделенный адрес одинаковым в обоих процессах? Предполагая, что другие части исполнения также детерминированы.

Примечание. Здесь я говорю только о виртуальной памяти, а не о физической.

4b9b3361

Ответ 1

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

Эта случайность может быть полезна для того, чтобы сделать труднее писать. Чтобы успешно использовать переполнение буфера, вам обычно нужно сделать две вещи:

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

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

Соответствующая цитата из стандарта §7.20.3.3/2:

Функция malloc выделяет пространство для объекта, размер которого равен заданный по размеру и значение которого неопределенно

Если бы это было намерение сделать его детерминированным, то это было бы четко указано как таковое.

Даже если он выглядит детерминированным сегодня, я бы не стал делать ставку на то, чтобы он оставался таким же с новым ядром или более новой версией libc/GCC.

Ответ 2

Спецификация C99 (по крайней мере, в своем окончательном публичном проекте) указывает в "J.1 Unspecified behavior":

Следующие неуказаны:... Порядок и смежность хранилища, выделенные последовательными вызовами функции calloc, malloc и realloc (7.20.3).

Таким образом, казалось бы, malloc не должен быть детерминированным. Поэтому небезопасно предполагать, что это так.

Ответ 3

Это полностью зависит от реализации malloc. Не существует неотъемлемой причины, по которой конкретная реализация malloc должна вводить недетерминированность (за исключением, возможно, теста на фьюзинг приложения, но даже тогда он должен быть отключен по умолчанию). Например, Doug Lea malloc не использует rand(3) или любые аналогичные методы в нем.

Но, поскольку malloc вызывает вызовы ядра, такие как sbrk(2) или mmap(2) в Linux или VirtualAlloc в Windows, эти системные вызовы не всегда могут быть детерминированными, даже в в противном случае идентичные процессы. Ядро может решить намеренно предоставить различные адреса mmap 'ed в разных процессах по любой причине.

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

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

Ответ 4

Это зависит от подробных реализаций malloc. Типичная реализация malloc (например, dlmalloc) была детерминированной. Это просто потому, что сам алгоритм детерминирован.

Однако из-за многих атак безопасности, таких как атаки переполнения кучи, malloc, то есть диспетчер кучи, ввел некоторые случайности в свои реализации. (Но его энтропия относительно невелика, потому что менеджеры кучи должны учитывать скорость и пространство). Таким образом, безопасно, что вы не должны принимать строгий детерминизм в менеджерах кучи.

Кроме того, когда вы запускаете процесс, существуют различные источники случайности, включая ASLR.

Ответ 5

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

Только, например, спецификация Single Unix говорит: "[...], чтобы избежать ошибок, дочерний процесс может выполнять только операции с асинхронным сигналом до тех пор, пока не будет вызвана одна из функций exec."

К лучшему или худшему, malloc не находится в списке функций с поддержкой асинхронного сигнала.

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

Вывод: вы не можете рассчитывать на malloc, производя идентичные результаты в родительском и дочернем. Если программа многопоточная, вы не можете рассчитывать на malloc, работая вообще в дочернем элементе, пока она не назовете exec - и есть место для разумного вопроса, действительно ли это гарантировано работать даже в однопоточном дочернем до того, как ребенок называет exec.

Литература:

Ответ 6

Вы не получите тот же физический адрес. Если у вас есть процесс A и B, каждый вызов malloc возвращает адрес свободного блока. Порядок, в котором A и B называет malloc, не предсказуем. Но это никогда не происходит "в тот же момент".

Ответ 7

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

Linux использует для копирования fork-copy-on-write, поэтому разветвленные дети делят свою родительскую память, пока что-то не изменится ни в одном из процессов. В этот момент ядро ​​проходит через последовательность копирования памяти, чтобы дать раздвоенному ребенку свою собственную/уникальную копию своего пространства памяти.