あれらの年踏んだ穴---マクロ関数の使用

7587 ワード

今日テストプロトコルの时にゲートウェイの设备がいつも再开することを発见して、原因を见てみるとカーネルがNULLに対して解引用したのです.問題が発生したコードは次のとおりです.
static int qq_match_prepare(struct app_param *param, struct app_protocol *protocol)
{
    struct http_header *hdr = &__get_cpu_var(http_header);
    const char *buf = param->buf;
    short index;
    struct app_conntrack *app=param->app;
    const char *tmp;
    char *pos = NULL;
    char qq[16] = {0};
    unsigned int qq_num = 0;

    if (hdr->req.len > 104 &&
        app_memeql(buf, "GET /gchatpic") &&
        http_hdr_match_head(buf, hdr, HTTP_REFERER, "http://im.qq.com/mobileqq") &&
        http_url_search2(buf, hdr, "uin=", pos)){

        int i;

        pos += 5;
        for (i = 0; i < 16 && *pos != '&' && *pos != ' '; i++, pos++)
            qq[i] = *pos;

        qq_num  = simple_strtoul(qq, NULL, 10);
        memcpy(param->app->protocol_private, &qq_num, 4);

        return 1;
    }
    return 0;
}

http_url_search 2はマクロ関数であり、以下のように定義される.
#define http_url_search2(buf, hdr, str, p) \
    ({ int ret = 0;\
     char c;\
     char *pos;\
     pos = (char *)(buf + (hdr)->req.begin + (hdr)->req.len);\
     c = *pos;\
     *pos = 0;\
       ret = ((hdr)->req.len >= STRSIZE(str) && \
        (p = strstr(buf, str)));\
     *pos = c;\
     ret;\
     })

奇妙な現象は2つあります.
  • 元文字列バッファbufの内容は1つの文字が変更されます.
  • NULLポインタ
  • を参照解除
    長い間見ていても見当がつかず、最後はhttpに集中しました.url_search 2マクロ関数では、このマクロ関数はここでの呼び出しが他の場所と少し異なるためです.よく分析したところ,マクロ置換による問題であり,マクロ関数の外側と内側に同じ変数名posが用いられ,置換後の結果が予想に反したことが分かった.そこで、このファイルを事前にコンパイルして、自分の考えを検証します.コードは以下の通りです.
    static int qq_match_prepare(struct app_param *param, struct app_protocol *protocol)
    {
        struct http_header *hdr = &__get_cpu_var(http_header);
        const char *buf = param->buf;
        short index;
        struct app_conntrack *app=param->app;
        const char *tmp;
        char *pos = ((void *)0);
        char qq[16] = {0};
        unsigned int qq_num = 0;
    
    
        if (hdr->req.len > 104 &&
            app_memeql(buf, "GET /gchatpic") &&
            ({ short index; int ret; if (HTTP_REFERER >= MAX_HTTP_TYPE || ((index = (hdr)->http_type[HTTP_REFERER]) == -1)) ret = 0; else { ret = ((hdr)->fields[index].value_len >= (sizeof(char [1 - 2 * is_ptr("http://im.qq.com/mobileqq")]) * 0 + sizeof("http://im.qq.com/mobileqq") - 1) && app_memeql((buf) + ((hdr)->fields[index].value_begin), "http://im.qq.com/mobileqq")); } ret; }) &&
            ({ int ret = 0; char c; char *pos; pos = (char *)(buf + (hdr)->req.begin + (hdr)->req.len); c = *pos; *pos = 0; ret = ((hdr)->req.len >= (sizeof(char [1 - 2 * is_ptr("uin=")]) * 0 + sizeof("uin=") - 1) && (pos = strstr(buf, "uin="))); *pos = c; ret; })){
    
            int i;
    
            pos += 5;
            for (i = 0; i < 16 && *pos != '&' && *pos != ' '; i++, pos++)
                qq[i] = *pos;
    
            qq_num = simple_strtoul(qq, ((void *)0), 10);
            memcpy(param->app->protocol_private, &qq_num, 4);
    
    
            return 1;
        }
    
        return 0;
    }
    

    前処理後のコードから分かるように、入力マクロ関数のパラメータとマクロ関数体は同じ変数名を使用しているが、マクロ関数は単純なテキスト置換のみであるため、本来意味の異なる変数がコンパイラによって同じ変数と誤認され、pos = strstr(buf, "uin=");以降*pos = cであるため、str呼び出しが成功すると元の文字列バッファbufの内容が1文字修正される呼び出しに失敗するとNULLポインタは参照解除されます.
    http_url_search 2が関数であればこのような問題は発生しません!
    まとめ:マクロ関数を使用するときは、マクロを簡単なテキストで置き換えるだけであることを覚えておくことが大切です.