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

第24回 君の名は(NSS:Name Service Switch)[その2]

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

激動の2020年も気づけば残り3ヵ月,朝晩はめっきりと涼しくなって,ソースコードと戯れるのには絶好の季節になってきました。今回は,前回に引き続いてglibc2のNSS回りの話題です。

転回

この問題をメンテナMLに報告してあれこれ議論しているうち,メンテナの加藤さんが「lsをstraceしたら,libnss_files.so.2を開いている」旨を報告してくれました。

straceは,対象のプログラムが動作する際に使うシステムコールや開こうとするファイルを追跡するコマンドです。前回紹介したgdbがプログラムの動作を追いかけるコマンドなのに対し,straceはプログラムとOSとのやりとりを調査するコマンドと言えるでしょう。straceのことはすっかり失念していたので,さっそく手元でも試してみました。

$ strace /sbin/installer/ls -l |& cat -n
   1  execve("/sbin/installer/ls", ["/sbin/installer/ls", "-l"], 0x7ffcf614a268 /* 28 vars */) = 0
   2  uname({sysname="Linux", nodename="2020_0822", ...}) = 0
   3  brk(NULL)                               = 0x1b62000
   4  brk(0x1b63200)                          = 0x1b63200
   5  arch_prctl(ARCH_SET_FS, 0x1b628c0)      = 0
...      
  80  openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 4
  81  fstat(4, {st_mode=S_IFREG|0644, st_size=1686, ...}) = 0
  82  lseek(4, 0, SEEK_SET)                   = 0
  83  read(4, "root:x:0:0::/root:/bin/bash\nbin:"..., 4096) = 1686
  84  --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xe5} ---
  85  +++ killed by SIGSEGV +++

この結果を見ると,SIGSEGVで落ちているのはgdbで見た時と同じなものの,その直前に/etc/passwdを開き(80行目⁠⁠,そこからデータを読み取って(83行目)から,SIGSEGVが発生していることがわかります。

もう少し絞り込むために,openシステムコールを呼び出している部分だけ取り出してみます。

$ strace /sbin/installer/ls -l |& grep  open | cat -n
   ....
   9  openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
  10  open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4
  11  open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
  12  open("/lib/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 4
  13  open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 4
  14  open("/lib64/ld-linux-x86-64.so.2", O_RDONLY|O_CLOEXEC) = 4
  15  openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 4

最初の方は今回の問題とは関係ないロケール回りの処理なので省略しました。

この結果を見ると,本来はNSSの設定ファイル(nsswitch.conf)を読んで,その設定("passwd: files")に従って/etc/passwdを見に行くはずなのに,libnss_files.so.2を読み込んで(12行目⁠⁠,芋づる式にlibc.so.6やld-linux-x86-64.so.2を読みこんでしまっています。

この動作はどう見ても仕様のレベルだよなぁ…… と,改めてglibc-2.32に付属のconfigureスクリプトを眺めていると,static_nssという変数が存在することに気づきました。

「あれれ,static_nssって定義する必要があるのか?」と,これをキーワードにconfigureスクリプトを読み進めると,文字通り--enable-static-nssというオプションがありました。

$ cat -n configure | less
....
1436    --enable-stack-protector=[yes|no|all|strong]
1437                            Use -fstack-protector[-all|-strong] to detect glibc
1438                            buffer overflows
1439    --enable-static-nss     build static NSS modules [default=no]
1440    --disable-force-install don't force installation of files from this package,
1441                            even if they are older than the installed files
....

configureの説明は"build static NSS modules(スタティック版のNSSモジュールを作る)"というごく簡単なもので,これだけでは意味がよくわからないから,"--enable-static-nss"をキーワードにドキュメント類を調べ直したところ,glibc2のインストール方法を紹介する"INSTALL"というファイルにこんな記述がありました。

$ cat -n INSTALL | less
....
149  '--enable-static-nss'
150       Compile static versions of the NSS (Name Service Switch) libraries.
151       This is not recommended because it defeats the purpose of NSS; a
152       program linked statically with the NSS libraries cannot be
153       dynamically reconfigured to use a different name database.
154  
....

どうやらこのオプションを指定しないとNSS回りのライブラリを静的リンク用にできないようです。ただし,この設定で作ったstatic linkなバイナリは,名前データベースを動的に切り替えれなくなるため,このオプションの指定は推奨しない,とのこと。確かにNSSの仕組みを考えると,この機能は動的に切り替えれた方が便利だとは思うものの,今回はこのあたりが原因のようなので,"--enable-static-nss"オプションを指定してglibc-2.32を再ビルドすることにしました。

著者プロフィール

こじまみつひろ

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

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