May 2013

tmem, cleancache, zprojectを推進していた Dan Magenheimer が Oracleを退社することに伴い、しばらく開発を離れるらしい。この雰囲気だと二度と戻ってこない可能性も高いよな。

まあ、それはいいんだが、こいつも捨て台詞に「魚をありがとう」って書いてるんだけど、そんなに面白いジョークかなあ


Hi Linux kernel folks and Xen folks --

Effective July 5, I will be resigning from Oracle and "retiring"
for a minimum of 12-18 months and probably/hopefully much longer.
Between now and July 5, I will be tying up loose ends related to
my patches but also using up accrued vacation days. If you have
a loose end you'd like to see tied, please let me know ASAP and
I will do my best.

After July 5, any email to me via first DOT last AT oracle DOT com
will go undelivered and may bounce. Please send email related to
my open source patches and contributions to Konrad Wilk and/or Bob Liu.
Personal email directed to me can be sent to first AT last DOT com.

Thanks much to everybody for the many educational opportunities,
the technical and political jousting, and the great times at
conferences and summits! I wish you all the best of luck!
Or to quote Douglas Adams: "So long and thanks for all the fish!"

Cheers,
Dan Magenheimer
The Transcendent Memory ("tmem") guy

Tmem-related historical webography:
http://lwn.net/Articles/454795/
http://lwn.net/Articles/475681/
http://lwn.net/Articles/545244/
https://oss.oracle.com/projects/tmem/
http://www.linux-kvm.org/wiki/images/d/d7/TmemNotVirt-Linuxcon2011-Final.pdf
http://lwn.net/Articles/465317/
http://lwn.net/Articles/340080/
http://lwn.net/Articles/386090/
http://www.xen.org/files/xensummit_oracle09/xensummit_transmemory.pdf
https://oss.oracle.com/projects/tmem/dist/documentation/presentations/TranscendentMemoryXenSummit2010.pdf
https://blogs.oracle.com/wim/entry/example_of_transcendent_memory_and
https://blogs.oracle.com/wim/entry/another_feature_hit_mainline_linux
https://blogs.oracle.com/wim/entry/from_the_research_department_ramster
http://streaming.oracle.com/ebn/podcasts/media/11663326_VM_Linux_042512.mp3
https://oss.oracle.com/projects/tmem/dist/documentation/papers/overcommit.pdf
http://static.usenix.org/event/wiov08/tech/full_papers/magenheimer/magenheimer_html/


stack overflowから

http://stackoverflow.com/questions/4156055/gcc-static-linking-only-some-libraries

いくつかのライブラリだけスタティックリンクして、それ以外はダイナミックリンクする方法は?みたいな話
-Bdynamicを使えという案とスタティックリンクしたいライブラリだけ .a 拡張子つきで指定すればいいじゃんという案が二強


gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

gcc some_static_lib.a -lsome_dynamic_lib code.c

glibcの修正方法を議論していてUbuntuの中の人が「んー、このglibc maintainerが提案している方法だと全パッケージのリコンパイルが必要になるよね。それは少なくともDebian, Ubuntuのパッケージングポリシーとしては受け入れがたい」と発言、即座にGentooが「じゃあいつ(全パッケージを)リビルドするの? 今でしょ!」 などと応じていて笑った。

Gentooさまエー


てゆーか、Gentooの場合リビルドしてるのはお前じゃなくて、個々のユーザーじゃねーかという気もするんだが。

前回、strcpy()は __builtin_strcpy_chk() を使っていて。。という話を書いたので、コンパイラが専用にサポートしている関数しかコンパイル時ワーニングを出せないと理解されてしまう恐れがあると思いここで、補足しておく

以下のgist をみるとわかるように大抵のコンパイル時処理は自前でできる。今のところコンパイル時にstrlen()をする方法が思いつかないので __builtin_strcpy_chk()は無理かもしれないが。

https://gist.github.com/kosaki/5515798

ポイントは

char* my_memcpy_warn () __attribute__ ((alias("my_memcpy_chk")));


attribute((alias))を使うと、my_memcpy_warnをmy_memcpy_chkのエイリアスとして登録できる。
その上で、関数宣言のほうで、

char* my_memcpy_chk (char *dest, const char *src, size_t len, size_t destlen);

char* my_memcpy_warn (char *dest, const char *src, size_t len, size_t destlen)
__attribute__((__warning__ ("my warning")));


__attribute__((warning))を使って、 my_memcpy_warnのほうだけ呼ばれたら、コンパイル時にwarning出すようにコンパイル時の扱いを変更できる。

んでもって、


static char* my_memcpy(char* dst, const char* src, size_t len)
{
size_t d = __builtin_object_size(dst, 0);

if (d != (size_t) -1) {
if (__builtin_constant_p(len) && len > d)
return my_memcpy_warn(dst, src, len, d);
else
return my_memcpy_chk(dst, src, len, d);
} else
return memcpy (dst, src, len);
}


コンパイル時に一意にきまるということは __builtin_constant_p() が trueになるはずなので、場合分けが
出来るのである。

