February 2012

ようするに /tmp に symlink があるときに、ownerが自分じゃないときはrootであってもsymlinkたどれないようにするよ。というパッチである

背景なんですが、Unixの古典的なセキュリティーホールにrootが O_EXCL つけずに/tmp以下のファイルを開くとアタッカーに利用されて脆弱性になるというものがありまして、古典的には以下のストーリー

1.悪意のあるユーザ Bob が ln -s /etc/passwd /tmp/app-A-tmpfile とかリンクを貼る
2.root権限をもったデーモンアプリが /tmp/app-A-tmpfile を O_EXCL なしで開く(そして成功する)
3.そのアプリが今開いたtmpファイルに適当にデータを書く
4.なぜか /etc/passwdが破壊される ← イマココ

O_EXCL以外の方法、たとえばstat(2)とかで確認するのはタイミング的に穴があってだめなのだが、この手のミスが一向になくならずに毎年おんなじセキュリティーホールが量産されてるのが現状である。

なおこのsymlink traversal 脆弱性については田中哲先生の以下のの解説が詳しい
http://www.ipa.go.jp/security/fy20/reports/tech1-tg/2_05.html


で、まあ、この動作はPOSIX準拠に必要なのでどんなにクソであってもサポートしなくちゃいかんというのが頭の痛いところでしてね。Plan9だと/tmpがユーザ毎に別ネームスペースだからこういう問題がおきないんだけど、Unix系では一部のアホアプリが/tmp以下にIPC的に使うファイルを作ることがあるので勝手にisolateするとアプリが動かなくなるとかなんとか。まあ死ねという話です。

さて、世の中にはPOSIXなんか無視しちゃえばいいじゃん。アプリ壊れてもいいじゃん。セキュリティー重要とか言い出す極右というか空気が読めないというかそういう方々がいます。Keesさんもそのうちの1人。

表題の彼のパッチはなにをやっているかというと

・ sticky bitが立っていて、かつworld writableなディレクトリ(普通のシステムではtmp dirしかないはず)
に直下にsymlinkがあるときは、は通常のアクセス権チェックとは別に
symlinkのuidとプロセスのuidが一致するかを調べ、不一致の時はリンクたどる処理が失敗する
(openとかstatとかのPATHを引数にとるシステムコールに対してEACCESSが返る)
・ /proc/sys/fs/protected_sticky_symlinks に0を設定すると従来の動作に戻る

とかいう処理

パッチ全体は以下を参照。
http://lwn.net/Articles/470891/

正直あんまり筋のいい解決方法とは思えないのだが
1.全アプリが気をつけないといけない状況だと次から次へと五月雨式にアホアプリが出てきてきりがない
2.Openwall と grsecurity で以前から使われており大きな問題は起きていない

というあたりで通った。

僕はこのパッチあんまり好きじゃないんだけどLinusがこのパッチをボロクソにけなしてたときに、Linusのカウンタープロポーサルを撃沈して流れを変えちゃったんだよなあ。ちょっと後悔

だれか、いまの仕様の問題点に気がついたら教えてください。ではでは

RubyのArgumentErrorってなんなんだろうか。正しい使い方が思いつかない。名前からすると「引数エラー」なのだが、IOやらスレッドやらの副作用がない世界だと引数が正しければ正しく動くはずなのであらゆるケースが ArgumentErrorに丸めることが出来てしまう(そして本当にあらゆる種類のArgumentErrorがあるように標準ライブラリからは見える)

これは困った事で ArgumentErrorを rescue しても何が起こったのかまわったく分からないのではリカバー処理の書きようがない。エラー処理が書けないぐらいならない方がましで、いっそ ArgumentError廃止したら?という気さえしてくる。

一方別の解法もある、エラーの種類毎にArgumentErrorのサブクラスをつくって判別できるようにすることである。Javaの IllegalArgumentExceptionや C#のArgumentException がこの方針であるように見える。

しかしサブクラス方針ってJavaの価値観としては美しいけど、Rubyの大クラス主義には反してる気がしなくもない。自分がライブラリつくる立場になった時に毎回 ArgumentErrorのサブクラス作りたいかというと疑問


・・・と、ここまで書いて下書きを放置していたのだが、まめさんが遙か昔にまったく同じ事を、しかも遙かに高いレベルで指摘しているのを気づいてしまったので続きを書く気が失せた


まめめも:Ruby の例外クラスは分類が粗すぎる or 細かすぎる
http://d.hatena.ne.jp/ku-ma-me/20090423/p1


補足1: Unixの世界の errno の EINVAL にも同じ問題があり、Linuxみたいにしょっちゅうシステムコールが拡張されてるケースでライブラリ作者にストレスが溜まる。(というか実際にglibc界隈から苦情を受けた経験がある)

Twitterに書いたけど、こういうネタはBlogに書くべきだった。再投稿


gccがstackの自動伸長のサポートを計画しているらしい。コンパイラがケアしてくれるならスタックが連続している必要はないからスタックの初期値をすごく小さく出来て32bit CPUでたくさんFiberがつくれるとかいう話っぽい

http://gcc.gnu.org/wiki/SplitStacks

Jan Karaがなかなか素敵なバグを報告している。

bitfieldいじるときに、64bit load/store使えうように、となりのbitfieldも合わせて read-modify-write
するから全然関係ないフィールド更新時に別のフィールドがlostするって・・・

でいつものようにGCC界隈から「Cの規格書にはやっちゃダメとは書いてない」という声が飛んできて
カーネル界隈から「そんなんソフトが書けるか。あほー」という怒号が飛び交う展開に
だって、普通たまたま隣り合ってるだけの全然関係ないフィールドってロックも別だよねえ(-_-;)


From: Jan Kara jack@suse.cz vger.kernel.org
To: LKML
Cc: linux-ia64@vger.kernel.org,
Linus Torvalds ,
dsterba@suse.cz,
ptesarik@suse.cz,
rguenther@suse.de,
gcc@gcc.gnu.org
日付: 2012年2月1日10:19
件名: Memory corruption due to word sharing

Hello,

we've spotted the following mismatch between what kernel folks expect
from a compiler and what GCC really does, resulting in memory corruption on
some architectures. Consider the following structure:
struct x {
long a;
unsigned int b1;
unsigned int b2:1;
};

We have two processes P1 and P2 where P1 updates field b1 and P2 updates
bitfield b2. The code GCC generates for b2 = 1 e.g. on ia64 is:
0: 09 00 21 40 00 21 [MMI] adds r32=8,r32
6: 00 00 00 02 00 e0 nop.m 0x0
c: 11 00 00 90 mov r15=1;;
10: 0b 70 00 40 18 10 [MMI] ld8 r14=[r32];;
16: 00 00 00 02 00 c0 nop.m 0x0
1c: f1 70 c0 47 dep r14=r15,r14,32,1;;
20: 11 00 38 40 98 11 [MIB] st8 [r32]=r14
26: 00 00 00 02 00 80 nop.i 0x0
2c: 08 00 84 00 br.ret.sptk.many b0;;

Note that gcc used 64-bit read-modify-write cycle to update b2. Thus if P1
races with P2, update of b1 can get lost. BTW: I've just checked on x86_64
and there GCC uses 8-bit bitop to modify the bitfield.

We actually spotted this race in practice in btrfs on structure
fs/btrfs/ctree.h:struct btrfs_block_rsv where spinlock content got
corrupted due to update of following bitfield and there seem to be other
places in kernel where this could happen.

I've raised the issue with our GCC guys and they said to me that: "C does
not provide such guarantee, nor can you reliably lock different
structure fields with different locks if they share naturally aligned
word-size memory regions. The C++11 memory model would guarantee this,
but that's not implemented nor do you build the kernel with a C++11
compiler."

So it seems what C/GCC promises does not quite match with what kernel
expects. I'm not really an expert in this area so I wanted to report it
here so that more knowledgeable people can decide how to solve the issue...

Honza

↑このページのトップヘ