And*_*tin 4 c buffer overflow 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);
return 1;
}
Run Code Online (Sandbox Code Playgroud)
我想知道这个代码是否通过向变量列表提供大小> 512的char数组来缓冲溢出?如果是这样 - 我该如何解决这个问题?
谢谢.
And*_*aev 10
是的,它很脆弱.
您可以这样实现您的功能:
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;
}
Run Code Online (Sandbox Code Playgroud)
是的,它很脆弱.
只需使用vsnprintf
:
vsnprintf(buffer, sizeof(buffer), message, va);
Run Code Online (Sandbox Code Playgroud)
正如其他人所说,问题的基本答案是"是的,你很容易受到缓冲区溢出的影响".
在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);
}
Run Code Online (Sandbox Code Playgroud)
注意vsnprintf()
用长度为0(第二个零)调用一次以获得所需长度的习惯用法,然后再次调用它以将数据格式化为缓冲区.还要注意args
每次通话后仔细重置vsnprintf()
; 这是C标准所要求的:
第7.15节
<stdarg.h>
如果需要访问不同的参数,被调用的函数应声明一个
ap
具有类型的对象(通常在本子条款中称为)va_list
.该对象ap
可以作为参数传递给另一个函数; 如果该函数va_arg
使用参数调用宏,则调用函数中ap
的值ap
是不确定的,并且应该在va_end
进一步引用之前传递给宏ap
.
这种表述的一个缺点是它需要一个悲观的观点并无条件地调用vsnprintf()
两次.您可能更喜欢采取乐观的观点(大多数情况下,512就足够了),并且只有在第一次调用显示它不足时才分配更多空间.
使用像这样的VLA的另一个缺点是,如果你的局部变量空间不足buffer
,你的代码可能永远不会有机会恢复.你必须判断问题有多严重.如果是一个问题,请使用显式内存分配(malloc()
):
char buffer = malloc(len+1);
if (buffer == 0)
return; // Report error?
...second vsnprintf() and send()...
free(buffer);
Run Code Online (Sandbox Code Playgroud)
因为你的函数只返回常量1
,所以没有明显的理由让它成为返回任何东西的函数 - 如果它是一个返回的函数void
,则调用代码不需要对返回的值进行任何检查.OTOH,也许你应该返回send()
调用的结果(如果malloc()
失败则可能是错误指示).
我还将格式(消息)参数改为a const char *
; 这个函数和它调用的函数都不会修改格式字符串.