実際、libcのなかではほとんどのFORTIFY_SOURCEの処理はこんな感じで gcc の専用サポートなしで構築されている。

_FORTIFY_SOURCEというバッファーオーバーフロー攻撃を防ぐのにとても有用なマクロがある。
知らなかった人は以下のmanでもまず見てください

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/feature_test_macros.7.html

_FORTIFY_SOURCE (glibc 2.3.4 以降)
このマクロを定義すると、文字列やメモリの操作を行う様々な関数を 使用する際にバッファオーバーフローを検出するための軽めのチェックが 実行されるようになる。すべてのバッファオーバーフローが検出される わけではなく、あくまでよくある例についてだけである。 現在の実装では、以下の関数にチェックが追加されている: memcpy(3), mempcpy(3), memmove(3), memset(3), stpcpy(3), strcpy(3), strncpy(3), strcat(3), strncat(3), sprintf(3), snprintf(3), vsprintf(3), vsnprintf(3), gets(3). _FORTIFY_SOURCE が 1 に設定された場合、コンパイラの最適化レベルが 1 (gcc -O1) かそれ以上であれば、規格に準拠するプログラムの振る舞いを 変化させないようなチェックが実行される。 _FORTIFY_SOURCE が 2 に設定された場合、さらなるチェックが追加されるが、 規格に準拠するプログラムのいくつかが失敗する可能性がある。 いくつかのチェックはコンパイル時に実行でき、コンパイラの警告として 表示される。他のチェックは実行時に行われ、チェックに失敗した場合 には実行時エラーとなる。 このマクロを使用するにはコンパイラの対応が必要であり、 バージョン 4.0 以降の gcc(1) で利用できる。



じゃあ、これがどういう風に動くのかという話なんですが、いくつか実例を見せます。

まず、固定長配列を使ったケース
https://gist.github.com/kosaki/5514378

まあ、明らかにバッファが1バイト足りないんですが、

cc -D_FORTIFY_SOURCE=2 -Wall -g -O2    strcpy_test.c   -o strcpy_test
In file included from /usr/include/string.h:642,
from strcpy_test.c:1:
In function ‘strcpy’,
inlined from ‘main’ at strcpy_test.c:10:
/usr/include/bits/string3.h:105: 警告: call to __builtin___strcpy_chk will always overflow destination buffer


コンパイルするといきなり、いや絶対バッファーオーバーフローするからやめとけと警告してくれます。親切ですね。
で、実行すると strcpyを呼んだ所で *** buffer overflow detected *** というメッセージとともに abort() してくれます。abortされちゃうと攻撃者としては乗っ取り失敗なので防御になってるわけです。

% ./strcpy_test
./strcpy_test
*** buffer overflow detected ***: ./strcpy_test terminated
======= Backtrace: =========
/lib64/libc.so.6(__fortify_fail+0x37)[0x3d28502507]
/lib64/libc.so.6[0x3d285003f0]
./strcpy_test[0x400506]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x3d2841ecdd]
./strcpy_test[0x400429]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:02 1861838 /home/kosaki/tmp/alloc_size/strcpy_test
00600000-00601000 rw-p 00000000 fd:02 1861838 /home/kosaki/tmp/alloc_size/strcpy_test
00f08000-00f29000 rw-p 00000000 00:00 0 [heap]
3d28000000-3d28020000 r-xp 00000000 fd:00 1175192 /lib64/ld-2.12.so
3d2821f000-3d28220000 r--p 0001f000 fd:00 1175192 /lib64/ld-2.12.so
3d28220000-3d28221000 rw-p 00020000 fd:00 1175192 /lib64/ld-2.12.so
3d28221000-3d28222000 rw-p 00000000 00:00 0
3d28400000-3d2858a000 r-xp 00000000 fd:00 1175212 /lib64/libc-2.12.so
3d2858a000-3d28789000 ---p 0018a000 fd:00 1175212 /lib64/libc-2.12.so
3d28789000-3d2878d000 r--p 00189000 fd:00 1175212 /lib64/libc-2.12.so
3d2878d000-3d2878e000 rw-p 0018d000 fd:00 1175212 /lib64/libc-2.12.so
3d2878e000-3d28793000 rw-p 00000000 00:00 0
3d32c00000-3d32c16000 r-xp 00000000 fd:00 1175232 /lib64/libgcc_s-4.4.7-20120601.so.1
3d32c16000-3d32e15000 ---p 00016000 fd:00 1175232 /lib64/libgcc_s-4.4.7-20120601.so.1
3d32e15000-3d32e16000 rw-p 00015000 fd:00 1175232 /lib64/libgcc_s-4.4.7-20120601.so.1
7f640b265000-7f640b268000 rw-p 00000000 00:00 0
7f640b277000-7f640b279000 rw-p 00000000 00:00 0
7fff67b14000-7fff67b29000 rw-p 00000000 00:00 0 [stack]
7fff67bf3000-7fff67bf4000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[1] 24433 abort (core dumped) ./strcpy_test



