wolfSSL 静的バッファ確保 オプション

1.イントロダクション

このドキュメントはwolfSSLライブラリにおける「静的バッファ確保」オプションについてその概要と使用方法を解説します。

wolfSSLでは動的メモリ管理が提供されていること、すなわち、malloc/free関数でバッファを確保/解放できることを前提に処理が記述されています。wolfSSLが内部で使用している暗号化ライブラリwolfCryptでは動的メモリを使用しない設定も可能ですが、TLSハンドシェーク処理を記述しているwolfSSLライブラリ内では動的メモリの使用を前提としたコード実装となっています。

一方、組み込み機器ではOSを使用しない(いわゆるベアメタル)環境で使用する場合、あるいはリアルタイムOSを使用している場合でも動的メモリ管理機能が提供されていない場合があります。このような環境でwolfSSLを使用する方法として静的バッファ確保機能を提供しています。

2.静的バッファ確保オプションの動作

2.1 静的バッファ確保の基本動作

先に説明した「動的メモリ管理」はバッファを「指定されたサイズ(可変長)」に動的に切り出して提供する管理方法です。バッファの使用効率は高いですが処理が比較的複雑です。一方、wolfSSLが提供する「静的バッファ確保機能」は、あらかじめ(静的に)用意した何種類かのバッファのなかから、要求したサイズに近いものを検索して提供するメモリ管理機能のことを言います。バッファの要求元には要求サイズ以上の大きさを持ったメモリブロックが割り当てられることがあります(そのため使用効率が低下します)が、バッファの確保と解放はmalloc/freeと同様に行え、割り当て処理は単純ですみます。任意サイズのメモリブロックを動的に確保する動的メモリ確保を模擬した機能となっています。

静的バッファ確保機能の使用はwolfSSLにとっては動的メモリ機能と等価に使用されます。この機能はwolfSSLがメモリの確保/解放をXMALLOC/XFREE関数呼び出しに抽象化してあることで実現できています。一旦、静的バッファ確保機能が設定されると、以降wolfSSLは内部で使用するバッファや他の構造体の確保に静的バッファ確保機能を使用します。この機能はWOLFSSL_CTXに対して設定するので、このオブジェクトの生存期間中は機能が継続します。

WOLFSSL_CTXに設定された静的バッファ確保機能はスレッドセーフとなっています。同一のWOLFSSL_CTXを異なるスレッドで共有しながら使用している場合でも、バッファの確保/解放はwolfSSL内部で排他制御しながら利用します。

また、RTOSで使用されているメモリプールを使ったメモリ機能では未使用のメモリブロックが見つからない場合にはそのスレッド(タスク)は空きブロックが発生するまでサスペンドされますが、wolfSSLの静的バッファ確保機能にはそのような待ち合わせ機能はありません。

2.2 静的バッファの用途指定

静的バッファ確保機能では、2つの用途別にメモリを分けることが可能です。つまり、一般的な目的用I/O用に使用するバッファを分けて割り当て/解放を行うことが可能です。I/Oに使用するバッファはTLSでの典型的必要サイズから比較的大きく(17KB程度)設定されていて、それ以外の一般的な用途のバッファサイズと異なる点と、I/Oを行うスレッド(タスク)とそれ以外のスレッドでのメモリ獲得/解放に伴う排他制御を排除したいという2つの理由で用途別に分けることを推奨しています。

さらに、バッファを設定した際に、同時に生成できるWOLFSSLオブジェクトの最大数を制限することができます。最大数を制限するとwolfSSL_new()関数を使う度に生成できるWOLFSSLオブジェクトの数がチェックされ、上限を超えるとエラーとなります。

2.3 静的バッファ確保機能の有効化

wolfSSLのビルド時に静的バッファ確保オプションを有効化します。autoconfツールを使用してビルドするシステムでは次のように”–enable-staticmemory“を指定します。

$./configure --enable-staticmemory

あるいはuser_settings.hを使用している場合には次のマクロ定義を追加します:

user_settings.h

    #define WOLFSSL_STATIC_MEMORY

さらに、静的バッファ確保機能は与えられたバッファから確保するメモリブロックが枯渇した際に、NULLを返さず標準関数のmalloc()を追加で呼び出す実装となっています。もし、環境に動的メモリ管理機能が提供されていない場合にはリンクエラーとなります。したがって、この機能をディセーブルにする為に、WOLFSSL_NO_MALLOCマクロも定義しておきます:

user_settings.h

    #define WOLFSSL_STATIC_MEMORY
    #define WOLFSSL_NO_MALLOC

2.4 静的バッファ確保機能の利用

(1) 静的バッファの設定関数とその引数

