June 2013

SD7月号の見本紙届いた。今回はレッドハットの人として東海岸雑記など書いた。RHKKのみなさまには大変お世話になりました(ぺこり)
今回一番驚いたのは「レッドハット恵比寿通信」というタイトルだという話で社内手続きを進めていたら、編集さんマジックでいつのまにかタイトルがレッドハットボストン通信に変わってたことかな。アルェー


表紙:
SoftwareDesign-2013年7月号-表紙


記事:
SoftwareDesign-2013年7月号-ボストン通信
このエントリーをはてなブックマークに追加

Unixの世界には readdir_r()というAPIがある。readdir()のthread safe バージョンとしばしば紹介されている。
それぞれの関数宣言は以下

http://man7.org/linux/man-pages/man3/readdir_r.3.html

       struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};


ところで、Solarisとかだと d_nameが d_name[1] だったりするので、


name_max = pathconf(dirpath, _PC_NAME_MAX);
if (name_max == -1) /* Limit not defined, or error */
name_max = 255; /* Take a guess */
len = offsetof(struct dirent, d_name) + name_max + 1;
entryp = malloc(len);


などとして、常にmallocで確保しないとポータブルではない。この例はLinuxの manから持ってきたものだが、このコードは問題がある。

"readdir_r considered harmful" [1] http://womble.decadent.org.uk/readdir_r-advisory.html によると以下の脆弱性がある

もし、setuidされた"rd" というプログラムが上記コード片を含んでいたとすると

# ln -sf exploit link && (rd link &; ln -sf /fat link)

pathconf(dirpath) が /fat を相手に行われて FATのrootの最大長は12byteだよーん、などという結果を受け取るので、buffer overflowの脆弱性となる。この場合はinjectionされるのがヒープなので実害がすくないが、allocaを使っていたり、

union {
struct dirent a;
char b[1024];
}


などと横着をしていると目もあてられない事になる。で、回避方法なんだがく、[1] だと BSD由来のdirfd APIでdir streamのfdとってきて fpathconf()でパス最大長取ってこいということになっている。が、もっといい方法がある

Ulrich Drepperが指摘したのだが、そもそも現代のOSではreaddir()はthread safeなんだから、それ使えばええんや。代替関数なんていらんかったんや。と指摘 [2]

http://udrepper.livejournal.com/18555.html

次世代POSIXでは readdir_rは時代遅れとマークされ、readdir()はDIRをスレッド間で共有しない限りスレッドセーフであることが mustになる。これは現状の追認だから誰も困らんだろう。


さて、実は readdir_r には Linux特有のもう1つ困った問題がある。Linuxは前述のとおり d_nameが256 byteになってるから、結構な数のアプリケーションがmalloc()とかせずに、スタックに直接 direntを置いている。普通はこれは問題ない、なぜなら、NAME_MAX は255 でPOSIXの世界でNAME_MAXがヘッダで defineされていたら、ファイルシステムの種類によらず NAME_MAXを超えてはいけないからである。
問題があるというからにはもちろんルールを破っているお馬鹿さんがいるからである。FUSEである。

linux/fs/fuse/fuse_i.h をみると

/** It could be as large as PATH_MAX, but would that have any uses? */
#define FUSE_NAME_MAX 1024


などというふざけたコメントとともに、何故か最大長が1024になっている。これによりfuseがONになっているシステムに readdir_r を使う setsidプロセスがインストールされた場合 root 特権が奪取できる穴が空く可能性がある。

何度でもいうが、スタックオーバーフローはヒープオーバーフローよりヤバいのであって、d_name[] が下手に大きいことによって、dirent がスタックに置かれる危険がましちゃってるのが Linux なのだ。

そういうわけで、アプリケーションデベロッパーのみなさまにおきましては、各自自衛のため、readdir_r()はreaddir()に書き換えていただくよう鋭意努力していだたくとよろしいかと思います。

わたしは今FUSEへこの件のパッチ書いてるんだけど、その話は別件なので、このエントリでは書かない。

おわり


あわせて読みたい: 革命の日々! 効率的なdirectry readingコードについて http://mkosaki.blog46.fc2.com/?no=1008


2016/09/28 追記
この件に関するPOSIXの議論はこのURL参照 http://austingroupbugs.net/view.php?id=696

readdir_rとFUSEの話はCVE-2013-4237として、glibc側で、カーネルがNAME_MAXを超えるパスを渡してきたら、readdir_rでENAMETOOLONGに変換してアプリケーションに返すというパッチが入った。NTFS, CIFSだとNAME_MAX超えるのは本来Validなので、Windows上でAttackerがそういうファイルをつくってしまうのを禁止できないからFUSEだけ考えてもしかたがない。
影響範囲はGNU C Library 2.18 およびそれ以前。

この件のリンク各種
Red Hat Bugzilla https://bugzilla.redhat.com/show_bug.cgi?id=995839
glibc Bugzilla https://sourceware.org/bugzilla/show_bug.cgi?id=14699

Red Hat CVE https://access.redhat.com/security/cve/cve-2013-4237
Red Hat RHSA https://rhn.redhat.com/errata/RHSA-2014-1391.html
JVN http://jvndb.jvn.jp/ja/contents/2013/JVNDB-2013-004574.html
このエントリーをはてなブックマークに追加

海外で Linux LXCのプレゼン見てると以下の画像を頻繁にみるのですが、日本のみなさまにおかれましては
どこで売っているシロモノか教えてください

f8bfa17c.jpg


なぜか中国のパチモン文化すげえと思われてますが、これは日本のものです(キリリッ
このエントリーをはてなブックマークに追加

↑このページのトップヘ