2026年04月16日

いつの間にか syzkaller 内にダンジョンが追加されていた件

Linux カーネル 7.0 が 2026/4/13 にリリースされ、 syzbot が報告した不具合の修正件数も7000件を超えました。
5000件が7000件になるまでに約2年、新しく混入した不具合が早期に修正されるようになってきたものの、古くから存在している不具合はなかなか修正されずに残っているようです。

syz-upstream.png

右上の隅を見ると、「城」のアイコンが追加されていました。なんだろうと思ってクリックしてみたら、ダンジョンに飛ばされました。

syz-dungeon.png

どうやら不具合の修正に貢献した開発者をランキング形式で表示しているページで、熊猫は2位だそうです。

でも、ダンジョンという名前が付いているからには、ゲーム画面っぽい画像の部分をクリックするとミニゲームが始まるのだろうか・・・と思ったら違いました。今後、実装されたりするのかな?(笑)

posted by 熊猫さくら at 22:09| Comment(0) | TrackBack(0) | Linux

2024年08月29日

glibc さんの裏金疑惑!?

OOM Killer 対策として消費メモリを削減する方法を探していたら、驚きの情報を見つけましたので、C言語で検証してみました。

https://man7.org/linux/man-pages/man3/mallopt.3.html で説明されているように、 glibc には dynamic mmap threshold という仕組みが実装されています。この仕組みが有効になっている場合、メモリの割り当て/解放を繰り返すプログラムを実行した際に、一部のメモリがOSに返却されなくなるという事象が発生します。これは、長期間動作を継続するプログラムにとって、無駄なメモリ消費の原因となることがあります。

実行結果例1:

物理メモリを6GB以上搭載した RHEL8 以降の環境で、以下のように約4GBのメモリの割り当て/解放を繰り返すプログラムを実行した場合、OSから割り当てたメモリの約8割がOSに返却されないまま(再割り当て要求に備えて glibc が保持したまま)の状態が発生することが観測できます。

--------------------------------------------------------------------------------
[root@localhost ~]# uname -r
4.18.0-80.el8.x86_64
[root@localhost ~]# ls -l /lib64/libc.so.6
lrwxrwxrwx 1 root root 12 May 14  2019 /lib64/libc.so.6 -> libc-2.28.so
[root@localhost ~]# gcc -Wall -O2 -x c - -o rss << EOF
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> #include <malloc.h>
>
> static void show_rss(void)
> {
>         static FILE *fp = NULL;
>         static char buffer[4096];
>         char *cp;
>         unsigned long rss;
>         if (!fp) {
>                 fp = fopen("/proc/self/status", "r");
>                 if (!fp)
>                         return;
>         }
>         rewind(fp);
>         buffer[fread(buffer, 1, sizeof(buffer) - 1, fp)] = 0;
>         cp = strstr(buffer, "VmRSS:");
>         if (cp && sscanf(cp + 6, "%lu", &rss) == 1)
>                 printf("\tRSS: %lu KB\n", rss);
> }
>
> int main(int argc, char *argv[])
> {
>         static char *buf[256];
>         int i, j, k;
>         srand(0);
>         for (k = 0; k < 20; k++) {
>                 unsigned long sum = 0;
>                 for (i = 0; i < 256; i++) {
>                         unsigned int size = (random() % 8192) * 4096;
>                         char *cp = malloc(size);
>                         if (!cp)
>                                 return 1;
>                         for (j = 0; j < size; j += 4096)
>                                 cp[j] = 1;
>                         sum += size;
>                         buf[i] = cp;
>                 }
>                 printf("%d\t+%lu KB\n", k, sum / 1024);
>                 show_rss();
>                 for (i = 0; i < 256; i++)
>                         free(buf[i]);
>                 printf("\t-%lu KB\n", sum / 1024);
>                 if (argc > 1)
>                         malloc_trim(0);
>                 show_rss();
>         }
>         return 0;
> }
> EOF
[root@localhost ~]# ./rss
0       +4122392 KB
        RSS: 4123292 KB
        -4122392 KB
        RSS: 1380 KB
1       +4182160 KB
        RSS: 4183300 KB
        -4182160 KB
        RSS: 1508 KB
2       +4130460 KB
        RSS: 4131836 KB
        -4130460 KB
        RSS: 1508 KB
3       +4241984 KB
        RSS: 4243208 KB
        -4241984 KB
        RSS: 1508 KB
4       +4198020 KB
        RSS: 4199160 KB
        -4198020 KB
        RSS: 1508 KB
5       +4392688 KB
        RSS: 4393868 KB
        -4392688 KB
        RSS: 1508 KB
6       +4246196 KB
        RSS: 4247432 KB
        -4246196 KB
        RSS: 3407164 KB
7       +4039140 KB
        RSS: 4040608 KB
        -4039140 KB
        RSS: 3407164 KB
8       +4346760 KB
        RSS: 4348884 KB
        -4346760 KB
        RSS: 3407164 KB
9       +4369996 KB
        RSS: 4371692 KB
        -4369996 KB
        RSS: 3407164 KB
10      +3937740 KB
        RSS: 3939848 KB
        -3937740 KB
        RSS: 3407164 KB
11      +4044560 KB
        RSS: 4046120 KB
        -4044560 KB
        RSS: 3407164 KB
12      +4193628 KB
        RSS: 4196104 KB
        -4193628 KB
        RSS: 3407164 KB
13      +4061028 KB
        RSS: 4062484 KB
        -4061028 KB
        RSS: 3407164 KB
14      +4529360 KB
        RSS: 4531736 KB
        -4529360 KB
        RSS: 3407164 KB
15      +4101504 KB
        RSS: 4102856 KB
        -4101504 KB
        RSS: 3407164 KB
16      +4254812 KB
        RSS: 4257280 KB
        -4254812 KB
        RSS: 3407164 KB
17      +4335764 KB
        RSS: 4339976 KB
        -4335764 KB
        RSS: 3407164 KB
18      +4118404 KB
        RSS: 4119636 KB
        -4118404 KB
        RSS: 3407164 KB
19      +4049376 KB
        RSS: 4050776 KB
        -4049376 KB
        RSS: 3407164 KB
--------------------------------------------------------------------------------

実行結果例2:

https://man7.org/linux/man-pages/man3/malloc_trim.3.html で説明されている malloc_trim() という関数を呼び出すことでOSに返却可能なメモリを返却することが可能になります。しかし、ソースコードに対する改修が必要となるため簡単に採用できるとは限りません。

--------------------------------------------------------------------------------
[root@localhost ~]# ./rss 1
0       +4122392 KB
        RSS: 4123356 KB
        -4122392 KB
        RSS: 1376 KB
1       +4182160 KB
        RSS: 4183280 KB
        -4182160 KB
        RSS: 1376 KB
2       +4130460 KB
        RSS: 4131756 KB
        -4130460 KB
        RSS: 1376 KB
3       +4241984 KB
        RSS: 4243340 KB
        -4241984 KB
        RSS: 1376 KB
4       +4198020 KB
        RSS: 4199232 KB
        -4198020 KB
        RSS: 1376 KB
5       +4392688 KB
        RSS: 4394072 KB
        -4392688 KB
        RSS: 1376 KB
6       +4246196 KB
        RSS: 4247444 KB
        -4246196 KB
        RSS: 1380 KB
7       +4039140 KB
        RSS: 4040316 KB
        -4039140 KB
        RSS: 1380 KB
8       +4346760 KB
        RSS: 4348104 KB
        -4346760 KB
        RSS: 1380 KB
9       +4369996 KB
        RSS: 4371196 KB
        -4369996 KB
        RSS: 1380 KB
10      +3937740 KB
        RSS: 3939036 KB
        -3937740 KB
        RSS: 1380 KB
11      +4044560 KB
        RSS: 4045792 KB
        -4044560 KB
        RSS: 1380 KB
