Meltdown, Spectre 対応での性能測定

Meltdown, Spectre の対応で、メモリ空間の分離の処理が重くなるとして色々話題になっているので、深い意味はないけど自宅のマシンで UnixBench を比較。Ubuntu 16.04 on hp microserver Gen8 にて測定。

環境

CPU

$ cat /proc/cpuinfo  | grep 'model name'
model name      : Intel(R) Celeron(R) CPU G1610T @ 2.30GHz
model name      : Intel(R) Celeron(R) CPU G1610T @ 2.30GHz

カーネルバージョン

いずれも Ubuntu 標準のリポジトリの物

環境 カーネルバージョン
対応後 4.4.0-109-generic #132-Ubuntu SMP Tue Jan 9 19:52:39 UTC 2018 x86_64 x86_64 x86_64
対応前 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 x86_64 x86_64

結果

広く指摘されている通り、システムコールのオーバーヘッドが大きくなっていることがわかりやすく出ている。

試験名 対応前 対応後 対応後の相対値
System Call Overhead 3237.3 965.2 29.8%
Pipe Throughput 2913.0 1509.0 51.8%
Pipe-based Context Switching 1447.4 809.6 55.9%
Process Creation 1825.5 1570.2 86.0%
Execl Throughput 2016.8 1806.5 89.6%
Shell Scripts (8 concurrent) 2908.6 2739.7 94.2%
Shell Scripts (1 concurrent) 3129.3 2960.0 94.6%
File Copy 1024 bufsize 2000 maxblocks 2592.4 2519.9 97.2%
File Copy 4096 bufsize 8000 maxblocks 4940.0 4831.9 97.8%
File Copy 256 bufsize 500 maxblocks 1712.1 1687.9 98.6%
Double-Precision Whetstone 1229.2 1228.9 100.0%
Dhrystone 2 using register variables 4475.4 4592.7 102.6%

ログ

$ uname -a
Linux storage 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$ ./Run
make all
make[1]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
make[1]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com


1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

2 x Execl Throughput  1 2 3

2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

2 x File Copy 256 bufsize 500 maxblocks  1 2 3

2 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

2 x Process Creation  1 2 3

2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

2 x Shell Scripts (1 concurrent)  1 2 3

