Насколько безопасны файлы с отображением памяти для чтения входных файлов?

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

Однако эта практика также кажется принципиально небезопасной, если вы не можете гарантировать, что никакой другой процесс не записывает в сопоставленный файл, потому что даже данные в частных сопоставлениях только для чтения могут измениться, если базовый файл записывается другим процессом. (POSIX, например, не указывает, «являются ли модификации базового объекта, выполненные после сопоставления MAP_PRIVATE установленные, видны через отображение MAP_PRIVATE ".)

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

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


person Stephan Tolksdorf    schedule 22.01.2014    source источник
comment
Другой процесс может изменить файл, пока вы его используете. Отображение памяти - один из множества способов использования файла - он не более опасен, чем любой другой.   -  person Casey    schedule 22.01.2014
comment
Копирование содержимого файла с помощью обычных API чтения файла в буфер памяти всегда безопасно, даже если файл одновременно изменяется. Напротив, если вы обращаетесь к файлу с отображением в память через обычные указатели, а внешний процесс модифицирует его одновременно, вы получаете неопределенное поведение, потому что это выходит за рамки ограничений модели памяти C (++). Как правило, если память, которую вы или ваш компилятор ожидаете, будет постоянно меняться у вас под ногами, произойдут плохие вещи. Ввод, который вы подтвердили в один момент, может стать недействительным в следующий момент.   -  person Stephan Tolksdorf    schedule 22.01.2014
comment
Вы неправильно понимаете цель MAP_PRIVATE. Это не означает, что предоставьте мне личную копию, это означает, что сделанные мной изменения являются личными для меня. Он имеет те же проблемы с параллелизмом, что и любой другой метод доступа к файлу.   -  person Petesh    schedule 22.01.2014
comment
@Petesh Где я написал или имел в виду, что MAP_PRIVATE означает дать мне частную копию? На самом деле я процитировал ту часть спецификации, в которой говорится об обратном. Хотя было бы неплохо, если бы существовала опция, которая действительно гарантирует, что сопоставленные страницы не будут изменены другими процессами после обращения к ним.   -  person Stephan Tolksdorf    schedule 22.01.2014
comment
@StephanTolksdorf вроде бы суть вашего вопроса. А «возможность гарантировать, что сопоставленные страницы не будут изменены другими процессами после того, как они были доступны» достигается путем записи на страницу. mmap - это не что иное, как удобство чтения / записи   -  person Petesh    schedule 22.01.2014
comment
В Windows вы обычно открываете файл монопольно или с помощью FILE_SHARE_READ, чтобы другие процессы не могли изменять файл, пока вы его используете.   -  person Harry Johnston    schedule 23.01.2014


Ответы (2)


На самом деле это не проблема.

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

Если вы используете обычный ввод-вывод (например, read), кто-то может изменить файл, пока вы его читаете. Другими словами, копирование содержимого файла в буфер памяти не всегда безопасно при наличии модификаций. Это "безопасно", поскольку read не приведет к сбою, но не гарантирует, что ваши данные непротиворечивы.
Если вы не используете readv, у вас нет никаких гарантий относительно атомарности (и даже с readv у вас нет гарантии, что то, что у вас есть в памяти, согласуется с тем, что находится на диске, или что оно не меняется между двумя вызовами readv). Кто-то может изменить файл между двумя read операциями или даже когда вы находитесь в середине этого процесса.
Это не просто то, что формально не гарантируется, но "вероятно, все еще работает" - - наоборот, например под Linux запись очевидно не атомарна. Даже не случайно.

Хорошие новости:
Обычно процессы не просто открывают произвольный случайный файл и начинают писать в него. Когда такое случается, обычно это либо хорошо известный файл, который принадлежит процессу (например, файл журнала), либо файл, в который вы явно указали процессу запись (например, сохранение в текстовом редакторе), либо процесс создает новый файл (например, компилятор создает объектный файл), или процесс просто добавляет в существующий файл (например, журналы БД и, конечно же, файлы журналов). Или процесс может атомарно заменить файл другим (или отсоединить его).

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

Если вам действительно не нравится возможность того, что другой процесс может записать в ваш файл, пока он у вас отображается, вы можете просто опустить FILE_SHARE_WRITE в Windows при создании дескриптора файла. POSIX несколько усложняет задачу, поскольку вам нужно fcntl дескриптор для обязательной блокировки, которая необязательно поддерживается или на 100% надежна в каждой системе (например, в Linux).

person Damon    schedule 22.01.2014
comment
Меня не беспокоит атомарность чтения или записи. Когда вы копируете содержимое файла в буфер памяти с помощью read, вы можете впоследствии проверить прочитанный ввод, а затем убедиться, что содержимое в буфере остается действительным. Когда входной файл отображается в память, он обычно впоследствии обрабатывается как постоянный буфер, доступ к нему и его анализ осуществляется напрямую через обычные указатели. Если эта предположительно постоянная память изменяется внешним процессом, это нарушает ожидания компилятора и разработчика, что может привести к неопределенному поведению. - person Stephan Tolksdorf; 22.01.2014
comment
Правильно, если это действительно проблема, вы все равно можете сделать memcpy анонимное отображение (или блок, выделенный кучей). Конечно, тогда вы теряете одно из главных преимуществ отображения памяти: данные больше не доступны волшебным образом и бесплатно. Хотя я бы не стал копировать. Дело в том, что обычно вы знаете, что когда вы изменяете файл (или программу от вашего имени), это не происходит просто случайным образом со случайными файлами. - person Damon; 22.01.2014
comment
@Damon Это исходный файл. Люди действительно открывают их в неожиданные моменты, и люди отвечают на них, когда вы меньше всего этого ожидаете. - person James Kanze; 22.01.2014
comment
@JamesKanze: Откуда вы знаете, что вопрос в исходном файле? Я что-то пропустил? - person Damon; 22.01.2014
comment
@Damon Он говорил о разборе. Итак, это источник его программы. (Но это правда, что можно анализировать многие вещи ... Я разбирал файлы журналов раньше.) - person James Kanze; 22.01.2014

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

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

И наконец: система не собирается произвольно открывать и изменять файл. Это исходный файл; этим займется какой-нибудь идиот-программист, и он заслуживает того, что получает. Ни в коем случае ваше неопределенное поведение не повредит систему или файлы других пользователей.

Также обратите внимание, что большинство редакторов работают с частной копией; при обратной записи они переименовывают оригинал и создают новый файл. В Unix, как только вы открыли файл в mmap, учитывается только номер inode. И когда редактор переименовывает или удаляет файл, вы все равно сохраняете свою копию. Измененный файл получит новый индексный дескриптор. Единственное, о чем вам нужно беспокоиться, это если кто-то откроет файл для обновления, а затем внесет в него изменения. Не многие программы делают это с текстовыми файлами, за исключением добавления дополнительных данных в конец.

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

person James Kanze    schedule 22.01.2014
comment
Я не думаю, что OP говорит о чтении исходных файлов как таковых, это просто любой файл, который программа может прочитать во время выполнения. Таким образом, ваша точка зрения об ошибках компилятора кажется неуместной. - person Marc Claesen; 22.01.2014
comment
@MarcClaesen Он упомянул синтаксический анализ. Ошибки компилятора могут быть немного серьезными, но каждый раз, когда вы анализируете текст, вам нужно иметь возможность каким-либо образом обрабатывать ошибки во входных данных. - person James Kanze; 22.01.2014