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返回本应写入的字节数,而不是实际写入的字节数。非常有用的提示这是一个非常全面的答案,谢谢。如果我能给它评分两次,我肯定会:)