LINUX.ORG.RU

Проблема флага O_DIRECT

 , , ,

Проблема флага O_DIRECT

3

6

Даже пользователь без прав администратора способен вызвать необратимую рассинхронизацию дисков.

В Linux обнаружена серьёзная уязвимость, существующая уже более десяти лет, и связана она с механизмом программного RAID при использовании флага O_DIRECT. Проблема позволяет привести массив в несогласованное состояние, причём без каких-либо ошибок или предупреждений со стороны системы. Несмотря на то, что баг впервые был зарегистрирован ещё в 2015 году, интерес к нему вновь возрос в контексте современных задач, таких как живая миграция виртуальных машин.

Суть проблемы заключается в том, как пользовательские программы взаимодействуют с блочными устройствами при помощи O_DIRECT. Этот флаг позволяет выполнять прямой доступ к данным, минуя кеш ядра, что полезно для повышения производительности в ряде задач. Однако в случае программного RAID, такого как MD RAID, DRBD или LVM RAID, это приводит к тому, что каждый диск массива может получить разные данные, даже если они записываются с одного и того же указателя в пользовательском пространстве. В результате данные на отдельных устройствах перестают быть синхронизированными – массив остаётся «рабочим» с точки зрения системы, но фактически оказывается повреждённым.

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

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

Из всех файловых систем Linux, известны только две, не подверженные этому дефекту при использовании с программным RAID – это OpenZFS и Bcachefs. Остальные решения остаются потенциально уязвимыми. Проблема до сих пор числится как открытая и не имеет официального исправления.

>>> Подробности

★★★★★

Проверено: dataman ()
Последнее исправление: hobbit (всего исправлений: 3)

so, zfs, bcachefs and btrfs seem to do right now and survive that «invalid O_DIRECT test cases»

dataman ★★★★★
()

Даже пользователь без прав администратора

Кого? CAP_SYS_ADMIN что ли? Давайте будем более точными в терминологии, всё таки не про Виндоус тема.

seiken ★★★★★
()

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

anc ★★★★★
()
Ответ на: комментарий от seiken

Любой пользователь может использовать O_DIRECT флаг.

vbr ★★★★★
()

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

quester ★★
()

linux soft raid лет 15 и куча народу его используют, просто не верю что такой кейс был не протестирован изначально при разработке

quester ★★
()

Очередная новость с картинкой ради картинки.

Evenik ★★
()

Из всех файловых систем Linux, известны только две, не подверженные этому дефекту при использовании с программным RAID – это OpenZFS и Bcachefs.

В репорте пишут, что и btrfs тоже не подвержена.

so, zfs, bcachefs and btrfs seem to do right now and survive that «invalid O_DIRECT test cases»

Похоже просто потому что вся CoW тройка запрещает писать произвольный мусор «мимо кассы» напрямую в диск из-за необходимости вычисления контрольных сумм записанных данных.

greedyskoof
()

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

gns ★★★★★
()
Ответ на: комментарий от gns

Пользователь вызвал write и передал буфер. Драйвер блочного устройства взял этот буфер и переписал с него данные прямо на диск. Это без рейда.

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

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

Когда используется файловая система с флагом O_DIRECT по сути управление передаётся сразу драйверу блочного устройства, для простых файловых систем, поэтому получается этот баг эксплуатировать без рута. Более сложные файловые системы с контрольными суммами работают сложней (но и, конечно, медленней) и в них O_DIRECT работает уже не так примитивно.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vbr

Интерестно, а в случае RAID5/6 как это работает?

И, тут вобще интерестно, что считать ошибкой RAID1, ведь, в общем случае SSD может быть с Non-deterministic TRIM. То есть те блоки RAID, которые тримнуты, могут давать разное содержимое для разных накопителей зеркала. RAID-устройство не знает, какие блоки тримнуты, то есть на определённых исправных накопителях RAID1 может быть всегда не синхронизированым...

mky ★★★★★
()
Последнее исправление: mky (всего исправлений: 1)
Ответ на: комментарий от cobold

Видимо, для виртуальных машин или их миграции — это норма.

mky ★★★★★
()
Ответ на: комментарий от quester

лет 15

Да побольше, mdctl появился году в 2001. И не факт, что на тот момент O_DIRECT был вобще, или работал так, как сейчас.

mky ★★★★★
()
Ответ на: комментарий от vbr

Я так понимаю, что пользователь, пишущий данные на raid в режиме O_DIRECT, может повредить данные в своём этом файле.

Технически это баг, согласен, но он же ссзб.

Aceler ★★★★★
()

Ах, вот, что за говно мне на ровном месте рейд развалило! Я еще жаловался тогда, что с какого-то хрена у меня на сервере без даунтаймов файлы разъехались в зеркале MD-raid. Потом решил переехать на ZFS.

Меня тогда @mx__, в приступе острой дистрофобии, обвинял в том, что это всё потому, что я использую арч, и развал был совершенно точно из-за этого (на LTS-ядре почти без патчей, ага-ага). Ну чо, дружок, как самочувствие?

liksys ★★★★
()
Последнее исправление: liksys (всего исправлений: 1)
Ответ на: комментарий от Aceler

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

greedyskoof
()
Ответ на: комментарий от greedyskoof

Можно список программ, использующих этот режим?

фундаментальная дырка в ядре.

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