12      +4193628 KB
        RSS: 4194892 KB
        -4193628 KB
        RSS: 1380 KB
13      +4061028 KB
        RSS: 4062356 KB
        -4061028 KB
        RSS: 1380 KB
14      +4529360 KB
        RSS: 4530592 KB
        -4529360 KB
        RSS: 1380 KB
15      +4101504 KB
        RSS: 4102700 KB
        -4101504 KB
        RSS: 1380 KB
16      +4254812 KB
        RSS: 4256008 KB
        -4254812 KB
        RSS: 1380 KB
17      +4335764 KB
        RSS: 4337904 KB
        -4335764 KB
        RSS: 1380 KB
18      +4118404 KB
        RSS: 4119732 KB
        -4118404 KB
        RSS: 1380 KB
19      +4049376 KB
        RSS: 4050680 KB
        -4049376 KB
        RSS: 1380 KB
--------------------------------------------------------------------------------

実行結果例3:

malloc_trim() を呼び出すことができない場合には、 dynamic mmap threshold を無効化するための環境変数( MALLOC_TRIM_THRESHOLD_ MALLOC_TOP_PAD_ MALLOC_MMAP_THRESHOLD_ MALLOC_MMAP_MAX_ の内の1個以上)を設定することで緩和する(OSに返却されないままとなる量を削減する)ことができます。

--------------------------------------------------------------------------------
[root@localhost ~]# MALLOC_TRIM_THRESHOLD_=131072 MALLOC_MMAP_THRESHOLD_=131072 ./rss
0       +4122392 KB
        RSS: 4123480 KB
        -4122392 KB
        RSS: 1476 KB
1       +4182160 KB
        RSS: 4183540 KB
        -4182160 KB
        RSS: 1476 KB
2       +4130460 KB
        RSS: 4131716 KB
        -4130460 KB
        RSS: 1476 KB
3       +4241984 KB
        RSS: 4243208 KB
        -4241984 KB
        RSS: 1564 KB
4       +4198020 KB
        RSS: 4199580 KB
        -4198020 KB
        RSS: 1564 KB
5       +4392688 KB
        RSS: 4394228 KB
        -4392688 KB
        RSS: 1564 KB
6       +4246196 KB
        RSS: 4247716 KB
        -4246196 KB
        RSS: 1564 KB
7       +4039140 KB
        RSS: 4040616 KB
        -4039140 KB
        RSS: 1564 KB
8       +4346760 KB
        RSS: 4348128 KB
        -4346760 KB
        RSS: 1564 KB
9       +4369996 KB
        RSS: 4371344 KB
        -4369996 KB
        RSS: 1564 KB
10      +3937740 KB
        RSS: 3939268 KB
        -3937740 KB
        RSS: 1564 KB
11      +4044560 KB
        RSS: 4045940 KB
        -4044560 KB
        RSS: 1584 KB
12      +4193628 KB
        RSS: 4195188 KB
        -4193628 KB
        RSS: 1584 KB
13      +4061028 KB
        RSS: 4062456 KB
        -4061028 KB
        RSS: 1584 KB
14      +4529360 KB
        RSS: 4530728 KB
        -4529360 KB
        RSS: 1584 KB
15      +4101504 KB
        RSS: 4102740 KB
        -4101504 KB
        RSS: 1604 KB
16      +4254812 KB
        RSS: 4256256 KB
        -4254812 KB
        RSS: 1604 KB
17      +4335764 KB
        RSS: 4337368 KB
        -4335764 KB
        RSS: 1604 KB
18      +4118404 KB
        RSS: 4119776 KB
        -4118404 KB
        RSS: 1604 KB
19      +4049376 KB
        RSS: 4050732 KB
        -4049376 KB
        RSS: 1604 KB
--------------------------------------------------------------------------------

実行結果例4:

この挙動は RHEL7 以前の環境でもある程度再現するようです。以下のように約96MBのメモリの割り当て/解放を繰り返すプログラムを実行した場合、約63MBがOSに返却されないままの状態が発生することが観測できます。GBレベルでの差異ではないので見逃してしまうかもしれませんが、 malloc_trim() を呼び出すように修正したり環境変数を指定することにより緩和するという対処は有効なようです。

--------------------------------------------------------------------------------
[root@localhost ~]# uname -r
2.6.32-71.el6.x86_64
[root@localhost ~]# ls -l /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Aug 18 19:13 /lib64/libc.so.6 -> libc-2.12.so
[root@localhost ~]# gcc -Wall -O2 -x c - -o rss << EOF
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> #include <malloc.h>
>
> static void show_rss(void)
> {
>         static FILE *fp = NULL;
>         static char buffer[4096];
>         char *cp;
>         unsigned long rss;
>         if (!fp) {
>                 fp = fopen("/proc/self/status", "r");
>                 if (!fp)
>                         return;
>         }
>         rewind(fp);
>         buffer[fread(buffer, 1, sizeof(buffer) - 1, fp)] = 0;
>         cp = strstr(buffer, "VmRSS:");
>         if (cp && sscanf(cp + 6, "%lu", &rss) == 1)
>                 printf("\tRSS: %lu KB\n", rss);
> }
>
> int main(int argc, char *argv[])
> {
>         static char *buf[3];
>         int i, j, k;
>         srand(0);
>         for (k = 28; k <= 32; k++) {
>                 unsigned long sum = 0;
>                 for (i = 0; i < 3; i++) {
>                         unsigned int size = (k + i) * 1048576 - (random() % 16) * 4096;
>                         char *cp = malloc(size);
>                         if (!cp)
>                                 return 1;
>                         for (j = 0; j < size; j += 4096)
>                                 cp[j] = 1;
>                         sum += size;
>                         buf[i] = cp;
>                 }
>                 printf("%d\t+%lu KB\n", k, sum / 1024);
>                 show_rss();
>                 for (i = 0; i < 3; i++)
>                         free(buf[i]);
>                 printf("\t-%lu KB\n", sum / 1024);
>                 if (argc > 1)
>                         malloc_trim(0);
>                 show_rss();
>         }
>         return 0;
> }
> EOF
[root@localhost ~]# ./rss
28      +89000 KB
        RSS: 89480 KB
        -89000 KB
        RSS: 520 KB
29      +92084 KB
        RSS: 92604 KB
        -92084 KB
        RSS: 30208 KB
30      +95108 KB
        RSS: 95632 KB
        -95108 KB
        RSS: 31204 KB
31      +98168 KB
        RSS: 98692 KB
        -98168 KB
        RSS: 64944 KB
32      +101312 KB
        RSS: 133496 KB
        -101312 KB
        RSS: 64944 KB
[root@localhost ~]# ./rss 1
28      +89000 KB
        RSS: 89476 KB
        -89000 KB
        RSS: 520 KB
29      +92084 KB
        RSS: 92604 KB
        -92084 KB
        RSS: 524 KB
30      +95108 KB
        RSS: 95632 KB
        -95108 KB
        RSS: 524 KB
31      +98168 KB
        RSS: 98692 KB
        -98168 KB
        RSS: 524 KB
32      +101312 KB
        RSS: 101836 KB
        -101312 KB
        RSS: 524 KB
[root@localhost ~]# MALLOC_TRIM_THRESHOLD_=131072 MALLOC_MMAP_THRESHOLD_=131072 ./rss
28      +89000 KB
        RSS: 89500 KB
        -89000 KB
        RSS: 528 KB
29      +92084 KB
        RSS: 92612 KB
        -92084 KB
        RSS: 528 KB
30      +95108 KB
        RSS: 95636 KB
        -95108 KB
        RSS: 528 KB
31      +98168 KB
        RSS: 98696 KB
        -98168 KB
        RSS: 528 KB
32      +101312 KB
        RSS: 101840 KB
        -101312 KB
        RSS: 528 KB
--------------------------------------------------------------------------------
posted by 熊猫さくら at 19:33| Comment(0) | TrackBack(0) | Linux

2024年02月04日

