カテゴリ: linux

ちょっと古い話題だけど、glibc 2.18に lock elision入ることが確定しましたね。lock elisionとはHaswellから搭載されるHTM(Hardware Transaction Memory) をつかって、pthread_mutex_* の並列度を劇的にあげようというパッチ。

ワーストケースでは逆に何十倍も遅くなるとか非互換ありでしかもPOSIX違反やんけなどと指摘されつつもIntelの強力なプッシュによりデフォルトでONになった。

ロック性能とか気になる人はチェックしとくといいよ。
このエントリーをはてなブックマークに追加

http://austingroupbugs.net/view.php?id=722

glibcは Open POSIX testcase通らないんだけど、規格がバグってんじゃねーのとイチャモンついてる。

pthread_rwlock_wrlock は

Implementations may favor writers over readers to avoid writer
starvation.



と、実装依存でどっちもでいいよと言っていて、それにしたがってglibcは pthread_rwlockattr_setkind_np という非標準関数でポリシーを選べるようにしてあるのだが、


pthread_rwlock_rdlock のほうを見ると

If the Thread Execution Scheduling option is supported, and the threads
involved in the lock are executing with the scheduling policies
SCHED_FIFO or SCHED_RR, the calling thread shall not acquire the lock if
a writer holds the lock or if writers of higher or equal priority are
blocked on the lock; otherwise, the calling thread shall acquire the
lock.



と、SCHED_FIFO, SCHED_RR の時は、write 優先にしなさいとある。じゃあ、実質オプショナルじゃないじゃんということになるかといえばそう簡単ではなく

同じく pthread_rwlock_rdlock の記述に、

The calling thread acquires the read lock if a writer does not hold the
lock and there are no writers blocked on the lock.



とあるので、readlock を recursive にとるのは許さないといけないように読める。しかし、これは write 優先ポリシーと組み合わせるとデッドロックしてしまうので混ぜるな危険なのである。

なので、SCHED_FIFOの記述も shall じゃなくて may にするか、リカーシブな read lockに関して記述を追加するかしないと workしないよ。と。
このエントリーをはてなブックマークに追加

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
このエントリーをはてなブックマークに追加

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
このエントリーをはてなブックマークに追加

前回、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() さえ理解しておけば自作関数にバッファオーバーフローチェックを追加するのは、あんまり難しくない。
このエントリーをはてなブックマークに追加

Linuxでは伝統的にディレクトリ操作(ファイルのrenameとか)を asyncに行なってきた。これはBSDなどよりも性能がいい理由の一つだったんだけど、クラッシュした時にとんでもない壊れ方をするのでまあ色々と評判が悪かった。
さて、じゃあこのようなシステムで安全なアプリケーションを書くためにはどうすればいいかという話になるが、頭の良い人がディレクトリに対してfdatasyncすればいいじゃん。と言い出して、みんなそれがPOSIX filesystem のルールだと思ってたんだな。

で、今回、aio_fsync()がなぜか write permissionを要求してるので、ディレクトリにaio_fsync()できねーじゃねーか。という規格の不整合を上訴したひとがいて、議論の結果「ディレクトリに操作に対してfdatasync()を要求するようなasyncなファイルシステムはPOSIX準拠じゃない」という一文が規格書に追記されることになった。


ええええええーーーーー


いまさらかよ。
まあ、同時にnon-comforming filesystemでどうすればいいのかが追記されたので、今後は混乱は減っていく・・・のかなあ?


http://sourceware.org/ml/libc-alpha/2013-04/msg00578.html

Just to close this thread, Geoff Clare has summarized the changes regarding aio_fsync() and relationship wrt. directory entry data.



"In the April 18 teleconference it was agreed that the standard
should mandate that directory operations are always synchronized
on conforming file systems, and should include warnings about
non-conforming configurations. The proposed changes are as follows.

Changes to XBD...

At page 94 line 2581-2588 section 3.376 change:

For read, when the operation has been completed or diagnosed if
unsuccessful. The read is complete only when an image of the data
has been successfully transferred to the requesting process. If
there were any pending write requests affecting the data to be
read at the time that the synchronized read operation was
requested, these write requests are successfully transferred prior
to reading the data.

For write, when the operation has been completed or diagnosed if
unsuccessful. The write is complete only when the data specified
in the write request is successfully transferred and all file
system information required to retrieve the data is successfully
transferred.

to:

For read operations, when the operation has been completed or
diagnosed if unsuccessful. The operation is complete only when an
image of the data has been successfully transferred to the
requesting process. If there were any pending write requests or
(if the file is a directory) directory modifications affecting
the data to be read at the time that the synchronized read
operation was requested, these requests are successfully
transferred prior to reading the data.

For write operations and directory modification operations, when
the operation has been completed or diagnosed if unsuccessful. The
operation is complete only when the written data or (if the file
is a directory) modified directory entries have been successfully
transferred to storage and all file system information required
to retrieve them is successfully transferred.

At page 107 line 2859 add a new XBD 4.2 section (and renumber the
current 4.2 and all later 4.x sections):

4.2 Directory Operations

All file system operations that read a directory or that modify
the contents of a directory (for example creating, unlinking, or
renaming a file) shall be completed as defined for synchronized
I/O data integrity completion (see section 3.376).

Note: Although conforming file systems are required to
perform all directory modifications as synchronized I/O
operations, some file systems may support non-conforming
configurations (for example via mount options) where
directory modifications are not synchronized. Applications
that rely on directory modifications being synchronized should
only be used with such file systems in their conforming
configuration(s).


Changes to XSH...

At page 574 line 19833 section aio_fsync() change the APPLICATION
USAGE section from:

None.

to:

Refer to fdatasync() and fsync().

At page 815 line 27215 section fdatasync() append to the first
paragraph:

If the file is a directory, an implicit fdatasync() is already
performed on every I/O operation (see XBD 4.2) and consequently
if fdatasync() is called explicitly it shall take no action and
shall return the value 0.

At page 815 line 27232 section fdatasync() change the APPLICATION
USAGE section from:

None.

to:

Although conforming file systems are required to complete all
directory modifications as defined for synchronized I/O data
integrity completion, some file systems may support non-conforming
configurations (for example via mount options) where directory
modifications are not synchronized. When the file system is
configured in this way, calls to fdatasync() on directories may
cause I/O operations to be synchronized, rather than being a no-op.

At page 954 line 31987 section fsync() add a new paragraph to the
APPLICATION USAGE section:

Since conforming file systems are required to complete all
directory modifications as defined for synchronized I/O data
integrity completion (see XBD 4.2), calling fsync() on a directory
only synchronizes the file attributes such as timestamps. However,
some file systems may support non-conforming configurations (for
example via mount options) where modifications to directory
contents are not synchronized. When the file system is configured
in this way, calls to fsync() on directories may cause directory
contents to be synchronized in addition to file attributes.

Changes to XRAT...

At page 3444 line 115531 add a new XRAT A.4.2 section (and renumber
the current A.4.2 and all later A.4.x sections):

A.4.2 Directory Operations

Earlier versions of this standard did not make clear that all directory
modifications are performed as synchronized I/O operations, although
that is the historical behavior and was always intended. Applications
have no need to call fdatasync() or fsync() on a directory unless
they want to synchronize the file attributes (using fsync()), provided
the directory is on a conforming file system. However, since
applications may wish to use fdatasync() or fsync() to synchronize
directory modifications on non-conforming file systems, implementations
are required to support fdatasync() on directories as a no-op on
conforming file systems."


このエントリーをはてなブックマークに追加

http://www.soum.co.jp/misc/tatuhiko/multi-thread/index.html

READ_RWLOCK_PREFER_READER_NPがデフォルト値のようなのです。字面から書き込みを優先しそうな、PTHREAD_RWLOCK_PREFER_WRITER_NPを設定して、再度確認してみます。しかし、なぜかこれを設定しても、書きこみロック待ちのスレッドがあっても、読み込みロックが取得できてしまします。意味がわかりません。

試しに PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP を指定して試してみると、書きこみロック待ちのスレッドがある場合にうまい具合に読み込みロックがブロックしてくれました。



とかいう記述をみつけたのひとこと。

答えはLSBに書いてある

http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/libpthread-pthread-rwlockattr-getkind-np-1.html

Notes
Setting the value read-write lock kind to PTHREAD_RWLOCK_PREFER_WRITER_NP, results in the same behavior as setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a reader thread holds the lock the thread holding a write lock will be starved. Setting the kind value to PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, allows the writer to run. However, the writer may not be recursive as is implied by the name.



コードだとこのへん。PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NPのときだけ、flagsが1になってるのがわかると思う


int
__pthread_rwlock_init (rwlock, attr)
pthread_rwlock_t *rwlock;
const pthread_rwlockattr_t *attr;
{
const struct pthread_rwlockattr *iattr;

iattr = ((const struct pthread_rwlockattr *) attr) ?: &default_rwlockattr;

memset (rwlock, '\0', sizeof (*rwlock));

rwlock->__data.__flags
= iattr->lockkind == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;



元々、read/write lockで write が recursiveって変なのでなかったことになってる。
このエントリーをはてなブックマークに追加

ハマりポイントは色々とあるけど基本は難しくない

・git clone
git clone git://sourceware.org/git/glibc.git

・ビルドディレクトリ作成
cd glibc
make build
cd build

・configure

targetdir=${HOME}/local/glibc

../configure \
--enable-add-ons=nptl \
--enable-all-warnings \
--prefix=${targetdir}

・make

make PARALLELMFLAGS=-j8

make -j8はMakefileの書き方がよくなくて、ちゃんと動かないのだそうだ。

・make install

make install

あー、configureの時にprefix指定しておかないとmake installで死ぬから気をつけてね。
prefixをroot権限不要な場所を選ぶ、make installをrootで実行しない。は鉄則

・ld.so.cacheの作成

ldconfig -C ${HOME}/local/glibc/etc/ld.so.cache

libgcc_s.so とかの場所を教えてあげないと、さまざまなトラブルがあなたを襲います

・make tests

make testsでglibcのtest suiteが走ります。いろいろとエラーが出るけど対処方法は別途書く(きっと書かない)


・自分のアプリケーションのテスト

以下のように staticリンクしてしまうのがもっとも楽。

http://sourceware.org/glibc/wiki/Tips_and_Tricks/How_to_Build_an_application_with_your_own_custom_glibc


・make testsでのエラーあれこれ

http://sourceware.org/glibc/wiki/Testing/Testsuite

- abi-check でエラー

prefixをつけると _nl_default_dirname が変わってしまうので絶対出るのだよ。あきらめれ

- bug-atexit3 and nptl tests

わたしがいったように、ldconfigしませんでしたね?反省しなさい

- tst-eintr1

テストがバグってるのでたまにこけます。libc-alpha にパッチあるけど、まだ巻かれてないっぽい

- tst-cpuclock2

それはカーネルのバグ。こないだLKMLにパッチ投げといた。posix timer は誰も使ってないので
まだまだコーナーケースの扱いが甘いようだ。
テストが定期的にコケてるのに、何年も無視し続けるglibc communityすごい
このエントリーをはてなブックマークに追加

↑このページのトップヘ