Пытаюсь понять, то ли проблема недостаточно фундаментальна, то ли условия для неё слишком специфические нужны.

Aceler ★★★★★
()
Ответ на: комментарий от cobold

Так это тогда баг приложения, если оно меняет данные в буфере до окончания write

Баг приложения не должен разваливать рейд. Ну интуитивно так кажется.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от Aceler

Можно список программ, использующих этот режим?

dd oflag=direct

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

Возможно, какие-то пионерские СУБД подвержены.

legolegs ★★★★★
()
Ответ на: комментарий от Aceler

Можно список программ, использующих этот режим?

Любой рандомный скрипт вызывающий dd с oflag=direct?

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

«У меня за окном такая же девятиэтажка и она не горит.»

greedyskoof
()
Ответ на: комментарий от legolegs

Ещё в qemu для производительности часто используют этот флаг (например в proxmox это по дефолту, насколько я знаю). А что там внутри виртуалки происходит - одному богу известно.

vbr ★★★★★
()

«Пользовательские программы», как правило, не имеют прав на доступ к блочному устройству минуя ФС, даже для чтения.

alegz ★★★★★
()
Ответ на: комментарий от legolegs

взрослые субд типа постгреса

Недавно в 18 версии кстати как раз данный режим завезли, лол.

https://pganalyze.com/blog/postgres-18-async-io:

Allow the use of Direct I/0 (DIO). Direct I/O refers to bypassing the OS kernel’s page cache and performing I/O operations straight between the application and storage device.

Пам-пам-пам.

greedyskoof
()
Ответ на: комментарий от greedyskoof

Только ведь абсолютно любая программа может это сделать без твоего ведома.

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

alegz ★★★★★
()
Ответ на: комментарий от quester

linux soft raid лет 15 и куча народу его используют, просто не верю что такой кейс был не протестирован изначально при разработке

Год назад решил детально разобраться с работой mdraid, протестировать его стабильность и выносливость, понять слабые стороны и прочее.

Взял три USB-флешки, создал на них raid10, вынул-вставил:

md/raid10:md127: not enough operational mirrors.
BUG: kernel NULL pointer dereference, address: 0000000000000050

Первая опробованная мной конфигурация развалила ядро.

ValdikSS ★★★★★
()
Последнее исправление: ValdikSS (всего исправлений: 1)
Ответ на: комментарий от alegz

Этот баг эксплуатируется через обычную ФС с флагом O_DIRECT, внимательней читай.

vbr ★★★★★
()
Ответ на: комментарий от vbr

По умолчанию в проксмоксе режим компания none, это так. Но любая гостевая ос тоже кеширует, поэтому проблемы быть не должно. Иначе багтрекер проксмокса был бы завален отчётами о пропадании данных.

Aceler ★★★★★
()
Ответ на: комментарий от cobold

В случае если оно работает на одиночном диске или аппаратном рейде - это не баг, это крутая оптимизация.

kirill_rrr ★★★★★
()
Последнее исправление: kirill_rrr (всего исправлений: 1)
Ответ на: комментарий от alegz

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

greedyskoof
()
Ответ на: комментарий от ValdikSS

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

vbr ★★★★★
()
Ответ на: комментарий от greedyskoof

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

ПО идее вот это должно воспроизводить баг, но это не точно:

for i in {1..100}; do
dd if=/dev/urandom count=128 oflag=direct >&3
perl -e 'open(FD,">&3"); seek(FD,0,0);'
done 3> testfile
legolegs ★★★★★
()
Ответ на: комментарий от quester

я пользовался mdraid ещё лет 25 назад. и он уже не был новостью.
и вот доказательство из mdadm.c:

  • mdadm - manage Linux «md» devices aka RAID arrays.
  • Copyright (C) 2001-2013 Neil Brown neilb@suse.de
mumpster ★★★★★
()
Ответ на: комментарий от vbr

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

Я вам больше скажу: линуксовый raid1 не поддерживает параллельное чтение нескольких блоков с разных дисков! Т.е. скорость чтения в raid1 не увеличивается!

Я когда узнал, не поверил, что такой нонсенс вообще возможен.

ValdikSS ★★★★★
()
Ответ на: комментарий от vbr

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

greedyskoof
()
Ответ на: комментарий от legolegs

LVM использует тот же mdraid, так что не увеличивается.

Речь идёт о «разделении» чтения одного блока на несколько дисков, т.е. если блочному устройству отправили запрос «прочти мне 1 МБ по вот такому смещению», mdraid не может разделить этот запрос на два «прочти по 500 КБ» на оба диска.

Скорость обычного чтения может быть выше из-за readahead, который отправляет несколько запросов, но это не то.

ValdikSS ★★★★★
()
Последнее исправление: ValdikSS (всего исправлений: 1)
Ответ на: комментарий от greedyskoof

пример в тикете:

2. Write data with O_DIRECT

./a.out /dev/md0

хрен ты его так запишешь в /dev/vg/home какой-нибудь. прав у тебя на это нет.

alegz ★★★★★
()
Ответ на: комментарий от ValdikSS

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

legolegs ★★★★★
()

Эта проблема должна быть глубже. Хотя бы потому что я смотрел код куда ходит этот самый директ. Не может там быть таких багов - и raid тут ни причем

ckotctvo
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.