2 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: storage: GNU/Linux
   OS: GNU/Linux -- 4.4.0-104-generic -- #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017
   Machine: x86_64 (x86_64)
   Language: en_US.utf8 (charmap="UTF-8", collate="UTF-8")
   CPU 0: Intel(R) Celeron(R) CPU G1610T @ 2.30GHz (4589.4 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET, Intel virtualization
   CPU 1: Intel(R) Celeron(R) CPU G1610T @ 2.30GHz (4589.4 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET, Intel virtualization
   16:04:12 up 9 days, 16:38,  1 user,  load average: 1.91, 2.93, 2.06; runlevel 2017-12-29

------------------------------------------------------------------------
Benchmark Run: Mon Jan 08 2018 16:04:12 - 16:32:59
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       26624684.7 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     3379.6 MWIPS (9.9 s, 7 samples)
Execl Throughput                               3837.8 lps   (29.6 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        800558.6 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          238528.1 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1921655.9 KBps  (30.0 s, 2 samples)
Pipe Throughput                             1811369.4 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 162381.4 lps   (10.0 s, 7 samples)
Process Creation                               9848.2 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   8493.7 lpm   (60.1 s, 2 samples)
Shell Scripts (8 concurrent)                   1618.4 lpm   (60.1 s, 2 samples)
System Call Overhead                        3039305.9 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   26624684.7   2281.5
Double-Precision Whetstone                       55.0       3379.6    614.5
Execl Throughput                                 43.0       3837.8    892.5
File Copy 1024 bufsize 2000 maxblocks          3960.0     800558.6   2021.6
File Copy 256 bufsize 500 maxblocks            1655.0     238528.1   1441.3
File Copy 4096 bufsize 8000 maxblocks          5800.0    1921655.9   3313.2
Pipe Throughput                               12440.0    1811369.4   1456.1
Pipe-based Context Switching                   4000.0     162381.4    406.0
Process Creation                                126.0       9848.2    781.6
Shell Scripts (1 concurrent)                     42.4       8493.7   2003.2
Shell Scripts (8 concurrent)                      6.0       1618.4   2697.3
System Call Overhead                          15000.0    3039305.9   2026.2
                                                                   ========
System Benchmarks Index Score                                        1408.8

------------------------------------------------------------------------
Benchmark Run: Mon Jan 08 2018 16:32:59 - 17:01:45
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables       52227790.6 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     6760.5 MWIPS (9.9 s, 7 samples)
Execl Throughput                               8672.1 lps   (29.5 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks       1026579.6 KBps  (30.1 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          283351.3 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       2865226.5 KBps  (30.1 s, 2 samples)
Pipe Throughput                             3623761.5 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 578965.5 lps   (10.0 s, 7 samples)
Process Creation                              23001.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                  13395.6 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                   1744.5 lpm   (60.1 s, 2 samples)
System Call Overhead                        4856018.0 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   52227790.6   4475.4
Double-Precision Whetstone                       55.0       6760.5   1229.2
Execl Throughput                                 43.0       8672.1   2016.8
File Copy 1024 bufsize 2000 maxblocks          3960.0    1026579.6   2592.4
File Copy 256 bufsize 500 maxblocks            1655.0     283351.3   1712.1
File Copy 4096 bufsize 8000 maxblocks          5800.0    2865226.5   4940.0
Pipe Throughput                               12440.0    3623761.5   2913.0
Pipe-based Context Switching                   4000.0     578965.5   1447.4
Process Creation                                126.0      23001.7   1825.5
Shell Scripts (1 concurrent)                     42.4      13395.6   3159.3
Shell Scripts (8 concurrent)                      6.0       1744.5   2907.6
System Call Overhead                          15000.0    4856018.0   3237.3
                                                                   ========
System Benchmarks Index Score                                        2485.8

$ uname -a
Linux storage 4.4.0-109-generic #132-Ubuntu SMP Tue Jan 9 19:52:39 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ ./Run
make all
make[1]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/home/nhirokinet/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
make[1]: Leaving directory '/home/nhirokinet/byte-unixbench/UnixBench'
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com


1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

2 x Execl Throughput  1 2 3

2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

2 x File Copy 256 bufsize 500 maxblocks  1 2 3

2 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

2 x Process Creation  1 2 3

2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

2 x Shell Scripts (1 concurrent)  1 2 3

2 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: storage: GNU/Linux
   OS: GNU/Linux -- 4.4.0-109-generic -- #132-Ubuntu SMP Tue Jan 9 19:52:39 UTC 2018
   Machine: x86_64 (x86_64)
   Language: en_US.utf8 (charmap="UTF-8", collate="UTF-8")
   CPU 0: Intel(R) Celeron(R) CPU G1610T @ 2.30GHz (4589.4 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET, Intel virtualization
   CPU 1: Intel(R) Celeron(R) CPU G1610T @ 2.30GHz (4589.4 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET, Intel virtualization
   23:30:18 up 1 min,  1 user,  load average: 0.19, 0.11, 0.04; runlevel 2018-01-16

------------------------------------------------------------------------
Benchmark Run: Tue Jan 16 2018 23:30:18 - 23:59:05
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       26767435.5 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     3379.3 MWIPS (9.9 s, 7 samples)
Execl Throughput                               3471.2 lps   (29.6 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        554695.5 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          148162.7 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1609334.7 KBps  (30.0 s, 2 samples)
Pipe Throughput                              914803.6 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 145211.3 lps   (10.0 s, 7 samples)
Process Creation                               8671.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   8052.1 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                   1534.6 lpm   (60.1 s, 2 samples)
System Call Overhead                         768182.9 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   26767435.5   2293.7
Double-Precision Whetstone                       55.0       3379.3    614.4
Execl Throughput                                 43.0       3471.2    807.3
File Copy 1024 bufsize 2000 maxblocks          3960.0     554695.5   1400.7
File Copy 256 bufsize 500 maxblocks            1655.0     148162.7    895.2
File Copy 4096 bufsize 8000 maxblocks          5800.0    1609334.7   2774.7
Pipe Throughput                               12440.0     914803.6    735.4
Pipe-based Context Switching                   4000.0     145211.3    363.0
Process Creation                                126.0       8671.7    688.2
Shell Scripts (1 concurrent)                     42.4       8052.1   1899.1
Shell Scripts (8 concurrent)                      6.0       1534.6   2557.7
System Call Overhead                          15000.0     768182.9    512.1
                                                                   ========
System Benchmarks Index Score                                        1050.6

------------------------------------------------------------------------
Benchmark Run: Tue Jan 16 2018 23:59:05 - 00:27:51
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables       53596515.9 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     6759.1 MWIPS (9.9 s, 7 samples)
Execl Throughput                               7767.9 lps   (29.5 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        997866.7 KBps  (30.1 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          279350.0 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       2802513.5 KBps  (30.1 s, 2 samples)
Pipe Throughput                             1877145.8 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 323835.7 lps   (10.0 s, 7 samples)
Process Creation                              19784.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                  12550.5 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                   1643.8 lpm   (60.1 s, 2 samples)
System Call Overhead                        1447857.1 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   53596515.9   4592.7
Double-Precision Whetstone                       55.0       6759.1   1228.9
Execl Throughput                                 43.0       7767.9   1806.5
File Copy 1024 bufsize 2000 maxblocks          3960.0     997866.7   2519.9
File Copy 256 bufsize 500 maxblocks            1655.0     279350.0   1687.9
File Copy 4096 bufsize 8000 maxblocks          5800.0    2802513.5   4831.9
Pipe Throughput                               12440.0    1877145.8   1509.0
Pipe-based Context Switching                   4000.0     323835.7    809.6
Process Creation                                126.0      19784.7   1570.2
Shell Scripts (1 concurrent)                     42.4      12550.5   2960.0
Shell Scripts (8 concurrent)                      6.0       1643.8   2739.7
System Call Overhead                          15000.0    1447857.1    965.2
                                                                   ========
System Benchmarks Index Score                                        1956.5

ISUCON 7 予選参加

ISUCON 7 予選二日目にチーム「ワイハリマ」(yuta1024, nhirokinet, tyabuki)で参加しました。76,904 点でした。

今回の題材は「isutaba」というチャットツールで、

  • ログインすると部屋がたくさんあるので、それぞれの部屋でチャットができる(部屋の権限などはない)
  • 部屋の発言およびほかの部屋の未読バッヂは、ストリーム風にリアルタイムに表示される。それを実現するために、各ブラウザが js で無限に新着を確認する「/fetch」 JSON APIをたたき続ける。
  • ブラウザは /fetch が返ってきてそのレスポンスを処理したら、また次の /fetch をたたく。サーバ側の /fetch 側はデフォルトで 1 秒待機(sleep 1)だが、こちらは変更可能。
  • デフォルトで、 web app 2 台、 db (MySQL) 1 台の計 3 台構成。ただし、構成は自由に変更可能で、 FE サーバの集合は自由に選択可能。
  • デフォルトではユーザアイコンも含めて自由に変更可能で、全部 db に載っている。ユーザアイコンの URL は、デフォルトでは画像の sha + 拡張子。画像への権限管理なし。

という内容でした。

リポジトリ

isubata リポジトリ by チーム「ワイハリマ」

準備内容

最低限、よくある MySQL や nginx の設定、kataribe、公開鍵一覧と配布スクリプトを準備。プライベートリポジトリに関しては、性能に余裕のあるマシンで GitLab を用意し、全員のアカウントを作り、外からも使用可能なようにしておいた。

当日

思い出せる範囲で。個人で書いているので、他の二人の実施内容に関しては違っている可能性がある。

  • 開始が10時から12時に変更だったのでそれに合わせて集合したが、最終的には13時になったので最初の進捗
  • まず、サーバに全員分の鍵や、 MySQL には InnoDB の書き出し間隔の遅延、 MySQL インデックスの追加などを実施。
  • 一旦一台構成に移行し、 icons を MySQL ではなく静的ファイルとして扱うよう変更。(初期段階としては)ある程度伸びる。
  • サーバのミドルウェアを一部は新しいものに更新しつつ、設定をざっくりチューニング。また、設定ファイルを扱いやすくするなどを実施。キャッシュ回りもいじっていたようだ。ここは担当していないので詳細はよくわからないがここで点数が大幅に伸びていた模様。
  • リクエストが多い /fetch, /message について、 Slim 脱却を試みる。テンプレートを使っていなかったのでそれ自体の修正量は少なめ。
    • ここで、 /fetch での sleep(1) が話題に上る。単につぶすと性能が下がる。 /fetch 自体はスコアに勘定されないルールになっており、単に排除するといたずらに負荷をかけることになる。
  • 40k を超えたあたりで、 php-fpm のワーカ数を調整しようとして、 sleep(1) が話題に上る。これが php のプロセス数を食っているようだ。とりあえず増加。
  • 静的ファイルの icons を相手のサーバにも送り付ける口を作り web app を二台に戻すが、性能が上がらない。
  • とにかく /fetch の sleep の問題が大きいということで、 /fetch だけは専用の php-fpm プールに逃がすように変更してスコアが上昇。
  • 同時に、ベンチマーカーのエラーメッセージや kataribe の結果などから、 icons が異常に遅延(nginx 視点でテイルレイテンシ 10 秒)していることに気付く
  • 外向け 100Mbps 、一分間での画像リクエスト約 1.4k ということで、画像ファイル平均 500kB 程度とすると帯域をほぼ使い切っている計算。
  • メモリを使い切ってキャッシュが減っているのでは?という疑惑はあった
  • ベンチマークを何度か走らせると、 +/- 15% 程度は平気で増減する状態であり、ガチャの影響も大きかった
  • icons が大幅遅延するかどうかもガチャ次第だった。
  • web app を二台活用できない理由も、 icons が遅延する(あるいは上位チームがこの何倍ものスコアを叩き出せる)根本原因もわからないが、終盤が近づいたので web app を一台運用することを決定。
  • 再起動試験実施。
  • 再起動後二回目の試験で、運よく過去最高得点が出た。この時終了約15分前だったので、以後外れベンチマークを引かないように全ての作業を終了。

感想

今回は、ウェブアプリケーションとしてはかなり王道なチューニングが主で、それでいて難易度が高く仕上がっていて、良い問題だったと感じました。

まだほかのチームの write up はあまり見られていませんが、それでも特に複数台構成/ローカルファイルシステム同期における /icons の更新日時や、せめてもの二台目の活用方法など、少し読んだだけでも見直すべき部分が色々出てきました。

二日目の通過ラインは 210,464 点で、ここからさらに三倍食えるようにしないといけなかったようです。とはいえ、学びはあったので、今後予選通過を狙えるようにしたいです。

Ubuntu 16.04 に Eclipse をインストールして C++ を開発できるようにする

基本的には Java をインストールして Eclipse のウェブページからインストーラをダウンロードするだけなのだが、微妙につまづいたのでメモ。

インストール先

インストーラが自分のホームディレクトリにインストールする。なので、 Eclipse のインストールには root 権限は不要。
パスは通らないので、自分で通すかフルパスで起動する。

Cannot run program "/bin/bash" 的なエラーメッセージが表示される

フルパス書いてあるし /bin/bash も起動可能だしなぜ?となるが、メモリ不足だった。VM であまりメモリを振っていなかったところに Eclipse を入れたためで、 VM の割り当てメモリを増やすと動作。

C++ のプログラムを読み込んだら、基本的に include が解決できていない

std::vector とか、間違いなく標準であるようなものも設定しないと現れないようだ。適当なファイルを g++ -v でコンパイルすると途中で次のようなのが出るので、全部プロジェクトのプロパティの C/C++ General > Paths and Symbols > Includes に追加する。

#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/5
 /usr/include/x86_64-linux-gnu/c++/5
 /usr/include/c++/5/backward
 /usr/lib/gcc/x86_64-linux-gnu/5/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

OpenSSL での証明書発行手順メモ

オレオレにしろちゃんとしたものにしろ、証明書の作成は毎回ググるし、その割には必要なコマンド群にありつけるまでに微妙に目的と違うことを書いているサイトをめぐるのに時間がかかるので、最低限のコマンドだけメモ。気が向いたら更新予定。だいたい何をしているかはわかっているけどコマンドオプションなどは覚えていられないから手っ取り早くコピペしたい人/時向け。

拡張子は、形式を尊重して key, crt の代わりに両方 pem にすることも。

秘密鍵作成

openssl genrsa -out hoge.key 4096

秘密鍵を暗号化保存したい場合は -des や -aes などのオプションを付与し、パスフレーズを入力する。

CSR 作成

openssl req -new -key hoge.key -out hoge.csr

外部の CA に署名してもらう場合(例えばブラウザでそのまま使えるちゃんとした証明書が必要な場合)はこの CSR を渡して証明書を受け取る。
このファイルは署名を受けとることができたらあとは削除して問題ない。

自己署名

openssl x509 -in hoge.csr -days 3650 -req -signkey hoge.key -out hoge.crt

自己署名でない場合、 -signkey ではなく -CA オプションを使用する。

Collabora Online の Ubuntu ネイティブパッケージのインストールのメモ

Collabora Online は、すぐ使えるものとしては以前 Docker イメージとして提供されていたが、最近になっていくつかの Linux ディストリビューション向けにパッケージが提供されるようになり、 Ubuntu 16.04 用の apt ソースの URL も提供されたので、それに従ってインストールした。その際、微妙に SSL だけ設定する必要があったのでメモ。これを機に別のページに、普通にオレオレ証明書のメモをいつでも見ることができるところに書こうと思った(追記:すぐ追加した:OpenSSL での証明書発行手順メモ | にろきのメモ帳)。現時点で必要というだけなので、将来的には自動実行されるようになるかもしれない。

NextCloud + Collabora | にろきのメモ帳 に従来構成でも必要だった手順を書いてある。

基本的には Collabora Online Development Edition (CODE) – Collabora Productivity にある通り、

# import the signing key
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 0C54D189F4BA284D
# add the repository URL to /etc/apt/sources.list
echo 'deb https://www.collaboraoffice.com/repos/CollaboraOnline/CODE ./' >> /etc/apt/sources.list
# perform the installation
apt-get update && apt-get install loolwsd code-brand

で入って 9980/tcp で HTTPS で LISTEN しはじめる(あとはリバースプロキシを構成すれば使える)はずだったが、これだと loolwsd が起動に失敗した。journalctl -u loolwsd で見てみたところ、証明書があるべき場所にないようだ。あと、そもそも、原因の切り分けはしていないが数十 kB/s 程度しか出ず、大容量のパッケージをダウンロードする必要があったため数時間かかった(追記:数日後別の場所から実施したときは、警戒して最初から screen で実施したにも関わらず数分で終わった)。

/etc/loolwsd/loolwsd.xml を見ると鍵や証明書がおかれるべき場所がわかったので、以下のように実行したら動いた。

$ cd /etc/loolwsd/
$ sudo openssl genrsa -out key.pem 4096
$ sudo openssl req -new -key key.pem -out tmp.csr
$ sudo openssl x509 -in tmp.csr -days 3650 -req -signkey key.pem -out cert.pem
$ sudo cp cert.pem ca-chain.cert.pem
$ sudo rm tmp.csr
$ sudo service loolwsd start

RouterBoard RB3011UiAS-RM 導入

自宅のルータは長らく iptables, dnsmasq あたりで構成した Ubuntu VM で実施してきたが、障害時の対応が面倒なのに加えて、サーバメンテナンス中という何かと急いでぐぐりたい場面で自宅インターネットが使えないという致命的な問題があったので、以前名前を聞いておもしろそうだった MikroTek 社の RouterBoard に移行した。Linux ベースの RouterOS なるものを搭載していて、 RB3011UiAS-RM は ARM ベースとのこと。ネットワーク機能も含めてなにかと仮想化がもてはやされるこのご時世、 VM からアプライアンスに移行するのは時代に逆行しているような気もするけど、自宅だとスケールメリットはおろか冗長構成ですらデータ保全ぐらいしかしないので、シンプルなアプライアンスがよいと判断。ちなみに、 1U Rackmount と書かれているが、この機種は奥行きが狭いのと、 1U ラックの横幅として言われる 19 インチ(482.6mm)はサーバルームで見るとでかそうに見えるが意外と家における大きさなのもあって、ラックサーバを置く気にならない自宅でも意外と現実的な大きさだった。ラックマウント用の金具の他に、普通にその辺に置く用のゴム足も付属した。

今回は、 RN3011UiAS-RMBaltic Networks から購入した。購入したときは取り寄せが必要で一か月くらいかかるとのことだったが、今見ると In Stock なので時期によるかもしれない。購入時、発送方法は料金だけを見て無料だった “Pick up at Lisle warehouse” を選んだが、「請求先も発送先も両方日本なので、この選択肢はおかしい。これらの選択肢の中から適切なものを指示してほしい」というメールがきて、返信すると対応していただけた。あとで気付いたのだけど Lisle は Baltic Networks の拠点の地域なので、これは Lisle warehouse という名前の業者に関する何かではなく、「Lisle の倉庫まで取りに行く」という意味だったのかもしれない。ちなみに AC アダプタのプラグは US 用で、日本と同じ形で 100V にも対応していたのでそのまま使用できた。品物 $145.00, 送料(FedEx International Economy) $49.59 で計 $194.59。

左 5 ポート、右 5 ポート、 SPF 1 ポートで、基本的にはポートごとに設定をすることができるようだ。ただ、左右それぞれ最大一つ master port を選ぶことができ、左右それぞれ master port を含むポートを 5 ポートの中から任意で選んで master port にぶら下げるとそれらは同一の L2 セグメントとして wire speed で通信できるようだ(この場合 slave port についてはちゃんと設定がいじれないようになっているっぽい)。左右をまたぐ場合は bridge を構成して実現するようになっている。初期設定では ether1 が DHCP で外、それ以外は中というよくある自宅ネットワークを想定したものになっており、 bridge の下に ether1 以外を治める左 master port、 右全部の master と SPF がぶら下がっていた。

まだあまりいじれていないが、メニューを見て色々できるな、と思ったのと同時に、例えばファイヤーウォールの設定画面で言えば iptables の設定で登場する用語がそのまま登場していた。DNS, DHCP に関しては GUI でいじれるが、 dnsmasq みたいに DNS が DHCP と連携するみたいな機能はないのでそこは手動でどうにかするしかなさそう。だいたいの設定は Web 画面でいじれそうで、試していないものの(VPN 等に使うと思われる)証明書発行用 GUI も Web にあった。ターミナルもブラウザから開くことができたほか、 ssh などでも使えるので、通常はルータへの接続のためにシリアルケーブルを用意する必要はなさそうだった(内蔵の液晶で IP アドレスもわかるし)。

とりあえず、最低限のファイヤーウォールだけ設定して、普通にルーティング(IPv4 で、ネットワークは異なって RouterBoard のポートも別だがマスカレードではないルーティング)経由で iperf してみたところ、

------------------------------------------------------------
Client connecting to ***, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local *** port 54934 connected with *** port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.05 GBytes   901 Mbits/sec

さすがにただのルーティングは充分速度が出るようだった。

また、実際に RB3011UiAS-RM 自身で IPv4 マスカレードするように構成して、インターネット(上流に IPv4 を IPv6 にトンネルする ISP 支給のルータがいるので PPPoE ではない)に繋いで www.speedtest.net で試してみたところ、

500Mbps 以上のスループットが出せることが確認できた。ファイヤーウォールの項目数を増やすとどうなるかはわからないのと、そもそも普通の ISP 経由のインターネット経由なのでこれが上限でもない(事実導入前と比べて遅くはなっていない)ので資料としては不充分なものの、自宅のインターネット用としては充分な速度が出ると言えそうだった。

内蔵液晶パネルに何を表示するかは選択可能だが、 Informative Slideshow だと全ポートの合計スループットから電圧、ファームウェアバージョン、時刻まで色々と出してくれる。一方、 Slideshow だとポートごとのグラフを出してくれるようだ。実用的というよりかは眺めていて飽きないという内容といえる。ちなみに、液晶自体は自動パワーオフの時間が設定可能だが、どちらかというと電源ランプの青色 LED が眩しいので(我が家のように)寝床と隔てがない空間に置く場合は、人によって(例えば僕のような場合)は直接光と壁への反射について対策をしたほうがよい。

ちなみに設定は困ってぐぐったら割と MikroTik Wiki が出る。ちなみに設定だいたいをエクスポートする /export というコマンドもあるようだ。

一方、最初に困ったのは AC アダプタで、運よく米国のサイトから仕入れたため AC アダプタもそのまま使用できたが、 DC 側が接触が微妙なようで、少し抜ける側に力が加わっただけで DC 入力が途絶えるようだ。力を加えようと思わないのに途絶えるほどではないのと、アダプタと本体どちらの不具合か、または仕様かもわからないのにどれほどかかるかわからない海外への依頼をするのも面倒なので運用でカバーすることにした。本体裏には謎の溝がありアダプタの DC ジャック付近のケーブルはそこにかませて仮固定できる。また、電圧は 10-28V (付属アダプタの出力は 24V)と幅広く受け付けているので直径さえあえば適当な AC アダプタを使うことはできそう。

また、この機種は感圧式タッチパネル付き液晶画面を備えているが、購入当初は(初期設定で 1234 の)暗証番号の入力すらも困難な状態だった。これに関しては Web UI 経由で Recalibrate を押し、その後タッチパネル側で指示に従ってキャリブレーションすることで完全に解消された。

まだ設定は完了していないが、一通り安定導入後も遊び用のポートを空けておいていじってみたいと思えた。

(2017/8/16 追記)証明書の署名操作(実際には秘密鍵の生成から実施している可能性がある)で分単位から十分単位の時間がかかるようだ。WebUI だとすぐ、ターミナルだと一定時間経った後にレスポンスがあるが(後者はタイムアウトとの表示)、実際には証明書の生成作業を行っており、先ほどレスポンスが返ってきた操作は終わっていない、かといってテンプレートの削除は Template being used! というエラーメッセージになる、そして数分待つと削除できる、という状態になった。WebUI の Tools > Profile で様子を見ることができる。見る限り、 CPU を 100% (コア単位)使っているようなのだが、この機械はデュアルコアなので 200% まで使えるので複数同時に走らせたりしなければ大丈夫そうだ。ARM CPU にしても秘密鍵生成、電子署名ってそんな時間かかる……?とは思うが、かかるものは仕方がないのでそういうものと思って使うのがよさそう。

(2017/8/28 追記)AC アダプタがゆるい問題は、秋月電子通商で外径 5.5mm 、内径 2.1mm で中央がプラスの、24V/1.5A の AC アダプタ(1,500 円)を買ってきたところ解決した。少なくとも今のところ、店舗の張り紙によると取り扱っているのはこの大きさ/極性しかないらしいので、大きさがあうものが取り扱われていたのは運がよかったということか。
なお、オンラインにあった説明書で 5.5mm/2mm と書かれていた。また、 [solved] Power connector problem in RB2011-RM and RB3011-RM – MikroTik RouterOS に同様の現象で悩んでいる状況の書き込みがあり、色々と分析がなされていたが、新機種にバンドルされた電源ユニットはゆるすぎて、旧機種のものが新機種にもちょうどよいというコメントや、内径が 2.5mm のアダプタだとゆるくなって、 2.1mm のものが正しいというコメントが書かれていた。秋月電商のアダプタは店の張り紙で内径 2.1mm と明記されていて確かにそのくらいに見えて、自信はないが 2.1mm より大きいような気はする(下の写真は付属品の方)。

NextCloud + Collabora

NextCloud と Collabora Office を連携させると複数人で同時に(共有リンク経由でも) LibreOffice ファイルを編集できて便利そうなので入れて見みました。なんだか色々ややこしかったというかハマりどころが多かったのでメモです。

NextCloud 設定

NextCloud 自体は、よくある設定のまま。細部は違えど、 Nginx Configuration — Nextcloud 11 Server Administration Manual 11 alpha documentation に書かれたままでよく、はまりどころも少ない。

NextCloud と Collabora を連携させるにあたって、セキュリティ上の理由で別のサブドメインで運用することが推奨されていた気がするが、ソースが見当たらない(あと、なぜか今 https://nextcloud.com/collaboraonline/ につながらない)。

Colabolla Online Development Edition 設定

Collabora に関しては、基本は先ほどのページの内容で実施するが、いくつか修正が必要だった。

まず、以前 VPS でやってうまくいかなかった経験から、一度 LAN 内のサーバでオレオレ鍵で試していたが、これだと権限がない旨が出力された。https://help.nextcloud.com/t/libreoffice-nextcloud-access-forbidden-issue/6358/17 に書かれているように、 NextCloud から Collabora に繋ぎに行くときに SSL 証明書を検証するのに失敗したときもこのエラーが出るらしく、オレオレ証明書を使う場合は (NextCloud のディレクトリ)/resources/config/ca-bundle.crt に自分が使う証明書を追記する必要があった。

続いて、つなぐと Collabora の画面が真っ白で止まった。nginx 側の設定を NextCloud のものからコピーしたが、そこに

add_header X-Frame-Options "SAMEORIGIN";

が含まれていたので削除した。

続いて、

wsd-00024-00033 15:52:08.397614 [ websrv_poll ] ERR  #20 Exception while processing incoming request: [GET /lool/https%3A%2F%2F(略) H...]: Invalid or unknown request.| wsd/LOOLWSD.cpp:1665

みたいなエラーが出た。Collabora: Invalid or unknown request – support / collabora – Nextcloud community に書かれている通り、 nginx 側の、 Collabora で使うドメインで

chunked_transfer_encoding off;

を追記すると動いた。

最終的に、Collabora 側の nginx の設定は、

server {
        #listen 80 default_server;
        #listen [::]:80 default_server;
        listen 443 ssl;
        listen [::]:443 ssl;

        ssl_certificate /etc/nginx/ssl/**;
        ssl_certificate_key /etc/nginx/ssl/**;

        chunked_transfer_encoding off;

        server_name ***;
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Robots-Tag none;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;

        proxy_buffering off;

        location ^~ /loleaflet {
                proxy_pass https://localhost:9980;
                proxy_set_header Host $http_host;
        }

        location ^~ /hosting/discovery {
                proxy_pass https://localhost:9980;
                proxy_set_header Host $http_host;
        }

        location ^~ /lool {
                proxy_pass https://localhost:9980;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header Host $http_host;
        }

}

という感じになった。(検証環境のものなので、 TLS 自体の設定はかなり手抜きになっている)

あとは以前試した時は繋ぐと Connecting で 1 分ぐらい待たされたりタイムアウトしたりする現象があったが、最新の CODE 2.1 を入れたところ解消したようだ。

HP MicroServer Gen8 導入

従来使用してきた hp ML115 Gen 5 もそろそろ寿命が近そうだったところで、 hp MicroServer Gen8 が安くで売られているのを見つけたので購入し、移行した。Amazon の HP ProLiant MicroServer Gen8 販売ページでは、在庫が安定しないようだが Celeron モデルが株式会社ゼット(zmart.jp)の販売で 38,458 円で入手できた。

在庫及び配送は Amazon が担当し、販売元は並行輸入品を廉価販売する株式会社ゼットが担当するようだった。機械はチェコ製(本体裏面では「MADE IN CZECH REPUBLIC」「捷克制造/捷克製造」)らしい。(欧州製にも関わらず一部記載は英語/中文簡体/中文繁体で、また一部記載は欧米の言語色々だった。謎。)

こちらの販売元で購入したものは、並行輸入品につき、電源コードが日本用ではなかった(付属品一覧を見る限り英国用と欧州用の二本?)ため、電源コードは別途自前で購入する必要があった。ディスプレイ付属のケーブルを流用できる端子だったので、とりあえず家にあった別の機器の流用で動作確認しつつ電気屋で買った。

メインメモリ

標準では 4GB のメモリが付属したが、2スロットで16GB(8GB×2)にまで増設できる。32GBは検索したところだと成功例がヒットせず、失敗例が多くあるようだ。ということで、HP Microserver Gen8を購入 | 三日坊主の軌跡を参考に、このページで動作実績のあるKTH-PL316E/8Gを二つ購入し、計16GBのメモリを認識させることができた。iLO と組み合わせた場合 HP 純正でないためアラートがくるとリンクした動作実績に書いてあったが、それはスルーした。

どこを分解すればメモリを交換できるかよくわからなかったので検索したところ、MicroServer Gen8 のメモリを交換している YouTube 動画がヒットした。

HDD

簡単と見せかけて、最初に購入した HGST の 6TB のディスクは騒音の問題に悩まされた。x64 な Ubuntu に搭載する HDD に相性問題なんかないだろうと思いながらも一応勧められるままにツクモの交換保証に加入していたが、この保証は「思ったよりうるさい、家に置くにはうるさすぎる」みたいな理由でも期間内なら別の機種への交換が可能(価格が上がる場合は差額、下がる場合はそのまま)で、しかも回数に制限がないため、思わぬ形で救われた。結局、 Western Digital Red 6TB (WD60EFRX 、Pro ではない方)に落ち着いた(まあこれでもシーク音はまあまあ鳴るが、僕の中では許容範囲)。

音の問題はそもそも感じ方や使い方に個人差がある上、静かな方のディスクでもうるさいと言っている人がいる一方でシーク音が大変うるさいディスクでもアイドル時に静かなためか静かと評価する人がいるのでレビューの見方が難しい。とはいえ傾向はあるので、最終的にはレビューを複数サイトでちゃんとみてればもう少しスムーズにいけたな、とは思った。

後で気付いたことだが、Linux mdadm で高速に RAID1 array を構築する – Qiita 等で記述されているように、特にオプションを指定しないまま mdadm で RAID 1 を構築するとどういうわけか「多分二つのディスクの内容は一致すると思う」みたいな状態から開始するらしく、同期が完了するまでは両ディスクから読み込んだうえで片方のディスクで書き込みなおすためにシークが大量発生するようだ。このせいで騒音が増大していたともいえるし、このお陰で本格的にデプロイする前から騒音に気付けたともいえる。

あと、交換後の HDD の片方が初期不良だったようで、なんだかインストーラが変な動きをするのでエラーログなどから片方のディスクがおかしいと判断、基本的に書き込みエラーだったので USB-SATA 経由で Western Digital の Data Lifeguard Diagnostic for Windows の全クラスタデータ削除に突っ込んでみたところ、数分で FAIL となった。仕方がないので秋葉原にあるツクモのサポートセンターに持ち込んで症状を伝えたらその場で交換していただけて、そちらを入れたら問題なかった。ちなみにツクモはサポートセンターと販売店が別ということを知らなかったので、ツクモの販売店に20:10ぐらいに持ち込んだら、20:00までにサポートセンターに持ち込めば対応していただける旨を教えていただけた。何をするにも下調べが大事。

HDD が初期不良であった場合のエラーログについては、 Ubuntu のインストーラのメニュー画面の中に「syslog を HTTP で公開する」みたなオプションがあることを初めて知った。LAN 内の他の端末でブラウザで見たりダウンロードしたりできて便利だった。

(今回やたらお世話になったので色々ツクモを称賛してるけど別にツクモの中の人とかじゃないよ)

インストール

Ubuntu インストールにあたってはハードウェア固有の罠はなかったはず。通常通りソフトウェア RAID を構築して OS をインストールすればよい。

Ethernet

まず、LAN端子は、二つとされていながらiLOなるネットワークベースの技術も搭載されていると書かれていた。公式ウェブページから想像つきにくいまま購入したが、結局は「通常のLAN端子2つ」「OSから見えず、iLOが使う端子一つ」の計3つが搭載されていた。(いずれも Broadcom チップらしい)

Ubuntu 16.04 では eno1/eno2 として見えたが、その順序について少し厄介だった。最初1と書かれた端子にのみケーブルをつないでいたところ、ケーブルが刺さっている方が eno1 となった。ところがその後で 2 と書かれた方にもケーブルを刺して再起動したところ、これまで eno1 と書かれていたものが eno2 になり、新しく刺したほうが eno1 として認識された。謎。

謎のランプ

先ほどの写真にも写っているしそもそも公式の画像でもだいたい写っている通り、このサーバの下部には謎の青いランプがある。飾りかと思っていたら、添付文書では正常稼働の確認の文脈でこのランプが青く光っていることの確認が電源ランプの確認と別に書かれていた。「Health LED bar」とか名前がついており、電源ボタンについているランプとは別物らしい。

その他

  • hp ML115 Gen5 と比べても通常時のファンの音が大変静か(ただし、比較対象の hp ML115 Gen5 はリアケースファンが 2000-5200 rpm で、僕は気にしなかったがアイドル時でもうるさいという人は多いはず)
  • 起動してから OS に制御が渡されるまではファンの回転数が制御されず普通にそこそこファン音がするのは hp ML115 Gen5 と同様(とはいえ、さすがに同じ状態の hp ML115 Gen5 よりは静か)
  • BIOS が色々検査している画面が妙にグラフィカル
  • カタログの時点でわかるが念のため追記:HDDはホットプラグ非対応。HDDを交換しようとすればまあ気付くであろう位置に注意書きがある。
  • カタログの時点でわかるが念のため追記:光学ドライブは別売りオプション。先ほどの写真の通り、なくとも見栄えが悪いというほどではない。
  • 今回は Ubuntu で使用したため実体験と全く関係のない話だが、 hp が公式にカスタムした ESXi イメージを配布している(添付文書にリンクあり)ため、どうやら ESXi からオンボード RAID が使えるらしい。ただし、HP ProLiant MicroServer Gen8を買って地雷を順番に踏んでった話 – なおたこブログにはパフォーマンスに難がある旨が記されている。
  • 日本用電源コードすら付属しない並行輸入品でも、 BIOS 画面は English/日本語 の二択だった。謎。
  • iLO は便利そうなのに試しすらしないで稼働開始してしまった。すみません。

SECCON 2016 Online CTF Writeup

SECCON 2016 Online CTF にチーム「yharima」で参加していました。なかなか難しい問題が多く、まだまだ勉強せねば、という感じがしました。145位、500点でした。

VoIP

pcap ファイルが渡されるので Wireshark で開き、 VoIP 機能で開くと音が再生できてフラグをしゃべっていた。少し聞き取りづらかったが、がんばって聞き取って提出。

Memory Analysis

メモリダンプがおかれており、 volatility というツールがヒントに書かれている。

偽の svchost がアクセスしているウェブサイトを調べてほしい、そのサイトに行けばフラグが手に入る、というもの。また、ヒントには「hosts」とも書いてあった。

volatility の使い方を軽く確認したあと、ウェブサイトを見ていると書いてあるのでまずは通信内容を確認した。

$ volatility_2.5_linux_x86 -f ./forensic_100.dat --profile=WinXPSP2x86 connections
Volatility Foundation Volatility Framework 2.5
Offset(V)  Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ---
0x8213bbe8 192.168.88.131:1034       153.127.200.178:80        1080

IP アドレスはわかったが、繋ぎに行っても nginx の初期インストール画面のようなものが表示されたので、「hosts」というヒントを元に C:\Windows\System32\drivers\etc\hosts を探すことに。filescan や dumpfiles を試みたがなかなか手に入らないので、最終的にはstrings して「153.127.200.178」で grep した。

$ strings forensic_100.dat | grep 153.127.200.178
153.127.200.178    crattack.tistory.com attack.tistory.com 
153.127.200.178    crattack.tistory.com 
153.127.200.178:80

ホスト名は候補が二つに絞られたが、 /etc/hosts を手元に書いてこのホスト名・IPアドレスの組み合わせにつないでもまだフラグはない。いろいろ見ているうちに、 HTTP をしゃべっている内容のようなものが見えていたので、このやり取りもまだメモリに残っているのでは、ということで “GET /” を探した。

$ strings forensic_100.dat | grep 'GET /' 
GET /image/237C0C3E576BA0AD06F720 HTTP/1.1
GET /entry/Data-Science-import-pandas-as-pd HTTP/1.1
GET /entry/Data-Science-import-pandas-as-pd HTTP/1.1
GET /entry/Data-Science-import-pandas-as-pd HTTP/1.1
GET /entry/Data-Science-import-pandas-as-pd HTTP/1.1

ようやく URL がわかったので、 アクセスするとフラグ。

$ curl http://attack.tistory.com/entry/Data-Science-import-pandas-as-pd
SECCON{_h3110_w3_h4ve_fun_w4rg4m3_}

Anti-Debugging

バイナリファイルが渡され、調べてみると Windows の exe ファイルだったので Windows 上で起動。起動するとパスワードを聞かれ、パスワードが違うと言われた。

Windows のバイナリデバッグはしたことがなかったので、全体的に yuta1024 に手伝ってもらって(というよりほぼ教えてもらって)実施。まずは OllyDbg をインストールした。

OllyDbg には上の画像のような文字列検索機能があり、それぞれ参照元の命令がある場所も検出できた。パスワードが違う的なエラーの周辺で、色々している判定っぽい処理があったので、そこで一旦止めると “I have a pen.” という文字列がメモリ上にあった。これがパスワードではないかと推測して入力すると表示が変わり、今度はデバッグ環境であることが検出された。デバッグではない環境で実行するとパスワードが正しいと言われたが何も表示されなかった。

エラーメッセージには OllyDbg や VMware などいろいろなものが書かれており、その少し前のコードを見るとだいたい直前に「CMP DWORD PTR SS:[***],*」「JNZ SHORT bin.********」の二つを実施しており、おそらくは正規の実行では通らないようになっていたので、 OllyDbg の機能で JNZ を JMP に(命令の一バイト目を 0x75 から 0xEB に)書き換えた。

すると、今度はゼロ除算で落ちるようになった。その部分の IDIV 命令(上の画像の 0x4015DE)だけ NOP で塗りつぶすと、何もしないで正常終了するようになった。文字列一覧では暗号化されたフラグと思わしき意味ありげな文字列があったので、その参照元を見るとその IDIV と同じ関数の少し下にあった。

そのため、落ちる IDIV から、フラグと思わしき文字列の参照元の直前までを NOP で塗りつぶして実行すると、フラグが出力された。

(追記)同じチームのメンバーの writeup です。

SECCON 2016 Online writeup – yuta1024’s diary

ISUCON 6 予選参加

チーム「にゃーん」で ISUCON 6 予選参加して惨敗してきました。厳しい。というわけで記録です。といっても、結局自分が担当した部分以外はあまり把握していないので、それは各自がブログか何かを書くのを待ちます。

まず、インスタンスを起動して、あまり分ける必要がない二つのアプリケーションが動いていることを確認したので、れにが二つをマージしていました。その間に DB のインデックスで露骨にまずいものがないか見ていましたが、一応張ったもののそれほど大幅ということはありませんでした。

htmlify なる関数があり、これが今回のメインのようでした。記事(初期状態で約七千)のタイトルを本文中から自動抽出してリンクに変換するものですが、全ての記事のタイトルを抽出したうえで正規表現や SHA1 を活用した実装になっていて遅いこと遅いこと。

なお、今回は Ruby だったこともあり初期実装は0点でした。

そのあと、他のチームメンバーは Web 周りのチューニングなどをしてそれなりに点数を伸ばしていたようですがあまり把握しておらず、僕は htmlify をなんとか早くしようとしていました。

他のメンバーと違い、僕は Ruby の知識があまりなかった状態でこれを見たので、 Ruby の置換が同様に書かれたほかの言語と比べても遅いことに絶望して、なんとか早くするアルゴリズムを模索しました。とりあえずなんにしてもその部分を Ruby で書くのは諦めて、 C++ で書いて外部コマンドで呼び出すことにしました。(引数で辞書と入力を渡し、出力は標準出力)

二分探索をしようとしたりさまよっていましたが、最終的にはハッシュマップで実装しました。与えられた文字列に対して短いほうから辞書にある最大の長さまで試す、ただし途中で打ち切るための情報を埋め込む、というものです。ポインタでいろいろ扱える方が楽だったのでポインタをベースとし、何かとコピー回数の増える std::string はなるべく使わないように変更、 C++ の std::map は hashmap ではないことを思い出して std::unordered_map に変えましたが、パフォーマンス計測値、スコアともに改善はしたものの依然遅いままでした。

#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <unistd.h>
#include <map>
#include <iostream>
#include <unordered_map>

using namespace std;

void urlwrite(const char *str) {
	while(*str!='\0'){
		if ((*str>='a' && *str<='z') || (*str>='A' && *str<='Z') || (*str>='0' && *str<='9') || *str=='.' || *str=='-' || *str == '_' || *str == '~') {
			fwrite(str, 1, 1, stdout);
			++str;
			continue;
		}
		int t = *((unsigned char*)str);
		int b1 = t/16;
		int b2 = t%16;

		char tmp[3];
		tmp[0]='%';
		tmp[1]=(b1<10)?(b1+'0'):(b1+'A'-10);
		tmp[2]=(b2<10)?(b2+'0'):(b2+'A'-10);

		fwrite(tmp,1,3,stdout);
		++str;
	}
}

void htmlwrite(const char *str) {
	while(*str!='\0'){
		switch (*str) {
			case '&':
				fwrite("&amp;", 1, 5,stdout);
				break;
			case '<':
				fwrite("&lt;", 1, 4, stdout);
				break;
			case '>':
				fwrite("&gt;", 1, 4, stdout);
				break;
			case '\'':
				fwrite("&#039;", 1, 6, stdout);
				break;
			case '\"':
				fwrite("&quot;", 1, 6, stdout);
				break;
			case '\n':
				fwrite("<br />\n",1,7,stdout);
				break;
			case '\r':
				fwrite("<br />\r",1,7,stdout);
				break;
			default:
				fwrite(str,1,1,stdout);
		}
		++str;
	}
}


int main (int argc, char *argv[]) {
	unordered_map <string, int> dic;

	setvbuf(stdout, NULL, _IOFBF, 102400);

	size_t longest=0;
	for(int i=2;i<argc; ++i){
		string key=string(argv[i]);
		for(int j=1;j<4;++j){
			string tmp=key.substr(0,j);
			if(!dic.count(tmp)){
				dic[key.substr(0,j)]=1;
			}
		}
		{
			int j=6;
			string tmp=key.substr(0,j);
			if(!dic.count(tmp)){
				dic[key.substr(0,j)]=1;
			}
		}
		dic[key]=2;
		longest=max(longest, key.length());
	}

	while (*argv[1]!='\0') {
		int skiplen=1;
		string title;
		int flag=0;

		for(int i=1;i<=longest;++i){
			char tmp=argv[1][i];
			argv[1][i]='\0';
			string key=string(argv[1]);
			argv[1][i]=tmp;
		
			if(dic.count(key)){
				if(dic[key]==2){
					title=key;
					flag=1;
				} 
			}else{
				if(i<4 || i==6){
					break;
				}
			}
		}

		if(flag){
			fwrite("<a href=\"/keyword/", 1,18,stdout);
			urlwrite(title.c_str());
			fwrite("\">", 1,2,stdout);
			htmlwrite(title.c_str());
			fwrite("</a>", 1,4,stdout);
			skiplen=title.length();
		}else{
			char tmp=argv[1][1];
			argv[1][1]='\0';
			htmlwrite(argv[1]);
			argv[1][1]=tmp;	
		}
		argv[1]+=skiplen;
	}
}

※ちなみに、<a href=”/keyword/~~”> の部分は、 Ruby の見本実装では <a href=”http://192.0.2.1/keyword/~~”> のように URL 変換済み、 PHP の見本実装では <a href=”/keyword/~~”> のように path 部分のみでした。後者で書きましたが、不思議でした。

その後、 Trie 木を実装しましたが、初期化コストが大きく辞書を作るのに 100ms 程度かかってしまいました。外部コマンドなので毎回初期化していたのと、特にバイナリでいい感じに複数のデータを受け渡す実装の時間もなく、トップページを表示するのに 10 回読みだしてしまい、却って遅かったのでこちらは導入に至りませんでした。Trie 木生成後の処理は一桁速くなったように思われる(雑な計測からの体感)ので、 Ruby のモジュールにするか、これ専用のサーバを立てて記事追加・削除・データ変換が毎回行えるようにすればかなり高速化できたものと思われますが、すでに終盤でそこまでする時間がありませんでした。

#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <unistd.h>
#include <map>
#include <iostream>
#include <unordered_map>

using namespace std;

void urlwrite(const char *str) {
        while(*str!='\0'){
                if ((*str>='a' && *str<='z') || (*str>='A' && *str<='Z') || (*str>='0' && *str<='9') || *str=='.' || *str=='-' || *str == '_' || *str == '~') {
                        fwrite(str, 1, 1, stdout);
                        ++str;
                        continue;
                }
                int t = *((unsigned char*)str);
                int b1 = t/16;
                int b2 = t%16;

                char tmp[3];
                tmp[0]='%';
                tmp[1]=(b1<10)?(b1+'0'):(b1+'A'-10);
                tmp[2]=(b2<10)?(b2+'0'):(b2+'A'-10);

                fwrite(tmp,1,3,stdout);
                ++str;
        }
}

void htmlwrite(const char *str) {
        while(*str!='\0'){
                switch (*str) {
                        case '&':
                                fwrite("&amp;", 1, 5,stdout);
                                break;
                        case '<':
                                fwrite("&lt;", 1, 4, stdout);
                                break;
                        case '>':
                                fwrite("&gt;", 1, 4, stdout);
                                break;
                        case '\'':
                                fwrite("&#039;", 1, 6, stdout);
                                break;
                        case '\"':
                                fwrite("&quot;", 1, 6, stdout);
                                break;
                        case '\n':
                                fwrite("<br />\n",1,7,stdout);
                                break;
                        case '\r':
                                fwrite("<br />\r",1,7,stdout);
                                break;
                        default:
                                fwrite(str,1,1,stdout);
                }
                ++str;
        }
}

class Trie {
        public:
                Trie* children[256];
                uint32_t ex[8];
                int exist=0;

                Trie(){
                        memset(ex,'\0',32);
                }

                void insert(unsigned char *str) {
                        if(*str=='\0') {
                                exist=1;
                                return;
                        }


                        if(!((ex[(*str)/32]>>((*str)%32))&1)){
                                children[*str]=new Trie();
                                ex[(*str)/32]|=1UL<<((*str)%32);
                        }
                        children[*str]->insert(str+1);
                }

                int find(unsigned char *str) {
                        int tmp=-2;
                        if (exist) {
                                tmp=-1;
                        }

                        if((ex[(*str)/32]>>((*str)%32))&1){
                                tmp=max(tmp, children[*str]->find(str+1));
                        }

                        if(tmp==-2)
                                return -2;
                        else
                                return tmp+1;

                }

};

int main (int argc, char *argv[]) {
        unordered_map <string, size_t> dic;
        Trie trie;

        setvbuf(stdout, NULL, _IOFBF, 102400);

        for(int i=2;i<argc; ++i){
                trie.insert((unsigned char*)argv[i]);
        }


        cout<<"<br>clock="<<clock()<<"<br>";

        while (*argv[1]!='\0') {
                int ret = trie.find((unsigned char*)argv[1]);
                int skiplen=1;

                if(ret>0){
                        char tmp=argv[1][ret];
                        argv[1][ret]='\0';
                        fwrite("<a href=\"/keyword/", 1,18,stdout);
                        urlwrite(argv[1]);
                        fwrite("\">", 1,2,stdout);
                        htmlwrite(argv[1]);
                        fwrite("</a>", 1,4,stdout);
                        skiplen=strlen(argv[1]);
                        argv[1][ret]=tmp;
                }else{
                        char tmp=argv[1][1];
                        argv[1][1]='\0';
                        htmlwrite(argv[1]);
                        argv[1][1]=tmp;
                }
                argv[1]+=skiplen;

        }
        cout<<"<br>clock="<<clock()<<"<br>";
}

この辺をバックグラウンドで走らせる準備などが必要なのだと学ぶことができました。