上記設定でwolfSSLをビルドすることにより、静的バッファ確保機能が有効となります。さらにこの機能を利用するにあたってはアプリケーションは次の関数を呼び出してヒープとして使用するバッファを指定します:

int wolfSSL_CTX_load_static_memory(
        WOLFSSL_CTX** ctx,         /* 生成したWOLFSSL_CTXを受け取る為の変数へのポインタ */
        wolfSSL_method_func method,/* メソッド */
        unsigned char* buf,        /* ヒープとして使用するバッファへのポインタ */
        unsigned int sz,           /* ヒープとして使用するバッファのサイズ */
        int flag,                  /* ヒープの使用用途 */
        int max);                  /* 許可する最大平行オブジェクト数 */
  • 引数ctxには生成されたWOLFSSL_CTX構造体へのポインタを受け取る変数のアドレスを指定します。
  • 引数methodにはwolfSSLv23_client_method_ex()などの”_ex“が付いた関数ポインタを指定します。使用できる関数は5章にリストアップしています。
  • 引数buf,szにはそれぞれヒープに使用するバッファのアドレスとそのサイズを指定します。設定すべきバッファのサイズの決定については「3.3 必要バッファのサイズの取得」を参照してください。
  • 引数flagには一般用途あるいはI/O用を指定する用途と静的バッファの確保状況のトラッキングを行うかどうかのフラグを組み合わせて指定します。一般用途を指定する場合には”0″あるいはWOLFMEM_GENERALを指定します。I/O用としての指定はWOLFMEM_IO_POOあるいはWOLFMEM_IO_POOL_FIXEDを指定します。静的バッファの確保状況のトラッキングを行う場合には用途を指定する値にWOLFMEM_TRACK_STATSをORして指定します。
  • 引数maxは引数flagで指定したバッファの用途に関係します。バッファの用途が一般用の場合には、生成するWOLFSSLオブジェクトの最大同時生成数(同時に存在できるオブジェクト数)を設定することになります。制限を行う必要がなければ0を指定します。0以外の制限値を指定した場合には、その後のwolfSSL_newの呼び出しで生成するWOLFSSLオブジェクトの同時オブジェクト数が設定値を超える際には生成が失敗することになります。

(2) 静的バッファの設定関数の呼び出し方

静的バッファ確保機能を利用する際には、このwolfSSL_CTX_load_static_memory関数を2回呼び出します。最初は一般用途用にバッファを設定し、さらにそのバッファを使ってWOLFSSL_CTX構造体を確保します。2回目の呼び出しでは、I/O用バッファを設定します:

WOLFSSL_CTX* ctx = NULL; /* WOLFSSL_CTXを生成する場合にはNULLを指定 */
int ret;

#define MAX_CONCURRENT_TLS  0
#define MAX_CONCURRENT_IO   0

unsigned char GEN_MEM[GEN_MEM_SIZE];
unsigned char IO_MEM[IO_MEM_SIZE]; 

   /* 最初の呼び出しで一般用途のバッファを設定してそこからWOLFSSL_CTXを生成する */
   ret = wolfSSL_CTX_load_static_memory(
           &ctx,                          /* ctx変数の内容にはNULLをセットしておく */
           wolfSSLv23_client_method_ex(), /* "_ex"の付いた関数を使う */
           GEN_MEM, GEN_MEM_SIZE,         /* 一般用途のバッファとそのサイズ */
           WOLFMEM_GENERAL,               /* 一般用途で使用 */
           MAX_CONCURRENT_TLS);           /* 最大許容同時接続数 */

   /* 次は生成したWOLFSSL_CTXにI/O用途のバッファをセットする */
   ret = wolfSSL_CTX_load_static_memory(
           &ctx,                          /* ctxには生成済み構造体が格納されている */
           NULL,                          /* 今度はctxの生成は行わないのでNULLをセット */
           IO_MEM, IO_MEM_SIZE,           /* I/O用途のバッファとそのサイズ */
           WOLFMEM_IO_FIXED,
           MAX_CONCURRENT_IO);

この後、WOLFSSL_CTX構造体の使用を終了した後は、通常のwolfSSL_CTX_free()を使って解放してください。

3.静的バッファ確保機能の調整

wolfSSLで提供しているこの静的バッファ確保機能では指定されたバッファを次の図の様に複数の”バケット(bucket)”という領域に分けて管理します。バケット内には同一サイズの複数のメモリブロックがリンクされています。下図の中にはメモリブロックの管理のための構造は省略していますが、実際にはそれらの管理領域も含めて必要なサイズを持つバッファが与えられなければなりません。

