1. 導入

1.1. まえがき

近年、メモリ安全性はソフトウェアの信頼性およびセキュリティを確保する上で最重要課題の一つとなっている。
特に C 言語や C++ により実装されたサーバソフトウェアは、バッファオーバーフローやユースアフターフリーといった脆弱性を悪用される危険性が高い。
この種の脆弱性は、リモートからの攻撃によって任意コード実行や情報漏洩につながる可能性があり、長年にわたりセキュリティ分野における重大な研究課題となってきた。

この問題に対し、さまざまな対策技術が提案されてきた。代表的なものとして、アドレス空間配置のランダム化(ASLR)、スタックカナリア、メモリサニタイザ(ASan など)、そして代替メモリアロケータ(jemalloc、tcmalloc など)が挙げられる。
これらは一定の効果を発揮しているものの、依然としてメモリ破壊バグが exploit に悪用される余地は残されている。

GrapheneOSプロジェクトは、この問題に対処するため hardened_malloc と呼ばれる強化型メモリアロケータを開発した。
hardened_malloc は境界チェックやガードページの挿入などを通じてメモリ破壊を即座に検出し、クラッシュとして顕在化させることで exploit を未然に防ぐことを目的としている。

本来は Android 向けに設計されたが、Linux 環境においても LD_PRELOAD を利用することで導入可能である。

本調査の動機は、セキュリティと性能のトレードオフを実運用レベルで明確にする点にある。 これまで hardened_malloc の有効性は主にモバイル OS 文脈で議論されてきたが、汎用的なサーバソフトウェアに適用した際の性能・安定性評価は十分に報告されていない。
特に nginx のように広く利用される Webサーバは、インターネットに直接晒されるため攻撃対象になりやすい。 hardened_mallocのような強化型メモリアロケータを適用することで、万一のメモリ破壊バグが悪用される前にクラッシュとして顕在化させられる可能性があり、exploitを未然に防ぐ効果が期待できる。

本稿では、Ubuntu LXC 環境においてnginxを対象にhardened_mallocを導入し、その性能および安定性への影響をベンチマークにより評価する。

1.2. 既存メモリアロケータとの比較

従来から利用されている glibc malloc と、セキュリティを重視して設計された hardened_malloc の特徴を比較すると、 表1 のようになる。

表1: glibc malloc と hardened_malloc の比較

実装主な利用環境特徴セキュリティレベル性能レベル
glibc mallocLinux デフォルトシンプルかつ汎用性重視の実装。互換性と幅広いワークロードでの妥当な性能を優先。標準標準
hardened_mallocGrapheneOS、Linux (LD_PRELOAD)ガードページ、境界チェック、ランダム化、破損検出時の即時クラッシュ。セキュリティ最優先で設計。かなり強い若干劣る

注記:
「セキュリティレベル」および「性能レベル」の評価は、各実装の設計方針や文書化された特徴に基づく相対的な位置づけであり、直接的な定量ベンチマークに基づくものではない。

この表から明らかなように、glibc malloc は互換性と性能のバランスを優先する一方で、セキュリティ機構は最小限にとどまっている。対照的に、hardened_malloc は性能を一部犠牲にしつつも、境界チェックやガードページなどを導入することで、バッファオーバーフロー等の悪用を困難にしている。

2. 手法

2.1. 実験環境

本調査では、Proxmox VE 上の LXC コンテナ環境において Ubuntu を稼働させ、以下の構成を用いて実験を行った。

  • 仮想化基盤: Proxmox VE
  • ホスト CPU: AMD Ryzen 9 3900(12コア24スレッド) @ 3.1GHz
  • コンテナへの割当: vCPU 2コア, メモリ 2 GB
  • コンテナOS: Ubuntu 24.04 LXC
  • カーネルバージョン: Linux 6.14.11-2-pve (x86_64)
  • Webサーバ: nginx 1.24.0(APTパッケージ版)
  • hardened_malloc: GrapheneOS公式リポジトリ よりソースビルド (コミット: 33ed302, 2025-09-23 時点)
  • ベンチマークツール: ApacheBench (ab)

2.2. hardened_malloc の導入方法

hardened_malloc は LD_PRELOAD 機構を用いることで既存の malloc 実装を置換できる。
本調査では nginx サービスに対し、以下の systemd ドロップイン設定を適用することで hardened_malloc を導入した。

2.2.1. ビルド

GrapheneOS 公式リポジトリを取得し、make によりビルドを実施した。

git clone https://github.com/GrapheneOS/hardened_malloc.git
cd hardened_malloc
make -j$(nproc)

ビルド後、out/libhardened_malloc.so が生成される。

2.2.2. nginx への適用

systemd のドロップイン設定を用いて、nginx サービスに対して hardened_malloc を適用した。 systemctlのserviceファイルに以下を追記する。

[Service]
Environment=LD_PRELOAD=/opt/hardened_malloc/out/libhardened_malloc.so

この設定を保存した後、systemctl daemon-reload および systemctl restart nginx を実行して反映させた。

2.2.3. 導入確認

hardened_malloc が正しくロードされているかを確認するため、nginx プロセスのメモリマップを参照した。

導入後、/proc/$PID/maps を確認し、対象プロセスにおいて libhardened_malloc.so がロードされていることを検証した。

$ PID=$(pidof nginx | awk '{print $1}')
$ cat /proc/$PID/maps | grep hardened_malloc
7f6d3ebf6000-7f6d3ebf8000 r--p 00000000 fc:07 1588226                    /opt/hardened_malloc/out/libhardened_malloc.so
7f6d3ebf8000-7f6d3ebfd000 r-xp 00002000 fc:07 1588226                    /opt/hardened_malloc/out/libhardened_malloc.so
7f6d3ebfd000-7f6d3ebff000 r--p 00007000 fc:07 1588226                    /opt/hardened_malloc/out/libhardened_malloc.so
7f6d3ebff000-7f6d3ec00000 r--p 00008000 fc:07 1588226                    /opt/hardened_malloc/out/libhardened_malloc.so
7f6d3ec00000-7f6d3ec01000 rw-p 00009000 fc:07 1588226                    /opt/hardened_malloc/out/libhardened_malloc.so

2.3. ベンチマーク条件

性能評価には ApacheBench を用い、以下の条件でベンチマークを実施した。

  • Document Path: /(固定レスポンス 21 bytes)

  • Concurrency Level: 100

  • Total Requests: 100,000

これらの条件下において、glibc実行環境とhardened_malloc実行環境を比較した。

2.4. ベンチマーク

2.4.1. glibcでの計測

以下コマンドでベンチマークを実施する。

ab -n 100000 -c 100 http://127.0.0.1:8080/ > result_normal.txt

result_normal.txt:

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)


Server Software:        nginx/1.24.0
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /
Document Length:        21 bytes

Concurrency Level:      100
Time taken for tests:   5.638 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      18700000 bytes
HTML transferred:       2100000 bytes
Requests per second:    17735.72 [#/sec] (mean)
Time per request:       5.638 [ms] (mean)
Time per request:       0.056 [ms] (mean, across all concurrent requests)
Transfer rate:          3238.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   0.6      2       5
Processing:     1    3   0.9      3      10
Waiting:        0    2   0.7      2       6
Total:          3    6   1.2      5      13

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      5
  75%      6
  80%      6
  90%      7
  95%      9
  98%     10
  99%     10
 100%     13 (longest request)

2.4.2. hardened_mallocでの計測

以下コマンドでベンチマークを実施する。

ab -n 100000 -c 100 http://127.0.0.1:8080/ > result_hardened.txt

result_hardened.txt:

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)


Server Software:        nginx/1.24.0
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /
Document Length:        21 bytes