syzbot が発見した Linux カーネルの不具合の内、修正されたことが報告された件数が2/2に5000件になりました。

今回も簡単に集計してみました。

4000 件時点では 511 人の開発者により 2831 個のパッチが適用されたのに対して、 5000 件時点では 605 人の開発者により 3620 個のパッチが適用されました。

Eric Dumazet さんは 1000 件中 96 件を修正して、1位を独走中です。熊猫は 1000 件中 33 件を修正して、現在2位のようです。

$ wget -q https://I-love.SAKURA.ne.jp/syzbot-5000.html
$ awk ' { for (i = 1; i <= NF; i++) if (length($i) == 12) { print $i } }' syzbot-5000.html | awk '/[0-9a-f]{12}/{ print $0 }' | sort -u > /tmp/patches.txt
$ wc -l /tmp/patches.txt
3620 /tmp/patches.txt
$ for i in `cat /tmp/patches.txt`; do git show $i; done | grep ^Author: > /tmp/authors.txt
$ awk '{ $1 = ""; $NF=""; count[$0]=count[$0]+1 } END { for (key in count) { printf("%4d %s\n", count[key], key) } }' /tmp/authors.txt | sort -nr | wc -l
605
$ awk '{ $1 = ""; $NF=""; count[$0]=count[$0]+1 } END { for (key in count) { printf("%4d %s\n", count[key], key) } }' /tmp/authors.txt | sort -nr | head -n 22
 494  Eric Dumazet
 122  Tetsuo Handa
 104  Cong Wang
  89  Takashi Iwai
  80  Eric Biggers
  74  Jens Axboe
  73  Pavel Skripkin
  60  Florian Westphal
  58  Jan Kara
  54  David Howells
  51  Xin Long
  50  Johannes Berg
  50  Alan Stern
  43  Pavel Begunkov
  34  Oliver Neukum
  33  Paolo Abeni
  33  Daniel Borkmann
  31  Sean Christopherson
  29  Willem de Bruijn
  28  Theodore Ts'o
  28  Ryusuke Konishi
  28  Christoph Hellwig

ロックに起因した不具合は本当に厄介です。不完全なパッチが適用されてしまったり、メンテナがパッチを受理してくれなかったりもします。

自分がメンテナになっている領域の不具合ならば自分の判断で適用できるのですが、熊猫は自分がメンテナになっていない領域の不具合を修正している(そもそも自分が担当している領域での不具合はほとんど存在しない)ので、正しいパッチを作成するための苦労だけでなく、作成したパッチを受理してもらうための苦労もすごいです。せっかくパッチを作成しても、受理されないことで未修正のままになってしまうこともよくあります。

一例として、CVE-2023-31082があります。コンソールにログインしたユーザによる local DoS 脆弱性程度ならば、いちいち CVE 番号を申請していないのですが、誰かがこの不具合に CVE 番号を申請したようですね。この不具合は syzkaller を使っているユーザにより 2023/4/18 に報告されたようです。この脆弱性の緩和策を意図したかどうかは不明ですが、 2023/7/31 に、コンソールにログイン可能なユーザなら誰でもこの脆弱性を攻撃できるという状態から、 root ユーザだけがこの脆弱性を攻撃できるという状態にするためのパッチが提案され、採用されたようです。

しかし、同じ原因の不具合は少なくとも 2022/12/02 の時点で syzbot により報告されており、 syzbot が報告してから人間が再報告するまで4か月以上放置されていたことが判ります。

さらに遡ってみると、 2022/8/26 の時点で syzkaller を使っているユーザにより報告されていました。この時はスピンロックをミューテックスに置換するという修正が行われました。しかし、見落としがあったことが syzbot により報告され、この修正は取り消されました。

2024/1/18 に syzbot が再報告してきたのを受けて、熊猫がパッチを作成したのですが、今度はメンテナから「ファジングテストで見つかった不具合を修正する必要あるの?」と待ったをかけられてしまいました。(泣)

2022/8/26 の報告が最初かどうか判りませんが、なんだかんだで、この不具合は発見されてから少なくとも17か月経過しても修正されていないようです。

ファジングツールである syzkaller を使って見つかった不具合は、人間が報告した場合には対処されるけれども、 syzbot が報告した場合には無視されるという感じになりつつあります。5年ほど前の記事に、「syzbotの能力が向上することで、ますますOSカーネル開発者たちがsyzbotに追い回されるようになるという説も…?」と書きましたが、その通りの状況になってしまって、逆にカーネル開発者たちが無視するようになってきたということかもしれません。

posted by 熊猫さくら at 20:21| Comment(0) | TrackBack(0) | Linux

2023年06月15日

2回目の Google Open Source Peer Bonus を受賞しました。

熊猫が Linux カーネルに出会ってから20年が経過した今春、2回目の Google Open Source Peer Bonus を受賞しました。

前回受賞時の貢献内容は紹介ページを用意できましたが、その後の4年間は Linux カーネルの不具合を地道に修正しているだけなので紹介ページはありません・・・って、これだけで終わってしまうと物足りないので、熊猫の仕事内容について紹介しようと思います。

近年の仕事内容についてですが、関係者が見落としている内容に関して、いちはやく気づいて先回りして対処することにより問題が顕在化することを予防したり、普通のサポートレベルでは解決できずに泣き寝入りしてしまうような事象に関して、報告して修正を依頼できるようになるまで切り分け調査をすることにより利用者と開発者の間を繋いだりするという仕事をしています。例えば、ウイルス対策ソフトのオンアクセススキャン処理による遅延が犯人であることを立証したり、悪意のない通信がIPS/WAFにより遮断されてしまうという問題の回避策を実装したりとかしています。
受賞対象となった Linux カーネルの不具合を修正する活動についても、 syzbot が発見した問題事象についての切り分け調査を行い、修正パッチを作成して関係者の承認を得ていくという点で、同様なのです。様々な発生パターンを見てきたことで、 syzbot が発見した問題事象だけを修正するのではなく、類似事象についても誰かが発見する前に先回りして対処できるようになってきました。知らない間に、ペンギンさんを守護する猫になりつつあるのかもしれません。

熊猫のスキルは、「どんな製品やシステムでもある程度使える」とか「特定の製品やシステムを使いこなせる」とかいうスキルではないので、検索でヒットするような名称が見当たらず、対外的な宣伝ページや社内向けスキルシートを作成する場合にどう説明したらよいのか悩んでしまいます。
情報処理安全確保支援士も LinuC 303 セキュリティも、熊猫のスキルを表現できているとは思えません。熊猫のスキルを表現することができる名称は無いものでしょうかねぇ・・・?

posted by 熊猫さくら at 20:12| Comment(0) | TrackBack(0) | Linux

2022年10月01日

まもなく Linux 6.0 がリリース。不具合の修正状況は・・・?

syzbot が発見した Linux カーネルの不具合の内、修正されたことが報告された件数が9/15に4000件になりました。

簡単な集計をしてみたところ、 511 人の開発者により 2831 個のパッチが適用され、熊猫は 89 個で第3位のようです。

$ wget -q https://I-love.SAKURA.ne.jp/syzbot-4000.txt
$ awk ' { for (i = 1; i <= NF; i++) if (length($i) == 12) { print $i } }' syzbot-4000.txt | awk '/[0-9a-f]{12}/{ print $0 }' | sort -u > /tmp/patches.txt
$ wc -l /tmp/patches.txt
2831 /tmp/patches.txt
$ for i in `cat /tmp/patches.txt `; do git show $i; done | grep ^Author: > /tmp/authors.txt
$ awk '{ $1 = ""; $NF=""; count[$0]=count[$0]+1 } END { for (key in count) { printf("%4d %s\n", count[key], key) } }' /tmp/authors.txt | sort -nr | wc -l
511
$ awk '{ $1 = ""; $NF=""; count[$0]=count[$0]+1 } END { for (key in count) { printf("%4d %s\n", count[key], key) } }' /tmp/authors.txt | sort -nr | head -n 20
 398  Eric Dumazet
 100  Cong Wang
  89  Tetsuo Handa
  85  Takashi Iwai
  75  Eric Biggers
  71  Pavel Skripkin
  64  Jens Axboe
  56  Florian Westphal
  47  Xin Long
  41  Alan Stern
  40  Johannes Berg
  37  Pavel Begunkov
  35  David Howells
  34  Oliver Neukum
  33  Jan Kara
  28  Paolo Abeni
  27  Daniel Borkmann
  26  Willem de Bruijn
  25  Sean Christopherson
  25  Johan Hovold

