Можем ли мы реализовать возможность безопасного создания временных файлов в C++23?

Прочитав обзор моего собственного кода , ответ о boost::filesystem::unique_path , еще один ответ о unique_path и ссылки на него, документацию о tmpnam и ее справочную страницу Linux, и и еще несколько связанных с этим вещей. У меня сложилось впечатление, что с C++23 у нас есть все необходимое для обеспечения безопасной реализации создания временных файлов, только с использованием стандартной библиотеки C++23.

Под безопасным я, среди прочего, подразумеваю реализацию, которая не имела бы известных уязвимостей безопасности tmpnam и boost::filesystem::unique_path.

Например, мы могли бы реализовать class temp_file, конструктор которого будет генерировать 128-битное случайное имя файла и открывать файл с этим именем, расположенный под std::filesystem::temp_directory_path(), в эксклюзивном режиме (std::ios::noreplace), чей деструктор закроет файл и удалит его, и чей stream Функция-член вернет ссылку на поток ввода/вывода открытого файла.

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

К сожалению, стандарт не дает никаких гарантий относительно поведения нескольких потоков или процессов, одновременно работающих с одним и тем же объектом файловой системы. Ни одна из операций файловой системы не будет работать корректно, если вмешается другой процесс или даже другой поток: eel.is/c++draft/fs.race.behavior

user17732522 26.05.2024 20:56

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

user17732522 26.05.2024 21:02

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

user17732522 26.05.2024 21:07

@user17732522 user17732522, мы говорим о одновременном доступе к записи во временный файл, который приводит к неопределенному состоянию файла? Но чем это отличается от одновременного доступа к любому другому файлу? Почему для временных файлов это будет большей проблемой, чем для других файлов? Кроме того, какая альтернатива не имела бы этой проблемы? mkstemp, например, не облегчило бы ситуацию, верно?

nilo 27.05.2024 00:05

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

nilo 27.05.2024 00:24

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

user17732522 27.05.2024 00:46

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

user17732522 27.05.2024 00:49

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

nilo 27.05.2024 12:23

Как это могло быть основано на мнении, если я и мои респонденты ДОКАЗЫВАЛИ, что единственный возможный ответ на этот вопрос — «нет», цитируя соответствующую часть стандарта?!

nilo 28.05.2024 17:17

Возможно, людям, которые закрыли это, следует связаться с рабочей группой по библиотекам C++, которая считает, что «Проблемы безопасности, вероятно, лучше всего решить в следующем TS файловой системы, поскольку необходимо полномасштабное предложение, и, вероятно, потребуется несколько лет. развиваться». Я уверен, что они были бы рады услышать, что проблемы TOCTTOU, которые они пытаются решить, на самом деле могут не существовать, в зависимости от мнения.

nilo 28.05.2024 18:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
10
169
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

API файловой системы C++ не может создать «безопасную» версию этой операции.

Чтобы иметь полностью безопасную версию, вам необходимо иметь возможность выполнять все следующие действия как одну атомарную операцию файловой системы:

  1. Создайте имя, которое не используется в каталоге.
  2. Создайте файл с таким именем.
  3. Эксклюзивно откройте этот файл.

Стандартная библиотека может выполнять каждую из этих операций по отдельности, но уязвимость заключается в невозможности выполнить всю последовательность как единый атомарный процесс. Пока какая-либо другая программа может вмешаться в любую из этих операций, уязвимости существуют. А в C++ нет механизма, обеспечивающего такую ​​атомарность файловой системы.

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

Я не совсем слежу за этим. Если я создаю std::fstream из случайного пути к файлу с флагом std::ios::noreplace, не выполняю ли я атомарно и 2, и 3? Кроме того, файл может быть создан злоумышленником между 1 и 2, но тогда шаг 2/3 завершится неудачей, верно? Т.е. атака не осталась незамеченной?

nilo 26.05.2024 19:36

@nilo: Возможно, вы выполняете 2 и 3 атомарно. Но опять же, в зависимости от используемой ОС и файловой системы, возможно, и нет. Вот лишь один довольно известный пример: примерно до версии 2.6 или около того O_EXCL на томе NFS из Linux не был атомарным.

Jerry Coffin 26.05.2024 23:21

@JerryCoffin, в своем «только для одного достаточно известного примера» вы, похоже, имеете в виду официально задокументированное ограничение системного вызова open, а именно то, что O_EXCL не поддерживался до NFSv3 и ядра 2.6. На мой взгляд, если в такой системе вы создаете std::fstream с флагом std::ios::noreplace и построенный поток сообщает, что файл открыт, то в реализации стандартной библиотеки есть ошибка. Это не означает, что конструкция std::fstream семантически неатомарна.

nilo 26.05.2024 23:53

@nilo: Ну, я полагаю, что это в любом случае спорно - я сомневаюсь, что кто-нибудь будет пытаться портировать C++23 на эту старую версию LInux.

Jerry Coffin 27.05.2024 04:19

Не читая раздел «Поведение файловой системы» в стандарте C++, который легко пропустить, я думаю, разработчик мог бы разумно ожидать, что std::fstream::open является атомарным, как и должно быть, если бы не практические препятствия, которые этому препятствуют. . Я думаю, что это делает этот ответ запутанным, поскольку он не объясняет, почему реализация может иметь такие недостатки и при этом соответствовать стандарту. Если бы такое разъяснение было предоставлено, я мог бы принять ответ.

nilo 27.05.2024 14:13

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

nilo 28.05.2024 18:29
Ответ принят как подходящий

Ответ Никола Боласа в сочетании с комментариями пользователя 17732522 позволяет мне составить самодостаточный ответ, основанный на стандарте C++.

Можно ли такую ​​реализацию, если она правильная, рассматривать как безопасное средство для создания и использования временных файлов, или я пропустил некоторые важные соображения, и в этом случае, каковы они соображения?

Окончательный вариант C++23 содержит:

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

Такое отсутствие гарантированной безопасности на самом деле является общим для файловых потоков C++, но, как выразился пользователь17732522, «для [временных файлов] это немного хуже, поскольку они могут быть созданы в каталоге, доступном всем пользователям».

Что касается альтернатив, то в стандарте C++ есть tmpfile, который не раскрывает имя файла и, следовательно, по крайней мере теоретически может быть безопаснее за счет использования FILE API вместо std::fstream.

Существуют также альтернативы для конкретной ОС.

Я думаю, что tmpfile в большинстве случаев безопасно использовать даже на уровне спецификации, по крайней мере, в последних версиях C и C++. Указано, что оно ведет себя так, как если бы файл открывался с флагом x, что, в свою очередь, гарантированно является атомарной операцией или завершается сбоем согласно C §7.23.5.3. Вероятно, все еще может быть осуществлена ​​атака типа «отказ в обслуживании», если угадать имена, которые предпринимаются. Ни C, ни POSIX, похоже, не гарантируют, что свободное имя также будет получено атомарно. Для этого потребуется полностью реализовать операцию на уровне ОС. В ОС, которая не допускает атомарности, tmpfile всегда должен давать сбой.

user17732522 28.05.2024 20:26

@ user17732522, большое спасибо за эту информацию. Я подозревал отсутствие гарантии, отсюда и моя осторожная формулировка.

nilo 28.05.2024 20:28

Мне придется снова исправиться. Судя по всему, эта гарантия появилась в C23 и еще не включена ни в одну версию C++. Насколько я могу судить, раньше, насколько я могу судить, вообще не было никакой гарантии атомарности ни в C, ни в POSIX.

user17732522 28.05.2024 20:36

Другие вопросы по теме