Не пора ли валить с gnu libc на что‑то другое?

255915ae73f640e3448f6c8716a68f38.png

Пользуюсь открытым ПО значительное время. Сижу на Линуксе.

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

Последний эпизод коснулся совсем уж системного кода — стандартной библиотеки libc от GNU. Системнее может быть только ядро.

История такая. Собрал Хромиум (не быстро). Когда наконец сборка завершилась с попутным решением проблем, думал: ну вот наконец щас запущу, посмотрю как работают интересующие меня вещи. И тут произошёл облом. Хром падал почти в самом начале запуска с ошибкой доступа к памяти. Довольно быстро удалось выяснить, что падение происходит из-за ошибки обращения по нулевому указателю. И происходит оно в динамическом загрузчике, то бишь в libdl, при загрузке библиотеки через dlopen. libdl.so является одной из компонент пакета стандартной библиотеки и понятно, самой системной библиотекой в ОС. Подробности всей ситуации я описал в вопросе на stackoverflow.

Вкратце: при загрузке библиотеки libXcursor.so подтягиваются непонятно откуда взявшиеся зависимости, не имеющие никакого отношения к упомянутой библиотеке. И зависимости эти не инициализированы корректно. Откуда и происходит обращение по нулевому указателю. Впоследствии выяснилось, что проблема начинается с несовпадающих версий библиотек libQt5Core, в результате чего libdl делает полный отбой с попыткой отката всех изменений.

Но, видимо, этот откат реализован из рук вон плохо, поскольку после него начинают происходить весьма странные вещи. И загрузка неинициализированной зависимости с нулевыми указателями лишь одна из них. Я ещё сделал пробник в виде простого приложения, которое пытается воспроизвести ситуацию. И в этом пробнике также происходил сбой, но уже при инициализации (вызов init или конструктор в их терминологии) либы libpthread.so (тоже очень системная) — потерян адрес глобального на процесс хранилища либ.

Что меня разочаровывает сильнее всего в этой истории — это то, что загрузчик находится в невалидном состоянии и продолжает в этом состоянии работать, никак его не детектируя. И понятно почему — код практически везде лишён каких-либо проверок на ошибки. Тот же самый нулевой указатель можно было бы отловить заранее, но он используется без проверки, да ещё в цикле. Ситуация усугубляется широким использованием макросов, который вроде как должны помочь избежать дублирования кода (для тех кто не в курсе — код написан конечно на Си). Но в этом, например, случае макрос используется всего для 2 вариантов кода. Проблема в том, что в таких макросах трудно сделать проверку на ошибки и самое главное — корректно вернуть её. То есть сам дизайн кода не способствует его качеству и затрудняет корректное изменение кода в случае такой надобности.

Да, я не сказал, что это не самая свежая версия glibc — 2.27. Да, в версии 2.32 проблема вроде бы не воспроизводится, потому что упомянутые участки кода были изменены — были внесены более-менее адекватные правки, которые в том числе удалили размещение огромных массивов на стеке, поменяли логику загрузки зависимостей. Но проблема в том, что эта логика, весьма странная логика, скажем честно, там была. Складывается впечатление, что те, кто вносили изменения в этот код до 2020 года, вообще плохо представляли как оно всё работает. Это в принципе подрывает доверие к всему проекту: непонятно, кто и почему вносит вносит изменения в столь критически важный системный код.

Плюс отсутствие проверок на ошибки.

Недавно я натолкнулся на анонс РедХат о несовместимых изменениях в сабже (Why glibc 2.34 removed libpthread), в котором делается ссылка на либу libmusl, как на авторитетный пример для подражания. Смотрите дескать, вот у них получилось, давайте и мы так сделаем. Так может быть, действительно мюсл уже в таком состоянии, что по качеству превосходит ГНУ либси?

Если посмотреть на использование совместимой кодовой базы в коммерческих целях, то на ум сразу приходит libbionic. Это основная системная либа в гаджетах на Linux-совместимом Андроиде. Получается, что самое массовое использование Linux в мире не полагается на glibc, а значит и не вкладывается в его развитие. glibc как бы остаётся в стороне от прогресса, и судя по всему для этого есть вполне резонные основания. И дело тут не только в проблемах с лицензией. Качество кода оставляет желать лучшего.

Так что напрашивается мысль: не пора ли переводить основные дистрибутивы на использование альтернатив (той же libmusl, например)?

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

© Habrahabr.ru