修正件数上位10名が作成したパッチについて統計情報を調べてみると、 Eric Dumazet さんと Cong Wang さんと Florian Westphal さんと Xin Long さんはネットワーク関連のパッチを、 Takashi Iwai さんはサウンド関連のパッチを、 Jens Axboe さんはブロック関連のパッチを、 Alan Stern さんはUSB関連のパッチを、熊猫と Eric Biggers さんと Pavel Skripkin さんは様々な分野のパッチを作成しているようです。

$ mkdir /tmp/authors/
$ for commit in `cat /tmp/patches.txt`; do git show $commit | awk ' { if (NR == 2) { $1 = ""; $NF = ""; AUTHOR = $0 } else if (NR > 3) { print $0 >> "/tmp/authors/"AUTHOR } } '; done
$ for author in /tmp/authors/*; do basename "$author"; diffstat -p1 "$author"; echo; done > /tmp/diffstat.txt

syzbot に報告されずに修正された不具合も存在しているため、誰も正確な件数は把握していないのですが、修正されたファイルの数としては、ネットワーク関連が一番多く、ドライバ関連が続くという、予想外の結果になっています。
ネットワーク関連の処理では、不具合を恐れずに適用してみて、不具合が見つかったら修正すればよいという考え方で開発が行われてきたということなのかもしれませんね。

syzbot による不具合探しが始まってから5年以上が経過し、自組織の環境で syzkaller を動作させるなど、不具合の発見と修正に関わるカーネル開発者も増えてきている感じがします。
複数の開発者が同じ不具合を修正するパッチを作成してしまうケースも見られるようになってきており、誰のパッチが採用されるかという一番乗り競争っぽくなってきました。

posted by 熊猫さくら at 16:43| Comment(0) | TrackBack(0) | Linux

2020年10月29日

OSSセキュリティ技術ワークショップ2020で syzbot の話をしました。

今年も相変わらず Linux カーネルのバグ退治をしています。

昨年講演した The SYZBOT CTF の「その後」ですが、1年半前の記事に書いた予想が現実になりつつある気がするので、「 syzbot に追い回される開発者たち?」というタイトルで苦労話を紹介しました。

25年前に追加されたコードに起因した不具合が発見されて、もう誰も使っていないだろうという理由で削除するとか、歴史を感じますね。

posted by 熊猫さくら at 01:04| Comment(0) | TrackBack(0) | Linux

2020年04月11日

Pacemaker で softdog を利用されている方へのお知らせ

kdump の設定をしているのであれば、 softdog カーネルモジュールをロードする際のパラメータに soft_panic=1 という指定を追加していただきますようお願いします。

■お知らせの背景

Pacemaker を使っているシステムが突然再起動したというお問合せは少なくありません。具体的な件数については把握していませんが、仮想化環境での発生事例が多いようです。残念なことに、ログファイルや sar を調査しても手がかりが残っておらず、原因不明のまま諦めざるを得ないという状態が何年も続いてきました。

RHEL6/CentOS6 以降の softdog カーネルモジュールには、タイムアウトの発生時に「システムを再起動させる」代わりに「カーネルパニックを発生させる」ための soft_panic というパラメータが存在しています。このパラメータを kdump の設定と組み合わせることで、タイムアウトの発生時に vmcore を取得することが可能になります。

vmcore にはタイムアウトが発生した時点の情報しか含まれないので、 vmcore を取得できればタイムアウトの原因を必ず突き止められるという訳ではありません。しかし、たくさんの事例を積み重ねることで、傾向を分析して対策を考えることはできるものと考えます。

■分析により見つかった問題点の例

Corosync main process was not scheduled for X ms (threshold is Y ms). Consider token timeout increase. というメッセージは、一般的には「カーネルが corosync プロセスにCPU時間を割り当てできなかった」という解釈/説明がされていますが、必ずしもカーネルやハードウェア側の問題を示唆している訳ではありません。1個のスレッドが様々な処理を行っているという corosync プロセスの構造上、「(カーネルは corosync プロセスにCPU時間を割り当てていたものの、 corosync プロセスが他の処理で忙しかったことにより、)このメッセージを出力するための関数を呼び出せるようになるまでに想定外の時間がかかっただけ」という可能性もあるのです。

このような問題が実際の業務負荷で発生しているのかどうかは、 vmcore を取得して確認してみないと判断できません。サポートセンタでは実際の業務負荷をかけることができないので、利用者の方々に vmcore の取得へのご協力をお願いする必要があります。

■NTT OSSセンタを利用されている方への補足

vmcore を送付できる場合には、サイズが数十GBのファイルであってもそのまま扱える「ファイルアップロード/ダウンロードシステム」をご利用ください。 vmcore を送付できない場合、あるいは、送付にかかる時間が勿体ないので自前で初期解析を行いたい場合には、 crash コマンドを用いて vmcore の初期解析を行う手順をご利用ください。詳細については、(NTT OSSセンタの契約者向けコンテンツである)「ナレッジの泉」を参照してください。

posted by 熊猫さくら at 14:31| Comment(0) | TrackBack(0) | Linux

2020年04月07日

RHEL7/CentOS7 で最新版 clamav のオンアクセススキャン機能を利用する手順について

先月 clamav パッケージが 0.102 にバージョンアップした際に、 RHEL 7 / CentOS 7 で提供されている curl のバージョンが古すぎることが原因で、 RHEL 7 / CentOS 7 向けの clamav におけるオンアクセススキャン機能が「突然」使えなくなってしまいました。

オンアクセススキャン機能を必要としている人は、業務で使っている人が多いと思うので、今すぐ RHEL 8 / CentOS 8 に乗り換えるという選択肢は難しいと思います。なので、既存環境への影響を最小限に抑えながら、 RHEL 7 / CentOS 7 環境でもオンアクセススキャン機能を使えるようにするための手順を作りました。

(1) ビルドに必要なパッケージを開発環境にインストールします。

# yum -y install gcc rpm-build yum-utils wget epel-release

(2) curl の最新版(記事作成時点では 7.69.1 )を開発環境でコンパイルします。

# wget https://curl.haxx.se/download/curl-7.69.1.tar.bz2 \
       https://curl.haxx.se/download/curl-7.69.1.tar.bz2.asc \
       https://daniel.haxx.se/mykey.asc
# gpg --import mykey.asc
# gpg curl-7.69.1.tar.bz2.asc
# tar -xf curl-7.69.1.tar.bz2
# cd curl-7.69.1
# ./configure --prefix=/usr/local/curl-7.69.1
# make
# make install

(3) clamav の最新版(記事作成時点では 0.102.2-4.el7 )を開発環境でコンパイルします。

# cd
# rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
# yumdownloader --source clamav
# rpm --checksig clamav-0.102.2-4.el7.src.rpm
# rpm -ivh clamav-0.102.2-4.el7.src.rpm
# yum-builddep -y ~/rpmbuild/SPECS/clamav.spec
# patch -d ~/rpmbuild/SPECS/ -p0 << "EOF"
--- clamav.spec
+++ clamav.spec
@@ -4,7 +4,7 @@

 ## Fedora Extras specific customization below...
 # EL7's curl is too old
-%if 0%{?fedora} || 0%{?rhel} >= 8
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %bcond_without  clamonacc
 %else
 %bcond_with     clamonacc
@@ -264,6 +264,7 @@ export have_cv_ipv6=yes
 rm -rf libltdl autom4te.cache Makefile.in
 autoreconf -i
 %configure \
+    --with-libcurl=/usr/local/curl-7.69.1 \
     --enable-milter \
     --disable-clamav \
     --disable-static \
EOF
# rpmbuild -bb ~/rpmbuild/SPECS/clamav.spec

(4) 開発環境で作成したファイルを本番環境にコピーします。なお、開発環境と本番環境が同一の場合は不要です。

# tar -cf ~/curl+clamav.tar /usr/local/curl-7.69.1/ ~/rpmbuild/RPMS/x86_64/clamd-*.rpm \
          ~/rpmbuild/RPMS/x86_64/clamav-*.rpm ~/rpmbuild/RPMS/noarch/clamav-*.rpm
# tar -xf ~/curl+clamav.tar -C /

(5) 本番環境で clamav パッケージをインストールします。

# rpm -ivh ~/rpmbuild/RPMS/x86_64/clamd-0.102.2-4.el7.x86_64.rpm \
           ~/rpmbuild/RPMS/x86_64/clamav-0.102.2-4.el7.x86_64.rpm \
           ~/rpmbuild/RPMS/x86_64/clamav-lib-0.102.2-4.el7.x86_64.rpm \
           ~/rpmbuild/RPMS/noarch/clamav-data-0.102.2-4.el7.noarch.rpm \
           ~/rpmbuild/RPMS/noarch/clamav-filesystem-0.102.2-4.el7.noarch.rpm

(6) clamonacc が開発環境でコンパイルした curl ライブラリを参照する設定になっていることを確認します。

# objdump -p /usr/bin/clamonacc | grep curl
  NEEDED               libcurl.so.4
  RPATH                /usr/local/curl-7.69.1/lib

(7) 本番環境で必要な設定をしてから clamonacc を起動します。

# sed -i -e 's/^#TCP/TCP/' /etc/clamd.d/scan.conf
# systemctl start clamd@scan
# /usr/bin/clamonacc
posted by 熊猫さくら at 13:28| Comment(0) | TrackBack(0) | Linux

2020年01月06日

SECCON CTF 2019 Final での syzbot panic クイズについて

2019年は熊猫にとって syzbot イヤーだったので、CTFの Jeopardy 問題として1問だけ出題させていただきましたが、いや〜、ここまで酷評されるとは思っていませんでしたよ。(笑)

出題したのは以下の問題です。表面的には、目的のページやパッチを如何に効率よく見つけられるかを競う出題なのですが、その裏には気づいて/考えてもらいたい話を含めていました。

Solve 5 questions listed below, and submit concatenated answers in SECCON{$answer_for_Q1+$answer_for_Q2+$answer_for_Q3+$answer_for_Q4+$answer_for_Q5} format.
Hint: Total length of answer string in SECCON{$answer_for_Q1+$answer_for_Q2+$answer_for_Q3+$answer_for_Q4+$answer_for_Q5} format will be 73 characters.

(Q1) What is FQDN (in all lower characters) of a website that manages the state of bugs syzbot has found?

(Q2) What is SHA-1 (only first 10 characters, in lower hexadecimal format) of a commit that explains the following improvement?

syzbot was unable to parse Linux kernel's messages when multiple threads concurrently emitted kernel messages. As a result, many reports had been discarded as "corrupted" indicating that "something went wrong but syzbot could not understand what has happened". After this patch was merged, ability to understand what has happened improved significantly. This patch was written by Tetsuo Handa.

(Q3) What is SHA-1 (only first 10 characters, in lower hexadecimal format) of a commit that fixed the following problem?

A local unprivileged user was able to trigger soft lockup using a reproducer shown below. This patch was written by Linus Torvalds.

----------------------------------------
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>

static void *thr(void *arg)
{
        char c;
        read(*(int *) arg, &c, 1);
        return 0;
}

int main(int argc, char *argv[])
{
        char buf[sizeof(struct termios) + 64] = { };
        int zero = 0;
        int ptyno = 0;
        int fd = open("/dev/ptmx", O_RDONLY);
        int fd2;
        pthread_t th;
        buf[0x9] = 0xfd;
        buf[0xc] = buf[0xd] = buf[0xe] = buf[0xf] = buf[0x10] = 0xff;
        ioctl(fd, TCSETS, buf);
        ioctl(fd, TIOCSPTLCK, &zero);
        ioctl(fd, TCXONC, TCIOFF);
        if (ioctl(fd, TIOCGPTN, &ptyno))
                return -1;
        sprintf(buf, "/dev/pts/%d", ptyno);
        fd2 = open(buf, O_RDONLY);
        pthread_create(&th, 0, thr, &fd2);
        sleep(1);
        ioctl(fd2, FIONREAD, buf);
        return 0;
}
----------------------------------------

(Q4) What is SHA-1 (only first 10 characters, in lower hexadecimal format) of a commit that fixed the following problem?

Since passing arbitrary arguments to system calls as root user causes legitimate problems (e.g. hang up, reboot), fuzzers are currently forced to blacklist some arguments that legitimately damage the target. In April 2018, syzbot generated a report which confused developers because it was believed that arguments which are known to generate this report are blacklisted. But it turned out that syzbot was by error passing arguments which should have been blacklisted during the fuzz testing.

(Q5) What is SHA-1 (only first 10 characters, in lower hexadecimal format) of a commit that explains the following improvement?

Currently, syzbot might by error generate C reproducer programs using incorrect structure definition (e.g. "struct serial_struct"). Therefore, a utility to validate correctness of structure definition was added.

正解は SECCON{syzkaller.appspot.com+15ff2069cb+966031f340+06c33b3af0+64ca0a3711} です。

1問目は、2問目以降を解いていくためのヒントとなるページへの誘導です。 syzbot でググれば一発でヒットするでしょう。

2問目は、ファジングツールにとって悩みの種であった、カーネルメッセージを読みやすくする改善の話題です。1問目で syzbot というキーワードでググった際に syzkaller.appspot.com を見つけて、逃げるsyzbot、追うカーネル開発者たちをスルーしてしまったチームにとっては、目的のパッチを見つけるまでにちょっと回り道してもらうことになったのではないかと思います。
不具合退治の舞台裏に興味を持ってもらいたくて、出題しました。詳細は The SYZBOT CTF を参照いただければと思います。

3問目は、ファジングツールは、 root ユーザの権限が無くてもシステムをダウンさせてしまうことのある不具合を見つける場合があるということを知ってもらう問題です。発見される不具合の多くは root ユーザの権限が無いと遭遇しないのですが、たまに一般ユーザの権限でも遭遇できてしまう不具合が発見されることがあります。そのため、「このシステムでは管理者権限を与えないから、ファジングテストで見つかった不具合を心配しないで大丈夫」と問題を甘く見ないことが求められます。この問題は、 syzbot が見つけたC言語の再現プログラムを、熊猫が単純化して security@kernel.org に投げて、非公開の議論を経て修正されました。

4問目は、ファジングで使うテストケース作成の難しさについて知ってもらう問題です。 root ユーザの権限があれば不具合を攻撃しなくても合法的にシステムを停止させることができてしまうので、合法的にシステムを停止させてしまうような操作は、テストケースから除外するという対処をしています。この問題ではテストケースから除外しているつもりが除外できていなかったという話題を扱いましたが、実際にはテストケースから除外することは容易ではありません。テストケースから除外することなく対処する方法として、テストケースを root ユーザの権限ではなく一般ユーザの権限で動作させるという方法も提案されていますが、多くの不具合を発見できなくなってしまいます。
そのため、ファジングテストのためのカーネルでは、合法的にシステムを停止させてしまうような操作をカーネル側で拒否できるようにするという提案が現在進行中です。

5問目は、ファジングの凄まじさを知ってもらう問題です。 syzbot は、C言語で記述された再現プログラムを提示してくれるのですが、熊猫が問題を解析しようとして再現プログラムの「読める化(ローカル変数や構造体などを用いて書き直す)」作業をしたところ、構造体を用いると問題が再現しなくなるという不思議な現象に遭遇しました。調査したところ、 syzbot が使用している構造体の定義が間違っている(構造体を間違えた状態のままクラッシュさせることが可能なパラメータの組み合わせを見つけてしまった)という予想外の出来事がありました。構造体のメンバの役割を知らずとも動作できるということなんですね。

出題には至りませんでしたが、12月の出来事として、クラッシュ発生回数トップの座に君臨していた不具合である unregister_netdevice: waiting for DEV to become free (2) が open 状態から fix pending 状態に遷移しました。(注:異なる不具合がこの事象として報告されているため、実際にはまだ終わっていません。)
不具合を修正するパッチには、不具合を報告してくれた人へのクレジット(謝意)として、報告者の名前を含めることが期待されるのですが、パッチの作成者は、必ずしも報告者の名前を含めてくれるとは限りません。その結果、 syzbot が発見した不具合が修正済みなのかどうかを追跡することが難しいという問題があります。この不具合の場合、グーグルさんのボットが見つけた不具合なのに、何故かファーウェイさんのボットが見つけたことにされているパッチに対して「誰?」ってツッコミを入れました。こんなところにも、グーグル対ファーウェイの競争が現れているのでしょうか?(笑)

さて、熊猫が扱っている領域って、CTFの攻防戦には不向きなんですよねぇ。いくつ発見できるか不明な未知の不具合を探したり、どれくらい修正が難しいか予想できない不具合を修正するパッチを書いたりしてもらうという出題にすると、誰も回答不能/出題者側も採点不能という状況に陥りかねません。可視化するためには機械的に採点が可能なフラグ文字列を使わざるを得ないのですが、扱う不具合の内容がプロセスの異常終了/ハングアップ/カーネルパニックなどである以上、フラグ文字列を表示させることや、現在誰が占有しているのかを判断することが非常に困難なのです。その結果、今回はパッチを探すという Jeopardy 問題になりました。CTF競技者にとっては、非常につまらない出題だったかと思いますが、 syzbot の世界は、頭がパニックになりそうなくらいに、予想外の出来事の連続なのです。

posted by 熊猫さくら at 19:24| Comment(0) | TrackBack(0) | Linux

2019年08月18日

セキュリティ・キャンプ2019で使用した、熊猫のテキストを公開しました。

熊猫がプログラミングに出会ってから25年となった今年は、不具合を修正できるようにするための試行錯誤について扱いました。

不具合を踏んでクラッシュした場合、メモリダンプを取得して原因を解析することが多いかと思います。しかし、 syzbot を用いたファジングテストでは、メモリダンプを取得することが(現時点では)不可能です。また、ハングアップやストールの場合は、仮にメモリダンプを取得できたとしても、時間経過に伴う状態変化をメモリダンプから知ることはできないという限界があります。そのため、 printf() デバッグに頼らざるを得ない訳ですが、複数のスレッドが同時に printf() を呼んでしまうことで複数のメッセージが混ざってしまい、個々のメッセージを解析できなくなるという問題もあります。

そんな厳しい状況で、問題が発生した時に必要な情報をいかにして取得できるようにするかという、とても複雑で面倒な問題に熊猫は取り組んできました。講義資料は、( Linux カーネルにおける printf() に相当する)printk() を巡る試行錯誤をメインに、メモリ管理サブシステム開発者との大バトルや、ある不具合が修正されたと思ったら別の不具合が発見されたりする沼など、盛りだくさんです。とても4時間では紹介しきれません〜。(笑)

今年は、担当講義の時間以外はZトラックにお邪魔していたため、思わず突っ込む側になってしまいました。熊猫はマルウェアを解析した経験はありませんが、講義の中で strace による動的解析が紹介されていたので、ちゃっかり AKARI による追跡手法を紹介することができました。

posted by 熊猫さくら at 20:10| Comment(0) | TrackBack(0) | Linux

2019年04月27日

Google Open Source Peer Bonus を受賞しました。

サポートセンタに勤務していた時に偶然発見してしまったメモリ管理の闇が原因で、現在でも Linux カーネルのバグハンティングを続けている訳ですが、ここ1年半ほど syzbot が見つけた不具合を退治していたところ、その貢献により Google Open Source Peer Bonus を受賞してしまいました。

受賞理由の1つでもある printk() の改善が来月公開される Linux 5.1 に含まれることから、今年のセキュリティ・キャンプ全国大会では、この体験談を The SYZBOT CTFとして語ることになっています。

3年前のセキュリティ・キャンプ全国大会で語った The OOM CTF では、受講者全員の思考が停止してしまうほどの脳内 Denial of Service 攻撃になってしまいました。
現在、今年の講義で取り上げるトピックを探すためにメールのアーカイブを読み返しているところなのですが、対象範囲がメモリ管理サブシステムだけでなく Linux カーネル全体となっています。そのため、今年は3年前を超える難易度となっており、果たして受講希望者が現れてくれるのかどうか心配しています。まずはキャンプに参加してくれないことには始まりませんので、たくさんの応募をお待ちしております。

posted by 熊猫さくら at 15:57| Comment(2) | TrackBack(0) | Linux

2018年08月21日

セキュリティ・キャンプ2018で使用した、熊猫のテキストを公開しました。

直ちにアップデートを適用するなどの対処を迫られるような影響範囲の大きな脆弱性が増えてきている感触がある昨今、自分が管理している Linux システムの概要を知っておくことは、障害や脆弱性への対処を行う上で有用であると考えます。

そこで、今年のキャンプでは、サポートセンタで故障解析に携わった経験を基に、 Linux システムに関するトラブルをOS視点で調査しながら、スムーズな問題解決を行うために必要なことについて扱いました。演習で使用した仮想マシンイメージ( SHA256: 0ab356f719031c64d798dd6802f04cf8f364089c8e9cdebd650aa6258e4bf20f )も公開していますので、実際に手を動かして体験してみてください。

ちなみに、同日に開催されたセキュリティ・コアキャンプ2018の方では、一昨年度の講義で扱ったメモリ管理の闇と、その後の2年間でどこまで進んだのかについて扱いました。振り返ってみると、「しょ〜もないミス」を繰り返しているんだなぁと感じます。

posted by 熊猫さくら at 20:11| Comment(0) | TrackBack(0) | Linux

2018年01月29日

Linux 4.15 が封印解除されました。

年明け早々 Meltdown/Spectre で大騒ぎになりましたが、 Meltdown/Spectre 対応としてみんなが一斉にカーネルのアップデートを行った結果、「アップデートしたらカーネルパニックやフリーズなどが発生するようになった」という不具合報告が大量に発生して、 Bugzilla や Launchpad が大変な状況になっているようです。
メモリ管理の闇と戦っていた熊猫も、今月ばかりは、アップデートに伴う不具合報告のうち、主に Meltdown/Spectre に起因しない不具合(アップデートして発生したから Meltdown/Spectre 対策の修正が原因かと思いきや、実は全然関係ない問題だったというケース)の切り分けや修正を手伝っていました。
でも、カーネルのトラブルについて情報を取得するのに不慣れな人がほとんどで、カーネルパニック/フリーズ時のメッセージを取得できていなかったり、バージョン情報など調査に必要な情報を提示してくれなかったりと、何が起きているのか不明な不具合報告が多かったのが残念です。
まだまだ、OSレベルのトラブルに対処するノウハウは共有できていないということなんですねぇ。

先月 CELF ジャンボリーで「メモリ管理の闇」について一緒に戦ってくれる協力者を募集する発表をした際の動画が公開されています。内容は、昨年度のセキュリティ・キャンプの講義のダイジェスト+その後 Linux 4.15 までにどこまで進んだかの報告です。
どうもメモリ枯渇時の挙動を考えていないコードが多いようで、メモリ枯渇時にハングアップするバグを退治していた筈が、次第にメモリ割り当てを伴う処理全般についてバグ退治をする羽目になってきています。発表後も、(年明け早々に遭遇して 4.15-rc9 の後に修正された)メモリ圧迫時にランダムなクラッシュを引き起こすバグなど、 4.15 が封印解除されるぎりぎりまで、いろいろなバグを退治していました。

さて、封印解除といえば「カードキャプターさくら」ですね。新編が始まって「はにゃ〜ん♪」状態です。
Linux「はにゃ〜ん♪」化計画というのがあるのですが、カードを捕まえるときの掛け声が「セキュア」というのは、 TOMOYO Linux が登場する前兆だったりするのでしょうか?(笑)

posted by 熊猫さくら at 20:15| Comment(0) | TrackBack(0) | Linux

2017年11月19日

Linux カーネルのメモリ管理サブシステムの5つの特徴

  1. メモリ割り当て処理が先に進むことを保証しない
  2. 問題が発生する可能性があっても現実に起こるまでは対処しない
  3. 故意/悪意あるいはストレステストにより発生する問題には対処しない
  4. 自力で真犯人を逮捕できない一般人は相手にしない
  5. 他のカーネル開発者の関心を惹かない問題は解決されない

「え〜っ!?」って思われましたでしょうか?でも、そういう世界なんだということが解ってきました。

Linux 4.9 で追加されてしまった warn_alloc() によるハングアップを「故意/悪意あるいはストレステストでなくても現実に起こる問題」として削除するのに丸1年かかりました。いやはや、疲れます。

「 Linux システムがハングアップしたらメモリ管理を疑え!?」という諺は、まだ有効です。意図的なストレスを掛けることで発生する問題は全力で無視されてしまうため、実際のシステムで使われている負荷を掛けることで発生することが重要です。そのためには、利用者の皆様のご理解・ご協力が欠かせません。情報の取得方法を習得して、ハングアップを見つけたら、どんどん報告してくださいますよう、お願いいたします。

posted by 熊猫さくら at 17:08| Comment(0) | TrackBack(0) | Linux

2017年10月24日

Apache HTTP Server 2.4.29 で修正された Out Of Memory 自爆バグについて

仕事上、例えば vmcore のような、数ギガバイトから数十ギガバイトになる可能性のある巨大なファイルを転送する必要に迫られることがあります。しかし、セキュリティ上の理由でインターネット上のサービスを利用できないという制約があるため、いっそのこと自作してしまえと思い、大容量ファイルのアップロード/ダウンロードを行うためのプログラムを試作することにしました。その際、スクリプト言語などに依存してしまうとEOLへの対応が必要になるため、10年20年先も無改造で使い続けることができるであろうC言語で、そして、メモリリークや Web サーバ本体のバージョンアップの影響を受けないようにするため、リクエスト毎に fork()/execve() する Apache のCGIとして動作するプログラムとして開発することにしました。

しかし、たまにリクエストがエラーになったり、システムの動作が極端に遅くなったりと、どうも様子がおかしいことがありました。発生条件を切り分けていったところ、何と「スリープ処理を入れずに大量のレスポンスの送信を行うだけ」で発生していることが判明しました。親プロセスである Apache のワーカープロセスが、子プロセスであるCGIプログラムの出力を全てメモリ上に保持しようとしてしまい、 OOM killer に殺されるか、さもなくば、後続のリクエストに対する fork() ができなくなるというサービス拒否状態に陥っていたのです。

おいおい、メモリ管理で悩みたくないから子プロセスを使うという選択をしたのに、親プロセスが盛大にメモリを食い潰してどうするのよ〜?

/〜(^x^)〜/

ちなみに、RHELへのバックポートは httpd-2.4.6-76.el7 に含まれる予定になっているため、 RHEL 7.5 のリリース時に修正されるものと予想しています。

posted by 熊猫さくら at 22:31| Comment(0) | TrackBack(0) | Linux

2017年08月20日

セキュリティ・キャンプ2017で使用した、熊猫のテキストを公開しました。

昨年あたりからLSMを巡る動きが活発になってきたので、今年のセキュリティ・キャンプでは、 ウイルス対策ソフトをLSMから使うという 実習を行いました。

3年間のトラブルシューティングと、その後2年間のメモリ管理を巡る戦いにより、LSMの動向を5年間全然追いかけていなかったため、紺屋の白袴状態でした。 そのため、当初はLSMメーリングリストのアーカイブを読み直して、どのような挑戦が行われてきたのかを纏めてみるつもりでした。しかし、今年に入ってからも 「 Linux カーネルのメモリ管理機構の闇」を巡る死闘が続いており、リソースの大部分がメモリ管理機構のバグ退治に費やされてしまうという、ブラックホールから 逃げ出せない状況下での講義準備となりました。講義資料の中に OOM killer の話が何度も出てきたのは、そのせいでしょうか?(笑)

今年のキャンプでは、 DirtyCOW の件で巻き添えを喰らったり WikiPedia からブログリンク+本名暴露攻撃を喰らったりと有名ながちゃぴん先生とか、 Ryzen SEGV Battleという貴重な経験をした Sat 先生とかに会うことができて、楽しかったです。

posted by 熊猫さくら at 15:09| Comment(0) | TrackBack(0) | Linux

2016年12月31日

Linux 4.9 が封印解除されたけれども・・・

今年はずっと「 Linux カーネルのメモリ管理機構の闇」と戦い続けてきました。

Linux カーネルのメモリ管理機構は「他の誰かが自分のために進捗を出してくれているから自分は余計なことを考えなくていい」という楽観論で動いています。そして、全員が同じ考えで動いたとき、誰も進捗を出せなくなり、システムは静かにハングアップしてしまいます。なんだか、責任の所在が不明な、某市場移転問題みたいですねぇ。

Linux 4.6 で OOM reaper が導入され、 Linux 4.9 では「 OOM killer が発動できる限りは OOM livelock 状態に陥らないことを証明できる」ようになる・・・ことを目指していました。しかし、誰も進捗を出せなくなったことを知らせてくれる仕組みとして、ストールしている間は10秒毎に警告を出力するという楽観的な修正が取り込まれたことにより、 Linux 4.8 までは存在しなかった「ロックを獲得したままバッファが空になるまで永遠に待ち続けるA v.s. ロックを獲得できないことでバッファへの追加を永遠に続けるA以外の全員」という新しい OOM livelock 状態が発生してしまいました。よって、残念ながら「 OOM livelock 状態に陥らないこと」を証明できませんでした。

この問題は printk() がバッファが空になるまで永遠に待ち続けることが原因ということにされたため、バッファを空にする処理を専用のカーネルスレッドにオフロードすることで解消される見込みです。しかし、本当の原因は「ロックを獲得したままスリープしてしまうA v.s. ロックを獲得できないままビジーループをしてしまうA以外の全員」であるため、「ロックを獲得できなかった場合はスリープすることで、ロックを獲得しているAの処理を先へと進める」というのが正しい修正だと思うのですが、そのような修正を加えることにより予期せぬ副作用が発生することを恐れているため、採用される見込みはありません。問題を指摘しても、「 DoS 攻撃を受けていて手遅れだ」という返事。原因がカーネル側にあり、それを修正する方法が存在していても、想定を超える負荷が掛かったら諦めるしかないという、セキュリティとトラブルシューティングをやっている人の視点としては納得いかない世界なのです。こんな調子では、 DirtyCOW のような脆弱性が見つかるのも、当然かもなぁ。

ストールしている間は10秒毎に警告を出力するという楽観的な修正には、全員が同じ考えで動いた場合には機能しないという致命的な欠陥があり、問題が発生しているかもしれないことを知らせるという watchdog としての役割を果たせません。この処理を、専用のカーネルスレッドにオフロードすることで、全員が同じ考えで動いてしまった場合でも機能するようにするという提案を続けています。果たして、採用されるのでしょうか?

posted by 熊猫さくら at 11:34| Comment(0) | TrackBack(0) | Linux

2016年11月05日

セキュリティ・キャンプ2016で使用した、熊猫のテキストを公開しました。

RHEL 7.3 が封印解除されたので、自主規制を解除します。

今年のセキュリティ・キャンプでは、 Linux カーネルのメモリ管理機構の闇について扱いました。3年超の期間を費やし、3000通超の関連メールを送受信し、そして、今年1月からは会社の業務時間の大部分も使わせてもらいながら対応した、膨大な活動履歴の中から抽出したものです。

講義資料では、20年前から存在していたと考えられる脆弱性である CVE-2013-4312 および CVE-2016-2847 の発見から始まり、ずるずると闇に引き込まれていき、幾つかの問題について光を取り戻すまでを描いています。

CVE-2013-4312 および CVE-2016-2847 については RHEL 7.3 のカーネルで修正されていますので、信頼できないユーザがログインする可能性のあるシステムではカーネルをアップデートしてくださいね。先月、 DirtyCOW ( CVE-2016-5195 )への対処でカーネルをアップデートしたばかりだとは思いますが。

メモリ管理機構に起因したシステムのハングアップが発生しても、それを平均的なシステム管理者でも認知できる仕組みが存在しないため、どれくらいの頻度で発生しているのかについての情報はありません。サポートセンタに「今回のハングアップに関して、メモリ管理機構が原因の可能性はあるか?」と照会しても、「判断できない」としか答えられないのです。もし、「無い」とか「低い」とか回答するようなサポートセンタを見つけたら、この資料を見せてやってくださいな。(笑)

posted by 熊猫さくら at 13:30| Comment(0) | TrackBack(0) | Linux

2015年12月21日

環境変数 TZ の謎

とあるプログラムのパフォーマンス測定をしていたら、 localtime() 関数の処理で予想外に時間がかかっていることが判明しました。

そこで、 localtime() 、 localtime_r() 、 gmtime() の3つについて、どれくらいの時間がかかっているのかを簡単に計測してみました。

$ gcc -Wall -O3 -x c - << "EOF"
#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
        int i;
        time_t now = time(NULL);
        struct tm tm0 = { };
        struct tm *tm;
        if (argc == 3)
                for (i = 0; i < 100000000; i++)
                        tm = gmtime(&now);
        else if (argc == 2)
                for (i = 0; i < 100000000; i++)
                        tm = localtime_r(&now, &tm0);
        else
                for (i = 0; i < 100000000; i++)
                        tm = localtime(&now);
        printf("%04u/%02u/%02u %02u:%02u:%02u\n",
               tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
               tm->tm_min, tm->tm_sec);
        return 0;
}
EOF

localtime()localtime_r()gmtime()
$ time ./a.out$ time ./a.out 1$ time ./a.out 1 2

仮想化環境での簡単な計測なのであまり正確ではありませんが、確実に localtime() 関数は gmtime() 関数と比べて遅いようです。

localtime()localtime_r()gmtime()
real 2m6.615s
user 0m51.170s
sys 1m15.550s
real 0m38.278s
user 0m38.309s
sys 0m0.000s
real 0m3.995s
user 0m3.994s
sys 0m0.002s

この原因は、 localtime() 関数は呼び出しの度に、 /etc/localtime の内容が変化していないかどうかを確認するために stat() システムコールを発行しているためです。

CentOS 7 では /etc/localtime が ../usr/share/zoneinfo/Asia/Tokyo へのシンボリックリンクとなっているため、 stat() システムコールでの無駄を減らすために、 /etc/localtime を /usr/share/zoneinfo/Asia/Tokyo のコピーに置き換えて実験してみました。シンボリックリンクを辿らないで済む効果はあるようです。

localtime()localtime_r()gmtime()
real 1m22.785s
user 0m52.446s
sys 0m30.407s
real 0m37.940s
user 0m37.970s
sys 0m0.000s
real 0m3.951s
user 0m3.954s
sys 0m0.000s

次に、環境変数 TZ で zoneinfo ファイルの場所を指定( export TZ=:Asia/Tokyo )して実験してみました。すると、 stat() システムコールが発行されなくなくなった分、 localtime() 関数が速くなりました。

localtime()localtime_r()gmtime()
real 0m39.887s
user 0m39.919s
sys 0m0.002s
real 0m37.597s
user 0m37.629s
sys 0m0.000s
real 0m3.970s
user 0m3.971s
sys 0m0.002s

次に、環境変数 TZ でタイムゾーン情報を指定( export TZ=JST-9 )して実験してみました。すると、 localtime_r() 関数が gmtime() 関数と同じレベルまで速くなりました。

localtime()localtime_r()gmtime()
real 0m8.735s
user 0m8.740s
sys 0m0.003s
real 0m6.733s
user 0m6.738s
sys 0m0.000s
real 0m6.647s
user 0m6.651s
sys 0m0.001s

でも、ちょっと不思議なことが起きてますよね?何故か、 gmtime() 関数は環境変数 TZ にタイムゾーン情報が指定されていると遅くなってしまうようです。

posted by 熊猫さくら at 19:59| Comment(0) | TrackBack(0) | Linux

2015年08月18日

セキュリティ・キャンプ2015で使用した、熊猫のテキストを公開しました。

去年から今年にかけて、例えば Shell Shock 脆弱性のような、OSレイヤーでの重大な脆弱性が見つかり、セキュリティ意識の高い人たちの間では「 SELinux を使おうよ」という機運が高まっているのかもしれません。しかし、サポートセンターでの経験より、「 SELinux を使ってトラブルが起きても対処できない」という人たちも大勢いることが判りました。そこで、改めて「OSの挙動を知って、OSレイヤーのセキュリティについて考えてみよう」と思い、今年度は「 TOMOYO / AKARI / CaitSith ハンズオン」というテーマにしました。

読むためのPDF版コピペするためのテキスト版

今回のテキストは、事前学習資料部分と当日学習資料部分の2部構成になっています。

事前学習資料部分では、「講義で使う環境に慣れてもらう」ことを意図して、「 PXE ブートして sl コマンドが走る Linux 環境を作る」ことに挑戦していただきました。 Scientific Linux ならぬ、 SL Linux です。(これは、昨年度のキャンプの企業見学からの帰りのバスの中での雑談から思いついたネタです。)

当日学習資料部分では、 TOMOYO をメインライン化するまでのドタバタ劇とか、3年間 RHEL システムのトラブルシューティング業務に従事して痛感した組織の問題点とかのような、技術的に Linux システムに詳しくない人にも何かの役に立つ話を交えたいと思いました。また、緊急コラム「 bash 脆弱性( CVE-2014-6271 )の影響範囲の調査方法について」が掲載されました。で「あまりに dis りすぎたため、公開したら怒られそうな内容になってしまいました」と書いたように、一昨年/昨年のテキストについては公開することを躊躇っていましたが、責任をとらないお偉いさんたちが蔓延していく現在の日本の危機的状況を見て、「来年では間に合わない」と判断し、過去テキストも含めて全テキストへのリンクを含めることにしました。

当日学習で使用した VirtualBox 向けのVMイメージは、 http://jaist.dl.sourceforge.jp/caitsith/63583/ からダウンロードできます。(ただし、サイズが大きいので、1か月後くらいに削除するつもりです。テキストでは CentOS 6 / CentOS 7 / Ubuntu 14.04 の3つしか言及していませんが、受講者の中に Arch Linux ユーザが居ましたので、 Arch Linux 用のVMイメージも用意しました。)

キャンプの様子は http://togetter.com/li/859151 から察していただければと思います。他の講師の方々が公開された資料や、参加者の反応なども見つけることができます。

posted by 熊猫さくら at 01:41| Comment(0) | TrackBack(0) | Linux