Oracle の SPARC M7 プロセッサは、ソフトウェアを高速かつ確実に実行できるようにする Software in Silicon を提供します。Software in Silicon 機能の 1 つが、Silicon Secured Memory (SSM) であり、以前はアプリケーションデータ整合性 (ADI) と呼ばれ、その回路は実行時のデータの破損を引き起こす可能性のある一般的なメモリーアクセスエラーを検出します。
これらのエラーは、誤ったコードまたはサーバーのメモリーへの悪意のある攻撃によって発生することがあります。たとえば、バッファーオーバーフローはセキュリティー上の弱点の主な原因になることがわかっています。さらに、インメモリーデータベースは、重要なデータをメモリー内に保持するため、そのようなエラーがアプリケーションに与える影響が大きくなります。
Silicon Secured Memory は、アプリケーションのメモリーポインタとそれらが指すメモリーにバージョン番号を追加して、最適化された本番コードでのメモリー破損を防ぎます。ポインタバージョン番号が内容のバージョン番号と一致しない場合、メモリーアクセスは中止されます。Silicon Secured Memory は、ソフトウェアエラーによって発生するメモリー破損に対して脆弱な C や C++ などのシステムレベルのプログラミング言語で書かれたアプリケーションで機能します。
Oracle Solaris Studio 12.4 4/15 Platform Specific Enhancement (PSE) には、libdiscoverADI.so ライブラリ (discover ADI ライブラリとも呼ばれる) が含まれ、これは隣接するデータ構造に確実に異なるバージョン番号が指定される更新済みの malloc() ライブラリルーチンを提供します。これらのバージョン番号により、プロセッサの SSM テクノロジがバッファーオーバーフローを検出できます。古いポインタアクセスを防ぐため、メモリー内容のバージョン番号はメモリー構造が解放されるときに変更されます。discover および libdiscoverADI.so によって捕捉されるエラーの詳細については、libdiscoverADI によって捕捉されるエラーを参照してください。
本番環境で Silicon Secured Memory を使用して潜在的なメモリー破損問題を検出することに加えて、アプリケーション開発時にそれを使用して、アプリケーションのテストと動作保証時にそのようなエラーが捕捉されるようにすることもできます。アプリケーションは破損の発生後かなりたってから破損したデータを検出するため、メモリー破損のバグの発見はきわめて困難です。Oracle Solaris Studio 開発者ツールスイートの一部である discover ツールと libdiscoverADI.so ライブラリは、誤ったコードの特定と修正を容易にする追加のアプリケーション情報を提供します。
discover ADI ライブラリの libdiscoverADI は、無効なメモリーアクセスを引き起こすプログラミングエラーを報告します。次の 2 つの方法で使用できます。
LD_PRELOAD_64 環境変数でアプリケーションに discover ADI ライブラリをプリロードすることによって。この方法では、アプリケーションのすべての 64 ビットバイナリを ADI モードで実行します。たとえば、server というアプリケーションを通常どおりに実行した場合、コマンドは次のようになります。
$ LD_PRELOAD_64=install-dir/lib/compilers/sparcv9/libdiscoverADI.so server
特定のバイナリに対して、–i adi オプションを付けた discover コマンドで ADI モードを使用することによって。
% discover -i adi a.out % a.out
エラーはデフォルトで a.out.html ファイルに報告されます。discover レポートの詳細については、discover レポートの分析および出力オプションを参照してください。
libdiscoverADI 使用の要件と制限を参照してください。
libdiscoverADI.so ライブラリは次のエラーを捕捉します。
配列範囲外アクセス (Array out of Bounds Access) (ABR/ABW)
解放済みメモリーへのアクセス (Freed Memory Access) (FMR/FMW)
古いポインタアクセス (Stale Pointer Access) (特殊なタイプの FMR/FMW)
非割り当て読み取り/書き込み (Unallocated Read/Write) (UAR/UAW)
メモリーの二重解放 (Double Free Memory) (DFM)
これらの各エラーのタイプの詳細については、メモリーアクセスエラーと警告を参照してください。
アプリケーションは独自のメモリー割り当ておよび解放リストを管理できます (たとえば、プログラムで大きなチャンクのメモリーを割り当てたり、それを分割したりすることによって)。ADI バージョン管理 API を使用して管理対象メモリーのエラーを捕捉する方法については、アプリケーションデータ整合性と Oracle Solaris Studio を使用したメモリーアクセスエラーの検出と修正に関するドキュメントを参照してください。
完全な例については、discover ADI モードの使用例を参照してください。
次のオプションは、ADI モードでの計測時に discover レポートで生成される情報の精度と量を指定します。
このフラグを on に設定すると、discover ADI ライブラリはエラーの場所とエラースタックトレースを報告します。この情報は、エラーを捕捉するには十分ですが、エラーを修正するために必ずしも十分なわけではありません。このフラグは、不正なメモリー領域が割り当てられ、解放された場所に関する情報も生成します。たとえば、エラーがArray out of Bounds Access であったことと、その配列が割り当てられた場所が、出力に示されていることがあります。off に設定されている場合、割り当てとスタックトレースは報告されません。デフォルトは on です。
バッファーオーバーフローアクセスが、バッファーの末尾のあと、またはバッファーの先頭の前の大きなオフセットで発生している場合。
libdiscoverADI.so がリソース制限に達した場合。この場合、discover は、エラーがバッファーオーバーフローであるかどうかを判断するために必要な割り当てスタックトレースを維持できる可能性があります。
このフラグを off に設定すると、ADI は正確でないモードで実行されます。正確でないモードでは、正確な命令が実行されてからいくつかの命令 (ソース行) が実行されたあとに、メモリー書き込みエラーが捕捉されます。正確なモードを有効にするには、このフラグを on (デフォルト) に設定します。
実行時のパフォーマンスを向上させるために、–A off、–P off を指定でき、または両方のオプションを off に設定できます。
discover の ADI モードは、Oracle Solaris Studio 12.4 4/15 Platform Specific Enhancement (PSE) がインストールされている Oracle Solaris 11.2.8 または Oracle Solaris 11.3 以上を実行する SPARC M7 チップ上の 64 ビットアプリケーションでのみ使用できます。
メモリー検査のための計測と同様に、libdiscoverADI.so の関数が同じ割り当て関数に割り込んだ場合、プリロードされたライブラリが競合することがあります。詳細については、プリロードまたは監査を使用するバイナリは互換性がないを参照してください。
libdiscoverADI でコードを検査する場合のその他の制限には次のものが含まれます。
ヒープ検査のみ使用できます。スタック検査、静的配列範囲外検査、リーク検出はありません。
メタデータを格納するために 64 ビットアドレスの未使用のビットを使用するアプリケーションでは動作しません。一部の 64 ビットアプリケーションでは、ロックなど、メタデータを格納するために 64 ビットアドレスの現在未使用の上位ビットを使用することがあります。そのようなアプリケーションでは、discover の ADI モードは機能しません。この機能は、バージョン情報を格納するために、64 ビットアドレスの上位 4 ビットを使用して動作するためです。
たとえば、2 つの連続した割り当ての間の距離など、ヒープアドレスに関する仮定のもとにポインタ演算を行うアプリケーションでは機能しないことがあります。
memcheck モード (–i memcheck で計測) と異なり、ADI モードでは、アプリケーションが実行可能ファイル内で標準メモリー割り当て関数を再定義している場合に、エラーを捕捉しません。アプリケーションがライブラリ内で標準メモリー割り当て関数を再定義している場合は、ADI モードが機能します。
バッファーオーバーフローの解像度は 64 バイトです。64 バイトで整列されている割り当てでは、libdiscoverADI.so は 1 バイト以上の単位でオーバーフローを捕捉します。64 バイトで整列されない割り当てでは、数バイト単位でバッファーオーバーフローを見落とす可能性があります。一般に、1 から 63 バイト単位のオーバーフローは、割り当ての整列と libdiscoverADI.so がキャッシュ行内に割り当てを配置する場所によって捕捉されないことがあります。
–xipo=2 でコンパイルされたバイナリには、誤検出 ADI エラーにつながり、結果としてトラップ処理のためにパフォーマンスの低下をまねくような方法でアドレスを操作するメモリー最適化コードが含まれる可能性がわずかにあります。
このセクションでは、ADI モードを使用した discover によって捕捉され、報告される配列範囲外エラーのあるコードサンプルについて説明します。
次のサンプルコードが testcode.c という名前のファイルにあるものと想定します。
#include <stdio.h> #include <stdlib.h> int main() { char *x = (char*)malloc(512); int *y = (int*)malloc(20*sizeof(int)); char *z = (char*)malloc(64); x[-14] = 0; y[-10] = 0; z[-4] = 0; x[16] = 0; y[20] = 0; z[64] = 0; x[20] = 0; y[26] = 0; z[120] = 0; }
次のコマンドを使用して、テストコードを構築します。
$ cc testcode.c -g -m64
このサンプルアプリケーションを ADI モードで実行するには、次のコマンドを使用します。
$ discover -w - -i adi -o a.out.adi a.out $ ./a.out.adi
このコマンドは、discover レポートに次の出力を生成します。これらのレポートの見方と内容の詳細については、discover レポートの分析を参照してください。
ERROR 1 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e877ff2: main() + 0x3c <test-abrw.c:10> 7: int *y = (int*)malloc(20*sizeof(int)); 8: char *z = (char*)malloc(64); 9: 10:=> x[-14] = 0; 11: y[-10] = 0; 12: z[-4] = 0; 13: x[16] = 0; _start() + 0x108 was allocated at (512 bytes): main() + 0x8 <test-abrw.c:6> 3: 4: int main() { 5: 6:=> char *x = (char*)malloc(512); 7: int *y = (int*)malloc(20*sizeof(int)); 8: char *z = (char*)malloc(64); 9: _start() + 0x108 ERROR 2 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e873ffc: main() + 0x50 <test-abrw.c:12> 9: 10: x[-14] = 0; 11: y[-10] = 0; 12:=> z[-4] = 0; 13: x[16] = 0; 14: y[20] = 0; 15: z[64] = 0; _start() + 0x108 was allocated at (64 bytes): main() + 0x28 <test-abrw.c:8> 5: 6: char *x = (char*)malloc(512); 7: int *y = (int*)malloc(20*sizeof(int)); 8:=> char *z = (char*)malloc(64); 9: 10: x[-14] = 0; 11: y[-10] = 0; _start() + 0x108 ERROR 3 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e876080: main() + 0x64 <test-abrw.c:14> 11: y[-10] = 0; 12: z[-4] = 0; 13: x[16] = 0; 14:=> y[20] = 0; 15: z[64] = 0; 16: x[20] = 0; 17: y[26] = 0; _start() + 0x108 was allocated at (128 bytes): main() + 0x18 <test-abrw.c:7> 4: int main() { 5: 6: char *x = (char*)malloc(512); 7:=> int *y = (int*)malloc(20*sizeof(int)); 8: char *z = (char*)malloc(64); 9: 10: x[-14] = 0; _start() + 0x108 ERROR 4 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e874040: main() + 0x70 <test-abrw.c:15> 12: z[-4] = 0; 13: x[16] = 0; 14: y[20] = 0; 15:=> z[64] = 0; 16: x[20] = 0; 17: y[26] = 0; 18: z[120] = 0; _start() + 0x108 was allocated at (64 bytes): main() + 0x28 <test-abrw.c:8> 5: 6: char *x = (char*)malloc(512); 7: int *y = (int*)malloc(20*sizeof(int)); 8:=> char *z = (char*)malloc(64); 9: 10: x[-14] = 0; 11: y[-10] = 0; _start() + 0x108 ERROR 5 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e876098: main() + 0x84 <test-abrw.c:17> 14: y[20] = 0; 15: z[64] = 0; 16: x[20] = 0; 17:=> y[26] = 0; 18: z[120] = 0; 19: 20: } _start() + 0x108 was allocated at (128 bytes): main() + 0x18 <test-abrw.c:7> 4: int main() { 5: 6: char *x = (char*)malloc(512); 7:=> int *y = (int*)malloc(20*sizeof(int)); 8: char *z = (char*)malloc(64); 9: 10: x[-14] = 0; _start() + 0x108 ERROR 6 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e874078: main() + 0x90 <test-abrw.c:18> 15: z[64] = 0; 16: x[20] = 0; 17: y[26] = 0; 18:=> z[120] = 0; 19: 20: } 21: _start() + 0x108 was allocated at (64 bytes): main() + 0x28 <test-abrw.c:8> 5: 6: char *x = (char*)malloc(512); 7: int *y = (int*)malloc(20*sizeof(int)); 8:=> char *z = (char*)malloc(64); 9: 10: x[-14] = 0; 11: y[-10] = 0; _start() + 0x108 DISCOVER SUMMARY: unique errors : 6 (6 total)