
現代的なシステム向けに設計された堅牢化されたアロケータ。AndroidのBionic libcに統合されており、他のLinuxベースのプラットフォームで使用するための動的ライブラリとしてmuslやglibcと外部で併用することもできます。今後、移植性と統合性はさらに向上していく予定です。
これはセキュリティに焦点を当てた汎用メモリアロケータであり、malloc APIといくつかの拡張機能を提供します。ヒープ破壊の脆弱性に対する大幅なハードニングを提供します。セキュリティ重視の設計はまた、従来のアロケータ設計よりもメタデータのオーバーヘッドと断片化によるメモリの浪費を大幅に削減します。アロケータのマイクロベンチマークではなく、長期的なパフォーマンスとメモリ使用量に焦点を当てた、適切な総合パフォーマンスの提供を目指しています。設定可能な数の完全に独立したアリーナを通じてスケーラビリティを提供し、アリーナ内の内部ロックはさらにサイズクラスごとに分割されています。
このプロジェクトは現在、Bionic(Android)、musl、glibcをサポートしています。将来的には他の非Linuxオペレーティングシステムをサポートする可能性があります。Android向けにはカスタム統合とその他のハードニング機能があり、これらは将来的にmusl向けにも計画されています。glibcのサポートはmalloc実装の置き換えに限定されます。なぜならmuslはより堅牢でクリーンな基盤であり、同じユースケースをカバーできるからです。
このアロケータは、OpenBSD mallocを様々な追加のセキュリティ機能で拡張することに基づいた以前の実装の後継として意図されています。依然としてOpenBSD mallocの設計に大きく基づいていますが、ハッシュテーブル実装を再利用する以外は既存のコードには基づいていません。設計上の主な違いは、バグの発見ではなくハードニングのみに焦点を当てていること、内部断片化を削減するために4kを超えるスラブサイズとともにきめ細かいサイズクラスを使用していること、カーネルがきめ細かいmmapランダム化を持っていることに依存していないこと、そして大きなアドレス空間を積極的に活用するために64ビットのみをターゲットにしていることです。実装アプローチには多くの小さな違いがあります。OpenBSD mallocに対して以前に行われた拡張を組み込んでおり、カナリア用の割り当てへのパディング追加(現在のOpenBSD mallocのカナリアとは異なる)、既存の解放時クリアリングに連動した解放後書き込み検出、割り当ての隔離のための既存のランダム化配列と並ぶキュー、隔離された割り当てに対する適切な二重解放検出が含まれます。独自のランダムベースを持つサイズクラスごとのメモリ領域は、PartitionAllocにおけるサイズおよびタイプベースのパーティショニングに緩やかに着想を得ています。OpenBSD mallocへの計画された変更はあまりにも広範かつ侵襲的になったため、このプロジェクトは目標をよりよく達成できる新しい実装として開始されました。32ビットの場合、このアロケータは基本的にその環境をサポートしないため、代わりに小さな拡張を加えたOpenBSD mallocの移植版を使用できます。
Debian stable(現在はDebian 13)が、サポートされる依存関係の最も古いセットを決定します:
Androidの場合、Linux GKI 6.1、6.6、6.12ブランチがサポートされています。
ただし、より新しいリリースの使用を強く推奨します。依存関係の古いバージョンは現時点では互換性があるかもしれませんが、テストされておらず、明示的にサポートされません。
musl による外部 malloc 置き換えには、musl 1.1.20 が必要です。ただし、 将来的には C 標準ライブラリ実装に対する他のハードニングとともに、 より優れたパフォーマンスを提供するカスタム統合が行われる予定です。
Android については、現在の世代で活発に開発されている Android
Open Source Project のメンテナンスブランチのみがサポートされ、現在は android17-release を意味します。
preload.sh スクリプトは、glibc または musl を使用する動的リンクされた
実行ファイルでのテストに使用できます:
./preload.sh krita --new-image RGBA,U8,500,500
ガードスラブおよび大きな割り当てガード領域によって引き起こされる多数のマッピングに対応するため、vm.max_map_count sysctl を大幅に増やす必要がある場合があります。マッピングの数は CONFIG_GUARD_SLABS_INTERVAL を大幅に増やすことでも劇的に削減できますが、この機能はパフォーマンスとメモリ使用量のコストが低いため、推奨されません。
C 標準ライブラリに統合するとわずかに優れたパフォーマンスを提供でき、C 標準ライブラリおよび動的リンカ実装内で同様のハードニングの他の機会があります。たとえば、このアロケータが異なるサイズクラス間で提供するのと同様の分離を動的ライブラリに提供するために、ライブラリ領域を実装できます。これは Bionic および musl C 標準ライブラリのハードニングされたバリアントの一部として提供されることが意図されています。
シンプルな自動テストのコレクションが提供されており、次のように make コマンドで実行できます:
make test
seccomp-bpf フィルタ内でプロセスを強制終了するのではなく、mprotect PROT_READ|PROT_WRITE システムコールを許可するには、OpenSSH 8.1 以降が必要です。
GrapheneOS では、hardened_malloc は標準 C ライブラリに標準の malloc 実装として統合されています。他の Android ベースのオペレーティングシステムは、統合 コード を再利用して提供できます。必要に応じて、hardened_malloc を条件付きでのみ使用して jemalloc をランタイム設定オプションとして残し、ユーザーにパフォーマンスとセキュリティの選択肢を与えることができます。ただし、これは永続状態が信頼されない脅威モデル、すなわち検証済みブートおよびアテステーションのセキュリティを低下させます(アテステーション姉妹プロジェクトを参照)。
hardened_malloc によって作成される非常に多数のガードページに対応するため、vm.max_map_count も大幅に引き上げるようにしてください。これは init.rc(system/core/rootdir/init.rc)内の他の仮想メモリ設定の近くで行えます:
write /proc/sys/vm/max_map_count 1048576
ビルド設定で CONFIG_GUARD_SLABS_INTERVAL を非常に大きな値に設定した場合は、これは不要です。
従来の Linux ベースのオペレーティングシステムでは、hardened_malloc は標準 malloc 実装の置き換えとして libc 実装に統合するか、動的ライブラリとしてロードできます。各実行ファイルを再ビルドしてリンクする代わりに、プリロードライブラリとして /etc/ld.so.preload に追加できます。たとえば、libhardened_malloc.so が /usr/local/lib/libhardened_malloc.so にインストールされている場合、そのフルパスを /etc/ld.so.preload 設定ファイルの行として追加します:
/usr/local/lib/libhardened_malloc.so
この設定ファイルの形式は空白区切りのリストであるため、各ライブラリを別々の行に配置するのが良い習慣です。
最大限の互換性を得るには、AppArmor プロファイルや systemd の ExecPaths= 制限によって引き起こされるプリロードの失敗を避けるため、libhardened_malloc.so を /usr/lib/ にインストールできます。次の形式のログを確認してください:
ERROR: ld.so: object '/usr/local/lib/libhardened_malloc.so' from /etc/ld.so.preload cannot be preloaded (failed to map segment from shared object): ignored.
LD_PRELOAD 環境変数を使ってケースバイケースでロードする方法は、setuid バイナリのように AT_SECURE が設定されている場合には機能しません。また、一般的に本番環境での使用には推奨されるアプローチではありません。推奨されるのは、グローバルに有効化し、パフォーマンスが重要なケースでは有効化せずにコンテナ/名前空間内でアプリケーションを実行することで例外を設けることです。
hardened_malloc が作成する非常に大量のガードページに対応するため、vm.max_map_count も大幅に引き上げてください。例として、/etc/sysctl.d/hardened_malloc.conf に次のように記述します:
vm.max_map_count = 1048576
ビルド設定で CONFIG_GUARD_SLABS_INTERVAL を非常に大きな値に設定する場合は、これは不要です。
arm64 では、16k ページと 64k ページのサポートをまだ追加していないため、カーネルが 4k ページを使用するように設定されていることを確認してください。また、デフォルトの hardened_malloc 設定では 39 ビットのアドレス空間しか持たないため、完全な 48 ビットアドレス空間に対応する 4 レベルのページテーブルを使用するようにカーネルを設定する必要があります。クラス領域のサイズを大幅に削減すれば 39 ビットのアドレス空間でも動作可能ですが、デフォルト設定では動作しません。
次のように make コマンドへの引数を通じて、コンパイル時にいくつかの設定オプションを指定できます:
make CONFIG_EXAMPLE=false
設定オプションは、移植性、パフォーマンス、メモリ使用量、セキュリティの間に大きなトレードオフが存在する場合に提供されます。中核となる設計上の選択は設定可能ではなく、すべてのオプション機能を無効にしてもアロケータは非常にセキュリティ重視のままです。
設定システムは、2 つの標準プリセットを備えた設定テンプレートシステムをサポートしています: デフォルト設定 (config/default.mk) と軽量設定 (config/light.mk) です。パッケージ作成者は、標準の default と light の両方の設定を同梱することを強く推奨します。ビルドする設定は make VARIANT=light で選択でき、make VARIANT=default は make と同じです。デフォルト以外の設定テンプレートでは、libhardened_malloc-light.so のように -variant というサフィックスが付いたライブラリがビルドされ、ビルドには out の代わりに out-variant ディレクトリが使用されます。
default 設定テンプレートは、通常のオプションのセキュリティ機能をすべて有効にしており (ただしニッチな CONFIG_SEAL_METADATA は除く)、セキュリティのためにパフォーマンスとメモリ使用量を犠牲にする点でかなり積極的です。light 設定テンプレートは、スラブ隔離、解放後書き込みチェック、スロットランダム化を無効にし、ガードスラブ間隔を 1 から 8 に引き上げますが、解放時ゼロ化とスラブカナリアは有効のままにします。light 設定は、主流のアロケータよりもはるかに安全であり、より優れたセキュリティ特性を持ちながら、堅実なパフォーマンスとメモリ使用量を実現します。解放時ゼロ化を無効にするとパフォーマンスはさらに向上しますが、スラブカナリアも同時に無効にしない限り、小さなアロケーションでは大きな違いは生じません。スラブカナリアはメモリ使用量をわずかに増やし、パフォーマンスをわずかに低下させますが、小さなオーバーフローや C 文字列のオーバーフローを緩和するために非常に重要です。スラブカナリアを無効にすることは、アロケーションにヘッダーを持ち、それらに対する基本的な整合性チェックを行う従来のアロケータに対する厳密なアップグレードではなくなるため、ほとんどの場合推奨されません。
パフォーマンスを犠牲にしてメモリ使用量を削減する場合 (これは空のスラブキャッシュと隔離領域のサイズも削減し、現在これらは最大サイズクラスのサイズに基づいているため、多くのメモリを節約します):
make \
CONFIG_N_ARENA=1 \
CONFIG_EXTENDED_SIZE_CLASSES=false
以下のブール型設定オプションが利用可能です: