続・玩式草子 ―戯れせんとや生まれけん―

第23回 君の名は[その1]

この記事を読むのに必要な時間:およそ 5 分

名前解決(name resolution⁠⁠」というと,最近ではIPアドレスとホスト名を関連付けるDNS(Domain Name System)を連想するものの,ユーザ名やグループ名をuid/gidとマッピングしたり,/etc/hostsのレベルでIPアドレスを解決するなど,1台のホストの中でも名前解決が必要な場面は多々あります。

この機能はNSS(Name Service Switch)と呼ばれ,glibcが提供するシステムの基本機能の一部になっています。

Mozilla/Firefox方面で開発されているNetwork Security Servicesも"NSS"と略されるものの,今回は関係ありません。

最近,glibc-2.32に更新した際,このNSSがらみで面白いトラブルに遭遇し,glibcの知られざる機能(?)を発見したので,今回はその事例を紹介してみます。

発端

時期を逸して本連載では紹介していなかったものの,Plamo-7.1のリリース後約1年を経過し,その間に更新されたパッケージもたまってきたので,2020年5月にそれらを整理したPlamo-7.2をリリースしました。

Plamo-7.2の時点ではglibcは2.30を採用していたものの,半年ごとに新バージョンがリリースされるglibcのこと,ふと気づくと2.32がリリースされています。

そろそろバージョンアップしておこうか,と考えて,2.30用のビルドスクリプトのオプション指定等はそのまま流用し,2.32をパッケージ化しました。

Plamo Linuxでは,glibcのソースコードからglibc(glibc-2.32-x86_64-B1.txz)libc(libc-2.32-x86_64-B1.txz)の2つのパッケージを作ります。

glibcパッケージは共有ライブラリ集で全ての環境に必須なのに対し,libcパッケージはC/C++のコードをコンパイルする際に必要なファイルを収めているためコンパイラを使わない環境には不要です。

configureやビルドの過程を眺めていても特にエラーはありませんし,できたパッケージを確認しても必要なファイルは揃っているようです。そのため,特に気にすることもなくupdatepkgで更新しようとしたところ,libcでパッケージが壊れている旨のエラーが発生しました。

$ sudo updatepkg glibc-2.32-x86_64-B1.txz 
[sudo] kojima のパスワード:
removing glibc-2.30

Removing package glibc...
Removing files:
  --&lgt; Deleting symlink lib/ld-linux-x86-64.so.2
...
glibc-2.32-x86_64-B1 のインストールスクリプトを実行中
 
processing [ glibc ] ...
glibc package initializing
Replace /etc/ld.so.conf, and backup it to /etc/ld.so.conf.old
'/etc/ld.so.conf' -&lgt; '/etc/ld.so.conf.old'
glibc timezone configuring
glibc initialize finished

$ sudo updatepkg libc-2.32-x86_64-B1.txz
removing libc-2.30

Removing package libc...
...
--&lgt; Deleting empty directory usr/include/bits
--&lgt; Deleting empty directory usr/include/arpa
Segmentation fault
expr: syntax error
libc-2.32-x86_64-B2 のインストール中
PACKAGE DESCRIPTION:
libc-2.32-x86_64-B1.txzをインストールできませんでした。
パッケージが壊れているようです(tar のエラーコード: 139)

「あれれ,libcパッケージを作り損ねたのかな?」と思って,中身を確認しても問題はありません。

$ tar tvf libc-2.32-x86_64-B1.txz 
drwxr-xr-x root/root         0 2020-08-16 08:37:03 usr/
drwxr-xr-x root/root         0 2020-08-16 08:36:41 usr/include/
...
-rw-r--r-- root/root      1503 2020-08-16 09:00:00 install/doinst.sh
$ mkdir Work ; cd Work
$ tar xvf ../libc-2.32-x86_64-B1.txz 
usr/
usr/include/
usr/include/limits.h
....
$ ls -l usr/lib
Mcrt1.o   libBrokenLocale.so  libdl.so    libnss_db.so      librt.so
Scrt1.o   libanl.a            libg.a      libnss_dns.so     libthread_db.so
crt1.o    libanl.so           libm-2.32.a libnss_files.so   libutil.a
....

「パッケージは別に壊れてないようだけどなぁ?」と首をかしげながら,再度installpkgを試してみてもやはり同じエラーになります。

これはどうやらパッケージではなく管理ツール側の問題らしい,と気づいて,パッケージ管理用スクリプトを追いかけてみると,新しいパッケージをインストールする/sbin/installpkg2コマンドの377行目,展開前のファイルサイズを"ls -l"で調べているところで落ちているようでした。

376 
377      COMPRESSED=`ls -l $package | crunch | cut -f5 -d' '`
378      if [ "$compress" = "bzip2" ]; then
379          UNCOMPRESSED=`bzcat $package | wc -c`

SegFaultの内容をdmesgで確認すると,確かに"ls"が原因のようなものの,落ちた場所はlibc-2.32.soの中と言っています。

$ dmesg
....
[ 1291.190826] ls[3525]: segfault at e5 ip 00007f0d689a4f90
     sp 00007ffcb9978530 error 4 in libc-2.32.so[7f0d6888f000+167000]
[ 1291.191697] Code: ff 48 85 c0 0f 84 9d 00 00 00 41 80 3c 24 ff 0f
   85 b8 00 00 00 48 8b 05 56 0e 0a 00 49 0f be 16 4c 89 f5 64 48 8b
   08 48 89 d0 <f6&lgt; 44 51 01 20 74 1c 66 0f 1f 84 00 00 00 00 00 48
   0f be 55 01 48

この結果を見て,頭の中が「???」となりました。というのも,Plamo Linuxでは共有ライブラリを含むパッケージを安全に更新するために,パッケージ管理用スクリプトはstatic linkした専用のコマンドを使うようにしているからです。

これら専用のコマンドは/sbin/installer/以下に収めてあり,確認すると,"ls"もきちんとにstatic linkになっています。

$ ls /sbin/installer                                                         
[*        bzip2* cp*     dirname* gzip*  mv*       rmdir* tar*   wc*
basename* cat*   cut*    echo*    ln*    paste*    sed*   tee*   xz*
busybox*  chmod* date*   expr*    ls*    readlink* sh*    touch* xzcat*
bzcat*    comm*  dialog* grep*    mkdir* rm*       sort*  uniq*  zcat*

$ file /sbin/installer/ls
/sbin/installer/ls: ELF 64-bit LSB executable, x86-64,
version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32,
BuildID[sha1]=da04378ba94777f445f236eca25143f6f2300b32, stripped

$ ldd /sbin/installer/ls
  動的実行ファイルではありません

static linkなバイナリは実行時に共有ライブラリを必要としません。それにもかかわらず,SegFaultは共有ライブラリlibc-2.32.soの中で発生しています。どうしてそんなことが起きるのだろう,と首をひねりつつ,調べてみることにしました。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたものの,いつの間にかミイラ取りがミイラになってOSSの世界にどっぷりと漬かってしまいました。最近は田舎に隠棲して半農半自営な生活をしながらソフトウェアと戯れています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html