[devel] Взгляд на флуктуации getrandom()

Vitaly Lipatov lav на altlinux.ru
Вт Фев 11 00:15:24 MSK 2020


Три года назад (февраль 2017) в glibc 2.25 появилась 
(https://sourceware.org/glibc/wiki/Release/2.25) реализация функций 
getrandom и getentropy.

Расскажу, как продолжение этой истории выглядит со стороны, сейчас, по 
прошествии времени.

Во-первых, эта реализация появлялась в glibc слишком долго. До этого 
момента в glibc была только игрушечная random(), которую можно было 
только для учебных программ использовать.

И вот после того, как в 2014 году в ядре 3.17 появилась getrandom 
(https://lwn.net/Articles/606141/), такую функцию, а ещё и getentropy 
(для совместимости с BSD) решили добавить и в glibc. Причём только для 
ядра Linux, просто обёртку вокруг ядерного вызова. Вроде как для 
потребностей проекта LibreSSL.

Желая избежать проблем с дескрипторами на открытый /dev/urandom, в glibc 
решили не реализовывать fallback на случай отсутствия getrandom в ядре 
(https://lwn.net/Articles/711013/), хотя первоначально такое предложение 
и было (https://www.sourceware.org/bugzilla/show_bug.cgi?id=17252). До 
этого я видел в glibc карусель обходных путей для fcntl-вызовов F_GETLK 
и F_SETLK, поэтому такая прямота обёртки даже удивляет.

После сборки 05.04.2017 glibc версии 2.25 в Сизиф стали появляться 
ошибки с python (достаточно неожиданные и с трудом понятые как 
использование glibc 2.25 на старом ядре (openvz 2.6.32):
https://bugzilla.altlinux.org/show_bug.cgi?id=33356
https://bugzilla.altlinux.org/show_bug.cgi?id=33355

И сегодня ещё gnucash на ядре openvz 2.6.32 неожиданно падает, 
положившись на boost:
https://bugzilla.altlinux.org/show_bug.cgi?id=38068

А boost по-простому вызывает getentropy, если glibc >= 2.25
#elif BOOST_LIB_C_GNU >= BOOST_VERSION_NUMBER(2, 25, 0) && 
!defined(BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX)
# define BOOST_UUID_RANDOM_PROVIDER_GETENTROPY
# define BOOST_UUID_RANDOM_PROVIDER_NAME getentropy

Хотя в странном решении от glibc задумано, что при возврате ENOSYS нужно 
самому искать обходные пути. Что в ряде проектов и делают:
https://github.com/getdnsapi/getdns/blob/develop/src/compat/getentropy_linux.c

https://github.com/libressl-portable/openbsd/blob/master/src/lib/libcrypto/arc4random/getentropy_linux.c

Путаница с getrandom, который пляшет между ядром Linux и glibc, вполне 
докатилась до boost, где долго обсуждали проблему:
https://github.com/boostorg/uuid/issues/92
выключали использование getentropy на Linux:
https://github.com/boostorg/uuid/pull/97

в итоге всё равно boost (проверялось на 1.72) пытается использовать 
функцию из glibc и не имеет fallback.
Проблема была актуальной и плавающей на RHEL 7.2 с его ядро 3.10 
соответствующих сборок.

Также пострадало и Qt5, работа которых на ядрах до 3.17 жёстко запрещена 
указанием соответствующего свойства бинарника
https://bugzilla.altlinux.org/show_bug.cgi?id=35297
https://github.com/flathub/flathub/issues/805

Мне ощущается, что появление системного вызова getrandom (которому к 
тому же когда-то не повезло быть портированным в стабильные ядра), а 
впоследствии и функции getrandom в glibc — это хороший пример, насколько 
осмотрительным нужно быть при добавлении новых вызовов, а особенно — 
насколько информативными должна быть информация об их появлениях.

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

-- 
С уважением,
Виталий Липатов,
ALT Linux Team


Подробная информация о списке рассылки Devel