上記のバッファには、0xfeedface のパターンが頻繁に現れています。このパターンは、レッドゾーンインジケータと呼ばれるものです。これによって、アロケータ (および問題のデバッギングを行なっているプログラマ) は、「バグのある」コードがバッファの境界を超えているかどうかを判断することができます。レッドゾーンの後に追加の情報があります。このデータの内容は他の要因によって異なります (「メモリー割り当てログ」を参照)。レッドゾーンとそのあとのデータ領域は、まとめて buftag 領域と呼ばれます。 図 6-1 に、この情報の要約を示します。
バッファのキャッシュに KMF_AUDIT、KMF_DEADBEEF、KMF_REDZONE、またはKMF_CONTENTS フラグが設定されている場合には、そのキャッシュの各バッファに buftag が付加されます。buftag の内容は、KMF_AUDIT が設定されているかどうかにより異なります。
前述のメモリー領域を個別のバッファに分解すると、次のように簡単になります。
0x70a9add8: deadbeef deadbeef ¥ 0x70a9ade0: deadbeef deadbeef +- ユーザーデータ (未使用) 0x70a9ade8: deadbeef deadbeef / 0x70a9adf0: feedface feedface -- レッドゾーン 0x70a9adf8: 70ae3260 8440c68e -- デバッギングデータ 0x70a9ae00: 5 4ef83 ¥ 0x70a9ae08: 0 0 +- ユーザーデータ (割り当て済み) 0x70a9ae10: 1 bbddcafe / 0x70a9ae18: feedface 4fffed -- レッドゾーン 0x70a9ae20: 70ae3200 d1befaed -- デバッギングデータ 0x70a9ae28: deadbeef deadbeef ¥ 0x70a9ae30: deadbeef deadbeef +- ユーザーデータ (未使用) 0x70a9ae38: deadbeef deadbeef / 0x70a9ae40: feedface feedface -- レッドゾーン 0x70a9ae48: 70ae31a0 8440c54e -- デバッギングデータ
0x70a9add8 と 0x70a9ae28 の未使用バッファでは、レッドゾーンには 0xfeedfacefeedface が使用されています。これは、バッファが未使用であることを判断する便利な方法です。
0x70a9ae00 で始まる割り当て済みバッファでは、状況は異なります。レッドゾーンの前半は (0x70a9ae18 で) バッファが終わることを示すために使用されており、後半にはレッドゾーンバイトが書き込まれています。「アロケータの基礎」 で説明したことを思い出してください。割り当てには、次の 2 つのタイプがあります。
1) クライアントが、kmem_cache_alloc() を使用してメモリーを要求した場合。この場合には、要求されたバッファのサイズは、キャッシュの bufsize と等しくなります。
2) クライアントが、kmem_alloc(9F) を使用してメモリーを要求した場合。この場合には、要求されたバッファのサイズは、キャッシュの bufsize 以下になります。たとえば、20 バイトの要求は、kmem_alloc_24 キャッシュによって満たされます。アロケータは、クライアントデータのすぐ後にレッドゾーンバイトを調整して強制的にバッファ境界を合わせます。
0x70a9ae00: 5 4ef83 ¥ 0x70a9ae08: 0 0 +- ユーザーデータ (割り当て済み) 0x70a9ae10: 1 bbddcafe / 0x70a9ae18: feedface 4fffed -- レッドゾーン 0x70a9ae20: 70ae3200 d1befaed -- デバッギングデータ
0x70a9ae18 にある 0xfeedface の後には、ランダムな値のように見える 32 ビットのワードがあります。この数字は、実際にはバッファサイズの符号化された表現です。この数字を復号化して割り当て済みバッファのサイズを知るには、次の公式を使用します。
size = redzone_value / (UINT_MAX / KMEM_MAXBUF)
KMEM_MAXBUF の値は 16384 であり、UINT_MAX の値は 4294967295 です。したがってこの例では、次のようになります。
size = 0x4fffed / (4294967295 / 16384) = 20 bytes.
これは、要求されたバッファのサイズが 20 バイトであることを示しています。アロケータはこの復号化操作を行なって、レッドゾーンバイトがオフセット 20 であることを知ります。レッドゾーンバイトは 16 進パターン 0xbb です。これは予想通り、0x729084e4 (0x729084d0 + 0t20) に存在しています。
図 6-3 に、メモリー配置の一般的形式を示します。
割り当てサイズがキャッシュの bufsize と同じである場合には、図 6-4 に示すように、レッドゾーン自体の最初のバイトにレッドゾーンバイトが上書きされます。
この上書きの結果、レッドゾーンの最初の 32 ビットワードは 0xbbedface または 0xfeedfabb になります。このどちらになるかは、システムを実行しているハードウェアのエンディアンによります。
割り当てサイズがこのような方法で符号化されるのはなぜでしょうか。サイズを符号化するために、アロケータは公式 ((UINT_MAX / KMEM_MAXBUF) * size + 1) を使用します。サイズを復号化する際には、整数の割り算を行い、余りの「+1」は捨てられます。しかし、この追加された 1 は貴重な役割を果たします。なぜなら、アロケータは (size % (UINT_MAX / KMEM_MAXBUF) == 1) になるかどうかをテストすることにより、サイズが有効かどうかをチェックできるからです。このようにして、アロケータはレッドゾーンバイトインデックスの破壊に対処します。