次に、mallocを使ったケース
https://gist.github.com/kosaki/5514394

これもまったく同じようにコンパイル時警告+実行時abortに成功します。すばらしいですね。
次にネタばらしに入るのですが、現実のプロジェクトでよくあるようにmallocに一枚ラッパーをかぶせて
使ってみます。

xmallocを使ったケース
https://gist.github.com/kosaki/5514404


% make
make
cc -D_FORTIFY_SOURCE=2 -Wall -g -O2 strcpy_test3.c xmalloc.c -o strcpy_test3

% ./strcpy_test3


おや、一転して警告も実行時エラーも出なくなってしまいました。
そうです。gccは "malloc" という関数がなにをしている関数が知っていてチートしていたのです。

じゃあ、どうすればいいかというと __attribute__((alloc_size)) という機能をつかって自分の関数が
mallocと同等であるとコンパイラに教えてあげればいい。

マニュアルから引用
http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
alloc_size
The alloc_size attribute is used to tell the compiler that the function return value points to memory, where the size is given by one or two of the functions parameters. GCC uses this information to improve the correctness of __builtin_object_size.
The function parameter(s) denoting the allocated size are specified by one or two integer arguments supplied to the attribute. The allocated size is either the value of the single function argument specified or the product of the two function arguments specified. Argument numbering starts at one.

For instance,

void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)))
void my_realloc(void*, size_t) __attribute__((alloc_size(2)))
declares that my_calloc returns memory of the size given by the product of parameter 1 and 2 and that my_realloc returns memory of the size given by parameter 2.


annotateしたソース
https://gist.github.com/kosaki/5514415

これは mallocを直接呼んだソースと、まったく同じように動作する。

mallocに皮をかぶせるだけで、_FORTIFY_SOURCEの機能の大半を無効化してしまい大変もったいないので、全員いますぐ alloc_sizeを理解して使うべきである。

はい、このエントリでいいたいこと終わり。以下は余談。

じゃあ、ここで strcpyが実際にどう展開されているかであるが、gccのソース読むとめどいので代わりに
wcpcpy で説明する。まあ、だいたい同じだ。だいたい。


_fortify_function wchar_t *
__NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
{
if (__bos (__dest) != (size_t) -1)
return __wcpcpy_chk (__dest, __src, __bos (__dest) / sizeof (wchar_t));
return __wcpcpy_alias (__dest, __src);
}


ここで、__bos(__dest) が -1 のときは、元々の wcpcpyを呼ぶけど、それ以外のときは
wcpcpy_chk()を呼ぶという処理になっているがわかる。

https://gist.github.com/kosaki/5514541


__bosというのは __builtin_object_size() という gccの builtinで バッファーオーバーフローを
理解する上で超重要な機能。

マニュアルから引用 http://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
— Built-in Function: size_t __builtin_object_size (void * ptr, int type)
is a built-in construct that returns a constant number of bytes from ptr to the end of the object ptr pointer points to (if known at compile time). __builtin_object_size never evaluates its arguments for side-effects. If there are any side-effects in them, it returns (size_t) -1 for type 0 or 1 and (size_t) 0 for type 2 or 3. If there are multiple objects ptr can point to and all of them are known at compile time, the returned number is the maximum of remaining byte counts in those objects if type & 2 is 0 and minimum if nonzero. If it is not possible to determine which objects ptr points to at compile time, __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0 for type 2 or 3.


要約すると、sizeof()だと配列の時しかバッファサイズとれないけど、__builtin_object_sizeだとポインタでも、コンパイルタイムに計算可能だったらサイズが取れるよ。と

というわけで、無事バッファ長がとれたので、コピーしてる最中にバッファ長超えそうになったら
abortする処理(ここでは __chk_fail()) を呼ぶようにするだけである。


/* Copy SRC to DEST, returning the address of the terminating L'\0' in
DEST. Check for overflows. */
wchar_t *
__wcpcpy_chk (wchar_t *dest, const wchar_t *src, size_t destlen)
{
wchar_t *wcp = (wchar_t *) dest - 1;
wint_t c;
const ptrdiff_t off = src - dest + 1;

do
{
if (__builtin_expect (destlen-- == 0, 0))
__chk_fail ();
c = wcp[off];
*++wcp = c;
}
while (c != L'\0');

return wcp;
}



こんな感じで __builtin_object_size() さえ理解しておけば自作関数にバッファオーバーフローチェックを追加するのは、あんまり難しくない。

nari3 先生が携帯ウォッシュレットよいとTwitterでつぶやいていたのを聞いて買ってみたんだけど、これはマジでええわ。USに越してきたからウォッシュレットなんてまったく使ってなかったんだけど、やっぱりあるといいねえ。値段も安いし

しかしこの値段で実現可能なんだったら、取り付け型ウォッシュレットの値段って説明がつかないような気がするのは気のせい?まあいいか。

水を温める機能とかはないので、そのへんは妥協してくださいね。



いちおうリンク貼っとくぜ


↑このページのトップヘ