Concurrency Level:      100
Time taken for tests:   6.248 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      18700000 bytes
HTML transferred:       2100000 bytes
Requests per second:    16006.13 [#/sec] (mean)
Time per request:       6.248 [ms] (mean)
Time per request:       0.062 [ms] (mean, across all concurrent requests)
Transfer rate:          2922.99 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   0.8      2       7
Processing:     1    4   1.3      3      11
Waiting:        0    2   0.9      2       8
Total:          3    6   1.7      5      15
WARNING: The median and mean for the initial connection time are not within a normal deviation
        These results are probably not that reliable.

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      6
  75%      7
  80%      8
  90%      9
  95%     10
  98%     11
  99%     12
 100%     15 (longest request)

3. 結果

単発で性能差を確認した後、多条件での安定性を検証した

3.1. 単発ベンチマーク

表2: 単発ベンチマーク結果概要(抜粋)

MetricDefault mallochardened_mallocDifference
Requests/sec17,73616,006≈ -9.7%
Time per request5.638 ms6.248 ms≈ +10.8%
Max response time13 ms15 ms+15%

ApacheBench を用いた評価の結果、通常の malloc 環境におけるスループットは平均 17,736 リクエスト毎秒であり、平均応答時間は 5.638 ミリ秒であった。 最大応答時間は 13 ミリ秒であり、全てのリクエストが正常に処理され、失敗は一件も発生しなかった。

一方、hardened_malloc 環境においては、スループットは 16,006 リクエスト毎秒に低下し、平均応答時間は 6.248 ミリ秒となった。最大応答時間は 15 ミリ秒であり、通常 malloc 環境と比較して 2 ミリ秒の増加が確認された。 ただし、この場合においても失敗リクエストは一件も発生せず、安定性に問題は認められなかった。

以上の結果から、hardened_malloc の導入によってスループットは約 9.7% 減少し、平均応答時間は約 0.6 ミリ秒増加したことが明らかとなった。最大応答時間の増加は限定的であり、安定性は維持されていることから、性能低下はセキュリティ強化機構の導入に伴うオーバーヘッドとして許容範囲に収まっていると考えられる。

3.2. マルチランベンチマーク結果

より包括的に性能を確認するため、リクエスト数(100k / 500k / 1M)、並列数(100 / 500)、レスポンス長(短文9B / 中文23B / 長文1KB)を組み合わせたマルチラン実験を行った。 結果の概要を 表3 に示す。

表3: マルチランベンチマーク結果概要(抜粋)

リクエスト数 / 並列数レスポンス長アロケータRequests/sec平均応答時間(ms)最大遅延(ms)平均CPU%最大CPU%
1M / 10023B (Hello)glibc17,1835.81419.224.8
1M / 10023B (Hello)hardened17,1265.838119.825.0
1M / 50023B (Hello)glibc16,95529.513819.624.0
1M / 50023B (Hello)hardened17,02529.414919.125.0
500k / 1009B (Short)glibc16,9535.914319.024.0
500k / 1009B (Short)hardened12,9247.744315.426.0
500k / 5001KB (AAAA..)glibc12,50540.0241215.829.0
500k / 5001KB (AAAA..)hardened15,30232.7125818.425.0

マルチランベンチマーク結果のフルバージョン(CSVファイル)は こちら から。

4. 考察

4.1. 単発ベンチ

実験結果は、hardened_malloc の導入がスループットを約 10% 低下させる一方で、安定性への影響は認められないことを示した。この性能低下は、hardened_malloc が実装するメモリアクセス境界チェック、ガードページ、ランダム化などの保護機構によるオーバーヘッドに起因すると考えられる。

すなわち、セキュリティ強化と性能低下は不可避のトレードオフの関係にあるが、本評価で確認された程度のオーバーヘッドは、セキュリティ上の利得を考慮すれば十分に許容可能である。

4.2. マルチランベンチ

multi-run 実験の結果、スループット(Requests/sec)は hardened_malloc と glibc の間でほぼ互角となり、条件によっては逆転する場面も見られた。 差は概ね ±5% の範囲に収まり、単発ベンチマークで観測された約10%の性能低下はワークロード依存であり、常に顕著に現れるわけではなかった。

Requests_per_sec

CPU使用率は平均で 16〜20%、ピークでも 25〜30% 程度に収まり、hardened_malloc によるオーバーヘッドは 1〜2% 程度にとどまった。 統計的には誤差範囲に近く、実運用上で問題になるほどではない。

cpu_bench

レイテンシに関しては、平均応答時間は glibc と同等で、特に低並列条件では差がほとんど見られなかった。一方で高並列(c=500)の場合には 1〜2ms 程度 hardened_malloc が遅れるケースがあった。 ただし最悪遅延(tail latency)はglibc側が数千msに達するケースもあり、むしろ hardened_malloc の方が安定している可能性も考えられる。

latency

全体として、hardened_malloc は本番環境での導入に伴うオーバーヘッドがごく小さく、セキュリティ強化とのトレードオフは十分許容できる結果となった。すべてのケースで失敗リクエストはゼロであり、安定性への悪影響は観測されなかった。

5. まとめ

本調査では、Ubuntu LXC 環境で稼働する nginx に hardened_malloc を適用し、その性能および安定性を評価した。その結果、スループットは約5〜10%低下したものの、安定性に問題は認められず、セキュリティ強化の観点から十分に実用的であることを確認した。

今後の課題として、hardened_malloc 以外の代替メモリアロケータ(jemalloc, tcmalloc, mimalloc 等)を対象に同様のベンチマークを行い、性能特性およびセキュリティ耐性の差異を体系的に評価することが挙げられる。

また、本調査では nginx を対象に評価を行ったが、他のサーバソフトウェア(例: bitcoind, ElectrumX, Redis, PostgreSQL, Apache HTTP Server)に対しても適用し、さらに長時間稼働環境での評価を行うことで、より包括的かつ実運用に近い知見を得ることができると考えられる。