上記構造は一般用、I/O用のいずれのバッファにも適用されますが、I/O用バッファにはBucketは一種類しかありません。

3.1 一般用バッファの設定値

各バケットはバケット内に含むメモリブロックの数とそのサイズに応じて大きさが異なります。

使用する各領域のメモリブロックサイズとブロックの個数が/wolfssl/wolfcrypt/memory.hに次のようなマクロで定義してあります。

/wolfssl/wolfcrypt/memory.h

    #define WOLFSSL_STATIC_ALIGN 16      /* 適用されるアライメント(デフォルト16バイト)*/
    #define WOLFMEM_MAX_BUCKETS  9    /* バケット数 */
  #define WOLFMEM_IO_SZ        16992   /* I/O用バッファサイズ  */
    #define LARGEST_MEM_BUCKET   16128   /* 最大ブロックのサイズ */
    #define WOLFMEM_BUCKETS      64,128,256,512,1024,2432,3456,4544,LARGEST_MEM_BUCKET
    #define WOLFMEM_DIST         49,10,6,14,5,6,9,1,1
  • WOLFSSL_STATIC_ALIGNはバッファのアライメントサイズを指定します。デフォルトで16バイトです。御使用のMCUでのアライメントサイズに合わせて変更する必要があります。
  • WOLFMEM_MAX_BUCKETSがバケットの数を示しています。9種類のサイズのバケットを使用することを意味しています。
  • WOLFMEM_BUCKETSが各バケット内のブロックのバイト数を小さいものから順にコンマ区切りで指定しています。この定義は一般用バッファに適用されます。
  • WOLFMEM_DISTが各バケットに含まれる同一サイズのブロックの数をWOLFMEM_BUCKETSの各ブロックに対応するようにそれらの個数をコンマ区切りで指定しています。この定義は一般用バッファに適用されます。

上記の例でいえば、ブロックサイズ64バイトのバケットが最小のサイズであり、そのバケットには49個のメモリブロックを用意することになります。次に大きいバケットはブロックサイズ128バイトで10個のメモリブロックを用意することを意味します。

上記定義値はデフォルトの値として使用していただけますが、実際の環境での使用時には各バケットのサイズとそこに含まれるメモリブロック数は調整が必要かもしれません。

3.2 I/O用バッファの設定値

I/O用途のバッファは上記一般用途と管理方法は同じですが、バケット数は”1”、バケット内のメモリブロックは1つだけです。またメモリブロックのサイズはWOLFMEM_IO_SZで定義された値となっています。

このI/OバッファのサイズはTLSハンドシェークで送受信される最大パケットサイズを考慮して設定されていますが、この最大パケットサイズをwolfSSL_CTX_UseMaxFragment()を使ってより小さい値に設定することが可能です。この関数を使って最大パケットサイズを小さくした場合には、その値(この例では660バイト)をWOLFMEM_IO_SZとして設定してください。

$ ./configure --enable-staticmemory C_EXTRA_FLAGS="-DWOLFMEM_IO_SZ=660"

3.3 必要バッファサイズの取得

静的バッファ確保機能に割り当てるバッファサイズ(すなわちwolfSSL_CTX_load_static_memory関数に渡すバッファサイズ)の決定に有用な関数を用意してあります。この章の冒頭でバッファが内部でバケットと管理領域からなる構造に構成されて使用されることを説明しました。実際にバッファのサイズを決定する際には、メモリブロックのサイズと管理領域の占めるサイズとパディングによる余分に必要なサイズも含めて計算する必要があります。この計算を行い、必要なバッファサイズを返してくれる関数を用意してあります。

wolfSSL_StaticBufferSz関数は、直前のセクションで紹介したマクロ定義値を基にに必要なバッファサイズを計算して返します。この関数が0より大きい値を返すまで、第2引数に与えるサイズは1000などの適当な値からスタートし、戻り値が正となるまでサイズを増やして何度も呼び出します。第3引数のflagには一般用バッファサイズを計算する場合にはWOLFMEM_GENERALを与え、I/O用バッファサイズを計算する場合にはWOLFMEM_IO_FIXEDあるいはWOLFMEM_IO_POOLを指定してください。

int wolfSSL_StaticBufferSz(byte* buffer, /* バッファアドレス */
                           word32 sz,    /* バッファサイズ */
                           int flag);    /* バッファの用途 */

この関数を使って一般用と、I/O用に必要なバッファサイズを取得し、前述のwolfSSL_CTX_load_static_memory関数に渡してください。

一旦バッファサイズが決定したら、上記wolfSSL_StaticBufferSz関数は呼び出す必要はありませんので、製品コードから呼び出し部分をコメントアウトするか削除していただけます。

