こんにちは、masm11 です。
Linux の procfs をご存知でしょうか。 疑似ファイルシステムの一つで、通常は /proc/ に mount されていて、 その中でも /proc/PID はプロセスに関するいろいろな情報を見せて くれます。
今回は、/proc/PID を使って何ができるかを見てみたいと思います。
なお、/proc/PID
の PID
の箇所にはプロセスIDを指定します。
ただし、/proc/self
で、そのプロセスのプロセスIDを指定したことにする
指定方法もあります。
環境変数
いきなりですが、
vim /proc/self/environ
と実行してみましょう。
GDK_DPI_SCALE=1^@XDG_ACTIVATION_TOKEN=kwin-7^@TERMINATOR_UUID=urn:uuid:e4ba8e93-e42f-4e16-8e01-00dec c17e9ca^@KDE_SESSION_UID=1000^@DESKTOP_SESSION=plasmawayland^@PWD=/home/masm^@CLICOLOR=1^@WAYLAND_DI SPLAY=wayland-0^@VISUAL=vi^@GDK_SCALE=1^@AURDEST=/aur^@LOGNAME=masm^@ANDROID_EMULATOR_USE_SYSTEM_LIB S=1^@MOZ_ENABLE_WAYLAND=1^@MAIL=/var/spool/mail/masm^@EDITOR=vi^@XDG_SESSION_TYPE=wayland^@SYSTEMD_E
実行すると、vim が色を付けてくれるので、もう少し見やすいと思います。
環境変数が見えました。
複数の環境変数が ^@
で区切られて表示されています。
「どうにもうまく動かんなぁ... 環境変数ちゃんと渡ってないのかしら...」 と疑問に思ったら、これで確認できます。
「環境変数なら外から見えないので安全」とか思ってるそこのあなた、 やばいかもしれませんね。
メモリマップ
あまりないかもしれませんが、
shared library (*.so
) がちゃんと読めてるのか疑問に思うことがあります。
ldconfig -p
である程度表示できますが、
「で、結局どうなん??」と思うこともあります。
直接メモリマップを見てしまいましょう。
vim /proc/self/maps
↑これを実行すると、↓こんな感じに表示されます。
55c6abfad000-55c6ac032000 r--p 00000000 00:19 204122 /usr/bin/vim 55c6ac032000-55c6ac346000 r-xp 00085000 00:19 204122 /usr/bin/vim 55c6ac346000-55c6ac3d1000 r--p 00399000 00:19 204122 /usr/bin/vim 55c6ac3d1000-55c6ac3e4000 r--p 00424000 00:19 204122 /usr/bin/vim 55c6ac3e4000-55c6ac411000 rw-p 00437000 00:19 204122 /usr/bin/vim 55c6ac411000-55c6ac41f000 rw-p 00000000 00:00 0 55c6adb4b000-55c6adc10000 rw-p 00000000 00:00 0 [heap] 7f4ef806b000-7f4ef80b5000 r--p 00000000 00:19 344829 /usr/share/vim/vim90/lang/ja/LC_MESSAGES/vim.mo 7f4ef80b5000-7f4ef85eb000 r--p 00000000 00:19 819739 /usr/lib/locale/locale-archive 7f4ef85eb000-7f4ef85ee000 rw-p 00000000 00:00 0 7f4ef85ee000-7f4ef85f0000 r--p 00000000 00:19 13542 /usr/lib/libcrypt.so.2.0.0
/usr/bin/vim がどこにロードされてて、/usr/lib/libcrypt.so.2.0.0 がどこにロードされてて、 というのがすべて表示されます。
もし万が一、ディスク上の別の場所にある同じ名前の違うバージョンの shared library が 両方読み込まれていたら、まずいですね。違うバージョンとはいえ、同じシンボルはあるでしょうから、 どちらが呼ばれるかわかりません。
カレントディレクトリ
/proc/PID を使うと、そんなものまでわかります。
luna:~ % ls -l /proc/self/cwd lrwxrwxrwx 1 masm masm 0 9月 26 19:30 /proc/self/cwd -> /home/masm/
カレントディレクトリを指しています。
あまり用途はないですが、たまに役に立つこともあります。
Rails アプリを /data/xxx/releases/YYYYMMDD_HHMMSS/ にデプロイしてあるルールとします。 「なんかおかしいなぁ... 古いプロセスが混ざってるんじゃ…?」と疑問を感じたら、 実行中の Unicorn の PID をすべて列挙し、それらの /proc/PID/cwd を確認すれば、 古いプロセスかどうかわかります。
また、chroot の檻の中で /proc に procfs を mount してある場合、 PID に檻の外にいるプロセスの PID を指定すると、 /proc/PID/cwd から外に出られてしまいます。 「chroot をセキュリティ用途に使うな」というのは、この辺からも解りますね。
「え? /proc/PID/cwd は symbolic link なんだから、外には出られないでしょ?」 と思われた方。するどいです。 実は symbolic link は ls が見せてる仮の姿で、本当は実体なんです。 以下に実証します。
luna:t % mkdir root # procfs を mount luna:t % mkdir root/proc luna:t % sudo mount --bind /proc root/proc # bash を用意 luna:t % mkdir root/bin luna:t % mkdir root/lib64 luna:t % mkdir -p root/usr/lib luna:t % cp /usr/lib/libreadline.so.8 root/usr/lib/ luna:t % cp /usr/lib/libdl.so.2 root/usr/lib/ luna:t % cp /usr/lib/libc.so.6 root/usr/lib/ luna:t % cp /usr/lib/libncursesw.so.6 root/usr/lib/ luna:t % cp /lib64/ld-linux-x86-64.so.2 root/lib64 luna:t % cp /bin/bash root/bin/ # chroot する luna:t % sudo chroot root /bin/bash # procfs 経由で脱獄 bash-5.1# cd /proc/1/cwd bash-5.1# echo * aur bak bin boot dev etc home lib lib64 mnt nfs opt pkg proc root run sbin srv sys tmp usr var bash-5.1#
ファイルディスクリプタ
そのプロセスがどんなファイルを開いているか、確認できます。
luna:~ % ls -l /proc/self/fd/ 合計 0 lrwx------ 1 masm masm 64 9月 26 19:54 0 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 19:54 1 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 19:54 2 -> /dev/pts/1 lr-x------ 1 masm masm 64 9月 26 19:54 3 -> /proc/133657/fd/ luna:~ %
/dev/pts/1 というのは擬似端末です。別のプロセスとつながっています。 今回の場合は、self は ls コマンドですから、出力先は端末エミュレータなので、 /dev/pts/1 はその端末エミュレータにつながっています。
では、pipe を作ります。
luna:~ % cat | cat
PID を確認して、/proc/PID/fd/ を確認します。
luna:~ % ps auxww | grep ' cat$' masm 140343 0.0 0.0 7980 780 pts/1 S+ 21:16 0:00 cat masm 140344 0.0 0.0 7980 788 pts/1 S+ 21:16 0:00 cat luna:~ % ls -l /proc/140343/fd/ 合計 0 lrwx------ 1 masm masm 64 9月 26 21:18 0 -> /dev/pts/1 l-wx------ 1 masm masm 64 9月 26 21:18 1 -> 'pipe:[344223]' lrwx------ 1 masm masm 64 9月 26 21:18 2 -> /dev/pts/1 luna:~ % ls -l /proc/140344/fd/ 合計 0 lr-x------ 1 masm masm 64 9月 26 21:18 0 -> 'pipe:[344223]' lrwx------ 1 masm masm 64 9月 26 21:18 1 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 21:18 2 -> /dev/pts/1 luna:~ %
0 や 1 が変なものを指してますね。 pipe らしいです。
0 が標準入力、1 が標準出力です。 なので、
- 左側の cat: 出力先が pipe: PID=140343
- 右側の cat: 入力元が pipe: PID=140344
です。
この fd に書き込むとどうなるのでしょうか?
echo 1 > /proc/140343/fd/1
cat してる端末に変化がありました。
luna:~ % cat | cat 1
なんと、pipe に割り込めました。 確かに /proc/PID/fd/ は実体を指しているようです。
こうなると、socket でもできるかもしれない、と想像が働きます。 ssh の connection に割り込んでみます。
luna:~ % ls -al /proc/138500/fd/ 合計 0 dr-x------ 2 masm masm 0 9月 26 21:11 ./ dr-xr-xr-x 9 masm masm 0 9月 26 21:10 ../ lrwx------ 1 masm masm 64 9月 26 21:11 0 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 21:11 1 -> /dev/null lrwx------ 1 masm masm 64 9月 26 21:11 2 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 21:11 3 -> 'socket:[338439]' lrwx------ 1 masm masm 64 9月 26 21:11 4 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 21:11 5 -> /dev/pts/1 lrwx------ 1 masm masm 64 9月 26 21:11 6 -> /dev/pts/1 luna:~ % echo foo > /proc/138500/fd/3 zsh: そのようなデバイスやアドレスはありません: /proc/138500/fd/3
あら残念、さすがに無理かー
まとめ
procfs を使って、いろいろ遊んでみました。
もしかすると、何か便利な場面はあるかもしれませんね。 /proc/PID/cwd の例で示したように、サーバが変な挙動をしている場合の調査には使えるかもしれません。
ではまた!