acl4のページ0002
(1)
#if (a_Version >= 1)
#define DoublyLinkList a_DoublyLinkList
#define DoublyLinkList_ini a_DoublyLinkList_ini
#define DoublyLinkList_insTail a_DoublyLinkList_insTail
#define DoublyLinkList_insHead a_DoublyLinkList_insHead
#define DoublyLinkList_rmv a_DoublyLinkList_rmv
#endif
a_class(a_DoublyLinkList) { a_DoublyLinkList *prv, *nxt; };
a_static void a_DoublyLinkList_ini(a_DoublyLinkList *w) { w->prv = w->nxt = w; }
a_static void a_DoublyLinkList_insTail(a_DoublyLinkList *w, a_DoublyLinkList *tgt)
{
a_DoublyLinkList *lst = w->prv;
lst->nxt = tgt;
tgt->prv = lst;
tgt->nxt = w;
w ->prv = tgt;
}
a_static void a_DoublyLinkList_insHead(a_DoublyLinkList *w, a_DoublyLinkList *tgt)
{
a_DoublyLinkList *hed = w->nxt;
w ->nxt = tgt;
tgt->prv = w;
tgt->nxt = hed;
hed->prv = tgt;
}
a_static void a_DoublyLinkList_rmv(a_DoublyLinkList *tgt)
{
a_DoublyLinkList *prv = tgt->prv;
a_DoublyLinkList *nxt = tgt->nxt;
prv->nxt = nxt;
nxt->prv = prv;
}
#if (a_Version >= 1)
#define malloc_ a_malloc
#define free_ a_free
#define realloc_ a_realloc
#endif
a_class(a_malloc_Header) {
a_DoublyLinkList lnk[1];
intptr_t dmy[1], sz, lin;
const char *fil;
intptr_t serialNum, sig;
};
a_malloc_Header a_malloc_hed0[1];
#define a_malloc_Sig 0xff0055aa
a_static void *a_malloc(_argDef_ intptr_t sz)
{
#if (a_DbgLv < 2)
void *p = malloc(sz);
if (p == NULL)
a_errExit("malloc_: out of memory: sz=%d");
return p;
#else
if (sz < 0)
a_errExit("%s(%d): malloc_ size error: sz=%d", a_fil, a_lin, (int) sz);
a_malloc_Header *h = (a_malloc_Header *) malloc(sz + sizeof (a_malloc_Header));
if (h == NULL)
a_errExit("%s(%d): malloc_: out of memory: sz=%d", a_fil, a_lin, (int) sz);
if (a_malloc_hed0->lnk->prv == NULL) {
a_DoublyLinkList_ini(a_malloc_hed0->lnk);
a_malloc_hed0->sz = -1;
a_malloc_hed0->sig = a_malloc_Sig;
a_malloc_hed0->lin = 0;
}
a_DoublyLinkList_insTail(a_malloc_hed0->lnk, h->lnk);
h->sz = sz;
h->sig = a_malloc_Sig;
h->lin = a_lin;
h->fil = a_fil;
h->serialNum = ++(a_malloc_hed0->serialNum);
a_malloc_hed0->lin++;
return h + 1;
#endif
}
a_static void a_free(_argDef_ void *p, intptr_t sz)
{
#if (a_DbgLv < 2)
(void) sz;
free(p);
#else
if (p == NULL) return;
a_malloc_Header *h = (a_malloc_Header *) p - 1;
if (h->sig != a_malloc_Sig)
a_errExit("%s(%d): free_: bad signature", a_fil, a_lin);
if (h->sz != sz)
a_errExit("%s(%d): free_: bad size: sz=%d, arg=%d", a_fil, a_lin, (int) h->sz, (int) sz);
h->sig = 0;
h->serialNum = 0;
a_DoublyLinkList_rmv(h->lnk);
a_malloc_hed0->lin--;
free(h);
#endif
}
a_static void *a_realloc(_argDef_ void *p, intptr_t sz0, intptr_t sz1)
{
#if (a_DbgLv < 2)
(void) sz0;
p = realloc(p, sz1);
if (p == NULL)
a_errExit("realloc_: out of memory: sz1=%d");
return p;
#else
if (sz1 < 0)
a_errExit("%s(%d): realloc_ size error: sz1=%d", a_fil, a_lin, (int) sz1);
if (p == NULL)
return a_malloc(a_fil, a_lin, sz1);
a_malloc_Header *h = (a_malloc_Header *) p - 1;
if (h->sig != a_malloc_Sig)
a_errExit("%s(%d): realloc_: bad signature", a_fil, a_lin);
if (h->sz != sz0)
a_errExit("%s(%d): realloc_: bad size: sz=%d, arg=%d", a_fil, a_lin, (int) h->sz, (int) sz0);
h->sig = 0;
intptr_t sn = h->serialNum;
h->serialNum = 0;
a_DoublyLinkList *prv = h->lnk->prv;
a_DoublyLinkList *nxt = h->lnk->nxt;
prv->nxt = nxt;
nxt->prv = prv;
a_malloc_Header *h1 = (a_malloc_Header *) realloc(h, sz1 + sizeof (a_malloc_Header));
if (h1 == NULL)
a_errExit("%s(%d): realloc_: out of memory: sz1=%d", a_fil, a_lin, (int) sz1);
prv->nxt = h1->lnk;
nxt->prv = h1->lnk;
h1->sig = a_malloc_Sig;
h1->serialNum = sn;
h1->sz = sz1;
return h1 + 1;
#endif
}
a_static intptr_t a_malloc_totalSiz(_argDef_ intptr_t *pn)
{
intptr_t s = 0;
#if (a_DbgLv >= 2)
(void) a_fil; (void) a_lin;
if (a_malloc_hed0->lnk->prv != NULL) {
a_DoublyLinkList *t = a_malloc_hed0->lnk->nxt;
for (;;) {
a_malloc_Header *h = (a_malloc_Header *) t;
if (h->sz < 0) break;
s += h->sz;
t = t->nxt;
}
}
#endif
if (pn != NULL) *pn = a_malloc_hed0->lin;
return s;
}
a_static void a_malloc_debugList(_argDef)
{
#if (a_DbgLv >= 2)
fprintf(stderr, "%s(%d): malloc_debugList()\n", a_fil, a_lin);
if (a_malloc_hed0->lnk->prv != NULL) {
a_DoublyLinkList *t = a_malloc_hed0->lnk->nxt;
for (;;) {
a_malloc_Header *h = (a_malloc_Header *) t;
if (h->sz < 0) break;
fprintf(stderr, " [p:%08x sz:%d / %s(%d)]\n", (int) (intptr_t) (h + 1), h->sz, h->fil, h->lin);
t = t->nxt;
}
}
#endif
}
(2) DoublyLinkList
- 私の大好きな双方向リストを簡単に扱うためのライブラリ関数群です。
- まず a_class(a_DoublyLinkList) { a_DoublyLinkList *prv, *nxt; }; で構造体を宣言しています。
- 最初なので、これがどう展開されるかを説明します。
typedef struct a_DoublyLinkList ## _ a_DoublyLinkList;
struct a_DoublyLinkList ## _ { a_DoublyLinkList *prv, *nxt; };
- となったあと、
typedef struct a_DoublyLinkList_ a_DoublyLinkList;
struct a_DoublyLinkList_ { a_DoublyLinkList *prv, *nxt; };
- となるのです。これで自前でtypedefしなくてもa_DoublyLinkListをstructなしで書けるようになるわけです。
- 私はよく使う語に関しては短く書くのが好きです。
- prv → previous
- nxt → next
- ini → init
- ins → insert
- rmv → remove
- tgt → target
- またクラスのthisポインタのことをwと略します。これは私がオブジェクトのことをworking-area(作業領域)と呼んでいたころの名残りです。
- こんな簡単な処理くらい、関数化しないで手書きしてもいいですし実際にそうすることもありますが、たまに間違えてデバッグで苦労するので、関数で処理することが多いです。まあたぶんほとんどインライン展開されていて、手書きと同じ速度になっているような気はします。
(3) malloc_, free_, realloc_
- まず a_DbgLv が 0 だったらどうなるかを確認してください。
- これらは malloc, free. realloc に out of memory 用のチェックがついただけのラッピング関数でしかなく、ほぼ長所はありません。
- さて、では a_DbgKv が 2 だった場合を見てみましょう。
- まず malloc ではサイズ指定が符号付きの整数になっていて、負の場合はエラーが出るようになります。
- そもそも私は、size_t型がunsignedであるのは下手な設計だとずっと思っています。サイズ計算しようと思ってポインタの引き算をするとき、 sz=p1-p0; を sz=p0-p1; と書いてしまうことは十分に想定できます。そうするとszの値は当然マイナスになりますが、size_tは負の数を受け付けないので、結果的に途方もなく大きな正の値になります。こうなってしまっては、引き算のミスを見つけにくくなります。
- size_tがunsignedなのは、メモリ領域のサイズとして2GB以上も指定できるようにしたかったのでしょう(32bitの場合)。しかし2GB以上のメモリ領域が必要なことなんてものすごくレアです。サイズ計算ミスの頻度のほうがはるかに高いです。だから私は malloc のサイズ指定は符号付きの整数値であるべきだと思っていますし、デバッグレベルが2以上なら、szが負になっていないかどうかチェックして、負だったら止まるべきだと思っているのです。
- mallocではシグネチャやシリアルナンバーをヘッダに記入する処理が追加されます。
- freeではヘッダの健全性をチェックする処理が追加されます。free時にはシグネチャとシリアルナンバーをわざわざ0クリアしてからfreeしています。どうせfreeするんだから、そんなことしても意味ないと思うかもしれません。いやいや、こうすれば謝って二重freeしたときにエラーを出せるようになるのです。
- シリアルナンバーは、この領域をfreeしてmallocで偶然に再利用されたときに、値が変わって気づけるようにするためのものです。再利用されてしまうとシグネチャだけでは判断できないのです。
- このプログラムではまだシリアルナンバーを全く利用できてないですが、malloc時にシリアルナンバーを控えておいて、それが変わらないことを監視すれば、use-after-free脆弱性を検出できるようになります。
(4) 使い方
- acl4ライブラリは以下のようにして利用します。
- [1]まずa4_0001~a4_0002のプログラムをつなげて"acl4.c"として保存します。
- [2]次にacl4を使ったプログラムを書きます。
- [3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。
(99) 更新履歴