4.静的バッファ利用状況のトラッキング

静的バッファ確保機能を利用する際に、バッファの確保、解放に関する使用状況を記録させることができます。この使用状況を記録する機能は静的バッファ確保機能にデフォルトで含まれていますからビルド時の有効化として別段のマクロ設定は必要ありません。機能の有効化は実行時に行います。

4.1 トラッキングの有効化

トラッキング機能の有効化は先に説明したwolfSSL_CTX_load_static_memory関数の第5引数にWOLFMEM_TRACK_STATSをORして指定します。wolfSSL内部にWOLFSSL_MEM_CONN_STATS構造体が確保されそこにメモリブロックの使用状況が記録されていきます。

wolfssl/wolfcrypt/memory.h

struct WOLFSSL_MEM_CONN_STATS {
        word32 peakMem;   /* メモリ使用量最大値(バイト数)  */
        word32 curMem;    /* 現在のメモリ使用量 */
        word32 peakAlloc; /* メモリ確保量最大値 */
        word32 curAlloc;  /* 現在のメモリ確保数 */
        word32 totalAlloc;/* 累計メモリ確保回数 */
        word32 totalFr;   /* 累計メモリ解放回数 */
};

4.2 メモリ使用状況の取得

トラッキングが有効になった時点からメモリブロックの使用状況は記録が有効となります。プログラム実行の任意の時点で次の関数を呼び出して、引数で渡したWOLFSSL_CTXあるいはWOLFSSLオブジェクトに静的バッファ確保機能が使用されているか否かを戻り値で返します。使用されている場合には戻り値として”1″を返します。さらに記録されているメモリの使用状況を取得することができます。

int wolfSSL_CTX_is_static_memory(WOLFSSL_CTX* ctx, 
                                 WOLFSSL_MEM_STATS* mem_stats);

int wolfSSL_is_static_memory(WOLFSSL* ssl,
                             WOLFSSL_MEM_STATS* mem_stats);

上記関数の引数として渡したWOLFSSL_MEM_STATS構造体は:

    struct WOLFSSL_MEM_STATS {
        word32 curAlloc;  /* 現在のメモリ確保数 */
        word32 totalAlloc;/* 累計メモリ確保回数 */
        word32 totalFr;   /* 累計メモリ解放回数 */
        word32 totalUse;  /* N/A */
        word32 avaIO;     /* I/O用ブロックの空きブロック数 */
        word32 maxHa;     /* 一般用に設定した最大コネクション数 */
        word32 maxIO;     /* I/O用に設定した最大コネクション数 */
        word32 blockSz[WOLFMEM_MAX_BUCKETS]; /* ブロックサイズの配列 */
        word32 avaBlock[WOLFMEM_MAX_BUCKETS];/* 空きブロック数の配列 */
        word32 usedBlock[WOLFMEM_MAX_BUCKETS];
        int    flag;   /* 静的メモリ管理機能に設定したフラグ(バッファ用途等) */
    };

この構造体に返却された値が呼び出した時点の静的バッファ管理状態を示します。この機能はメモリーリークの検出や無駄に多く割り当てたメモリブロック数の調査などに利用できます。

5.静的バッファ管理API

5.1 WOLFSSL_METHOD構造体を返す関数群

ここで紹介する関数はwolfSSL_CTX_load_static_memory関数の第2引数に指定するWOLFSSL_METHOD構造体へのポインタを取得する為に使用する関数です。こららの関数はアプリケーションをTLSあるいはDTLSプロトコルのクライアントあるいはサーバーのいずれとして動作させるのかによって選択すべき関数が異なります。静的バッファ管理機能を使う際には必ず関数名の最後に”_ex”が付加された以下の関数を指定してください。こららの関数は”_ex”がつかない関数と機能は同じで、内部でWOLFSSL_METHOD構造体を有効になった静的バッファ管理機能を使って確保する点だけが異なります。各関数の詳細はwolfSSLマニュアルの”wolfSSL APIリファレンス>wolfSSLコンテクストの設定”を参照してください。

TLSクライアント用関数

TLSサーバ用関数

DTLSクライアント用関数

DTLSサーバ用関数

5.2 静的バッファ管理機能 API

APIリファレンス

関数機能
wolfSSL_CTX_load_static_memoryWOLFSSL_CTXに静的バッファ管理用のバッファを設定します。
wolfSSL_CTX_is_static_memory静的バッファ管理が設定されているかと設定されている場合にはその使用状況を取得します。
wolfSSL_is_static_memory静的バッファ管理が設定されているかと設定されている場合にはその使用状況を取得します。
wolfSSL_StaticBufferSz静的バッファ管理で必要なバッファサイズを計算します。