簡體   English   中英

C中va_list可能存在緩沖區溢出漏洞?

[英]possible buffer overflow vulnerability for va_list in C?

我有以下代碼:

int ircsocket_print(char *message, ...)
{
    char buffer[512];
    int iError;
    va_list va;
    va_start(va, message);
    vsprintf(buffer, message, va);
    va_end(va);
    send(ircsocket_connection, buffer, strlen(buffer), 0);
    return 1;
}

我想知道這個代碼是否通過向變量列表提供大小> 512的char數組來緩沖溢出? 如果是這樣 - 我該如何解決這個問題?

謝謝。

是的,它很脆弱。

您可以這樣實現您的功能:

int ircsocket_print(char *message, ...)
{
    char buf[512];
    char *buffer;
    int len;
    va_list va;

    buffer = buf;
    va_start(va, message);
    len = vsnprintf(buffer, 512, message, va);
    va_end(va);

    if (len >= 512)
    {
        buffer = (char*)malloc(len + 1);
        va_start(va, message);
        len = vsnprintf(buffer, len + 1, message, va);
        va_end(va);
    }

    send(ircsocket_connection, buffer, len, 0);

    if (buffer != buf)
        free(buffer);
    return 1;
}

是的,它很脆弱。

只需使用vsnprintf代替:

vsnprintf(buffer, sizeof(buffer), message, va);

正如其他人所說,問題的基本答案是“是的,你很容易受到緩沖區溢出的影響”。

在C99中,您可以使用VLA:

void ircsocket_print(const char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    int len = vsnprintf(0, 0, message, args);
    va_end(args);

    char buffer[len+1];

    va_start(args, fmt);
    int len = vsnprintf(buffer, len+1, message, args);
    va_end(args);

    send(ircsocket_connection, buffer, len, 0);
}

注意調用vsnprintf()一次的長度為0(第二個零)的習慣用法來獲得所需的長度,然后再次調用它來將數據格式化為緩沖區。 還要注意每次調用vsnprintf()后仔細重置args ; 這是C標准所要求的:

§7.15 <stdarg.h>

如果需要訪問變量參數,則被調用函數應聲明類型為va_list的對象(在va_list條款中通常稱為ap )。 對象ap可以作為參數傳遞給另一個函數; 如果該函數使用參數ap調用va_arg宏,則調用函數中的ap值是不確定的,並且應該在進一步引用ap之前傳遞給va_end宏。

這個公式的一個缺點是它需要一個悲觀的觀點並且無條件地兩次調用vsnprintf() 您可能更喜歡采取樂觀的觀點(大多數情況下,512就足夠了),並且只有在第一次調用顯示它不足時才分配更多空間。

使用像這樣的VLA的另一個缺點是,如果你的局部變量buffer空間不足,你的代碼可能永遠不會有機會恢復。 你必須判斷問題有多嚴重。 如果是一個問題,請使用顯式內存分配( malloc() ):

char buffer = malloc(len+1);
if (buffer == 0)
    return;  // Report error?
...second vsnprintf() and send()...
free(buffer);

由於你的函數只返回常量1 ,因此沒有明顯的理由使它成為返回任何東西的函數 - 如果它是一個返回void的函數,則調用代碼不需要對返回的值進行任何檢查。 OTOH,也許你應該返回send()調用的結果send()如果malloc()失敗則可能是錯誤指示)。

我還將format(message)參數改為const char * ; 這個函數和它調用的函數都不會修改格式字符串。

如果你在Linux上,你可以使用vasprintf() ,它將分配一個正確大小的緩沖區。

如果需要可移植性,可以使用vsnprintf()來避免緩沖區溢出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM