Состояние гонки в надежных мьютексах glibc/NPTL/Linux?
В комментарии по вопросу Автоматически выпускать мьютексы при сбоях в Unix еще в 2010 году, заявила Джилл:
glibc надежные мьютексы настолько быстры, потому что glibc принимает опасные ярлыки. Нет гарантии, что мьютекс все еще существует, когда ядро отмечает его как "вызывает EOWNERDEAD". Если мьютекс был уничтожен, а память заменена файлом с отображением памяти, в котором содержится последний идентификатор потока прав в нужном месте, а последний собственный поток заканчивается сразу после записи слова блокировки (но до полного удаления мьютекса из его списка принадлежащие мьютексам), файл поврежден. Устойчивые мьютексы Solaris и will-be-FreeBSD9 медленнее, потому что они не хотят этого риска.
Я не могу понять смысл претензии, так как уничтожение мьютекса не является законным, если оно не разблокировано (и, следовательно, не в каком-либо потоковом списке). Я также не могу найти ссылки на такие ошибки/проблемы. Была ли претензия просто ошибочной?
Причина, по которой я спрашиваю и что меня интересует, заключается в том, что это имеет отношение к правильности моей собственной реализации, основанной на одном и том же примитиве Linux-надежного мьютекса.
Ответы
Ответ 1
Описание гонки от разработчика FreeBSD pthread David Xu: http://lists.freebsd.org/pipermail/svn-src-user/2010-November/003668.html
Я не думаю, что цикл munmap/mmap строго необходим для гонки. Часть разделяемой памяти может быть использована и для другого использования. Это редко, но действительно.
Как упоминалось в этом сообщении, более "забавное" происходит, если потоки с различной привилегией имеют доступ к общему надежному мьютексу. Поскольку node для списка принадлежащих надежным мьютексам находится в самом мьютексе, поток с низкой привилегией может испортить список потоков высоких прав. Это может быть легко использовано для предотвращения сбоя потока высоких прав и в редких случаях это может привести к повреждению памяти потоков с высокими правами. По-видимому, надежные мьютексы Linux предназначены только для использования потоками с теми же привилегиями. Этого можно было бы избежать легко, создав массив надежных списков массива полностью в памяти потоков вместо связанного списка.
Ответ 2
Думаю, я нашел гонку, и это действительно очень уродливо. Это происходит следующим образом:
В потоке A был сохранен надежный мьютекс и разблокирован. Основная процедура:
- Поместите его в "ожидающий" слот заголовка заголовка потока.
- Удалите его из связанного списка надежных мьютексов, удерживаемых текущим потоком.
- Разблокируйте мьютексы.
- Очистите "ожидающий" слот заголовка заголовка потока.
Проблема заключается в том, что между шагами 3 и 4 другой поток в том же самом процессе мог получить мьютекс, затем разблокировать его и (по правде), считая себя конечным пользователем мьютекса, уничтожить и освободить /munmap его. После этого, если какой-либо поток процесса создает совместное сопоставление файла, устройства или общей памяти, и, как выясняется, ему присваивается один и тот же адрес, а значение в этом месте совпадает с pid потока, который все еще находится между шагами 3 и 4 разблокировки, у вас есть ситуация, при которой, если процесс будет убит, ядро испортит отображаемый файл, установив высокий бит 32-разрядного целого, который, по его мнению, является идентификатором владельца мьютекса.
Решение состоит в том, чтобы удерживать глобальную блокировку на mmap/munmap между шагами 2 и 4 выше, точно так же, как в моем решении проблемы с барьером, описанной в моем ответе на этот вопрос:
Может ли реализованный корректный отказоустойчивый процесс-общий барьер в Linux?