C中va_列表可能存在缓冲区溢出漏洞?

C中va_列表可能存在缓冲区溢出漏洞?,c,overflow,buffer,variadic-functions,C,Overflow,Buffer,Variadic Functions,我有以下代码: 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); retur

我有以下代码:

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的字符数组来缓冲溢出?如果是的话,我该如何解决这个问题


谢谢。

如果您使用的是Linux,您可以使用它来分配正确大小的缓冲区

如果您需要可移植性,可以使用它来避免缓冲区溢出。

是的,它很容易受到攻击

只需使用
vsnprintf
即可:

vsnprintf(buffer, sizeof(buffer), message, va);
char buffer = malloc(len+1);
if (buffer == 0)
    return;  // Report error?
...second vsnprintf() and send()...
free(buffer);
是的,它很脆弱

您可以通过以下方式实现您的功能:

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;
}

正如其他人所指出的,您的问题的基本答案是“是的,您很容易受到缓冲区溢出的影响”

在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);
}
请注意这样一个习惯用法:使用长度0(第二个零)调用
vsnprintf()
一次以获得所需的长度,然后再次调用它以将数据格式化到缓冲区中。还要注意在每次调用
vsnprintf()
后仔细重置
args
;这是C标准所要求的:

§7.15
如果无法访问不同的参数 如果需要,被调用函数应声明一个对象(在本例中通常称为
ap
) 子条款)具有类型
va_列表
。对象
ap
可以作为参数传递给 另一个功能;如果该函数使用参数
ap
调用
va_arg
宏,则 调用函数中的
ap
值不确定,应传递给
va_端
在进一步引用
ap
之前执行宏

这个公式的一个缺点是它采取悲观的观点,并且无条件地调用
vsnprintf()
两次。您可能更愿意采取乐观的观点(大多数情况下,512就足够了),只有在第一次调用显示空间不足时才分配更多空间

这样使用VLA的另一个缺点是,如果本地变量
buffer
的空间不足,代码可能永远无法恢复。你必须判断这个问题有多严重。如果是问题,请使用显式内存分配(
malloc()
)代替:

由于您的函数只返回常量
1
,因此没有明显的理由使其成为返回任何内容的函数-如果它是返回
void
的函数,则调用代码不需要对返回值进行任何检查。OTOH,也许您应该返回
send()
调用的结果(如果
malloc()
失败,则可能会返回错误指示)


我还将format(message)参数设置为
const char*
;此函数及其调用的函数都不会修改格式字符串。

+1用于指出
vsnprintf
返回本应打印的字符数以及不截断输出。请注意,在第一次调用vsnprintf()后,必须调用
va\u end(va)
va_启动(va,消息)vsnprintf()
。C99标准§7.15
中记录了这一点:[…]被调用函数应声明一个具有类型va_列表的对象(在本款中通常称为ap)。对象ap可以作为参数传递给另一个函数;如果该函数使用参数ap调用va_arg宏,则调用函数中ap的值是不确定的,应在进一步引用ap之前传递给va_end宏。此外,严格来说,问题被标记为C(而不是C++)和
new
运算符(或者该运算符是
new
?)不是C的一部分。
delete
。感谢您澄清vsnprintf返回本应写入的字节数,而不是实际写入的字节数。非常有用的提示这是一个非常全面的答案,谢谢。如果我能给它评分两次,我肯定会:)