C 当您不使用';我不知道缓冲区的大小
我的问题是,我不能将缓冲区大小参数添加到我的sprintf中,因为它在超过50k个位置使用,我不能用vsprintf_s或vsnprintf替换vsprintfC 当您不使用';我不知道缓冲区的大小,c,static-analysis,stdio,cstdio,C,Static Analysis,Stdio,Cstdio,我的问题是,我不能将缓冲区大小参数添加到我的sprintf中,因为它在超过50k个位置使用,我不能用vsprintf_s或vsnprintf替换vsprintf 有没有其他方法可以使上述函数更安全?您在这里要问的是一个(in)著名问题的特殊化:“如果我只有一个指针,如何获得数组的大小?” 无法确定dest指向的对象的大小。你最好的选择可能是咬紧牙关,改变那些50k的位置来通过这个尺寸 #define SPRINTF_TRACE_BUFSIZE 4096 int my_sprintf( char
有没有其他方法可以使上述函数更安全?您在这里要问的是一个(in)著名问题的特殊化:“如果我只有一个指针,如何获得数组的大小?” 无法确定
dest
指向的对象的大小。你最好的选择可能是咬紧牙关,改变那些50k的位置来通过这个尺寸
#define SPRINTF_TRACE_BUFSIZE 4096
int my_sprintf( char* dest, const char* fmt, ... )
{
/* in threaded code use malloc(3) instead */
static char trace_buf[SPRINTF_TRACE_BUFSIZE];
va_list va;
va_start( va, fmt );
int rc = vsnprintf( trace_buf, SPRINTF_TRACE_BUFSIZE, fmt, va );
assert( rc != -1 && rc < SPRINTF_TRACE_BUFSIZE );
memcpy( dest, trace_buf, rc + 1 ); /* +1 for \0 terminator */
return rc;
}
您的代码中可能有更多您没有告诉我们的内容。例如,在你提到的那些“50k”的地方,大小是已知的吗?如果是这样的话,你可以使用一个肮脏的变量宏,它在幕后使用
sizeof
,然后调用一个带有长度参数的函数。对不起,这里没有适合你的银弹,正如@cnicutar已经提到的
您可以从限制缓冲区大小和断言溢出开始。比如:
__inline int my_sprintf (char *dest,char *format,...)
{
va_list va;
va_start(va,format);
return vsprintf(dest,format,va);
}
#定义SPRINTF_TRACE_BUFSIZE 4096
int my_sprintf(char*dest,const char*fmt,…)
{
/*在线程化代码中使用malloc(3)*/
静态字符跟踪[SPRINTF\u trace\u BUFSIZE];
va_列表va;
va_启动(va,fmt);
int rc=vsnprintf(trace_buf,SPRINTF_trace_BUFSIZE,fmt,va);
断言(rc!=-1&&rc
然后开始降低跟踪缓冲区大小,直到断言开始触发。在这一点上,您可以找到并修复有问题的呼叫
这当然会降低整个系统的速度,但我们这里不讨论性能
只是强调一下-这是一个快速而肮脏的黑客攻击,用于与大量旧的现有代码库作斗争,不要将其用于新的开发这一大规模的重构工作需要自动化。更改
my_sprintf
函数本身的行为是微不足道的,因此我将把这个练习留给读者。正如你所指出的,电话是最困难的部分。我假设这些调用的结构如下:
ret=my_sprintf(dest,“%d:%s”,arg1,arg2)代码>
许多IDE/文本编辑器在其搜索/替换选项中支持正则表达式。我的其中一个,SlickEdit,特别好,因为它允许Perl正则表达式。下面是一个将上述调用转换为以下内容的示例:
ret=my\u sprintf(目标,最大大小,“%d:%s”,arg1,arg2)代码>
请注意,已指示SlickEdit将此模式递归应用于我的项目树中的所有C源文件
请注意,对话框条目应相当于s/(\“+\”)/MAX\u SIZE、$1/
的正则表达式。您的开发环境是否具有类似的功能?很多人都是这样,看看吧
如果没有,还有其他选项,比如独立脚本。例如,假设您有一些类似*nix的shell和Perl可用。Google“perl one liner replace”查找一些很好的示例,如和。将这些与明智地使用find
命令()结合起来,问题就解决了
*免责声明:我不能保证我的示例regex的优雅。在我的例子中,它是有效的,但regex专家无疑可以改进它。OP评论说“大量的缓冲区是动态分配的,…”。malloc()
、realloc()
、calloc()
、free()
等可以使用存储大小的包装函数重新编写
#define SPRINTF_TRACE_BUFSIZE 4096
int my_sprintf( char* dest, const char* fmt, ... )
{
/* in threaded code use malloc(3) instead */
static char trace_buf[SPRINTF_TRACE_BUFSIZE];
va_list va;
va_start( va, fmt );
int rc = vsnprintf( trace_buf, SPRINTF_TRACE_BUFSIZE, fmt, va );
assert( rc != -1 && rc < SPRINTF_TRACE_BUFSIZE );
memcpy( dest, trace_buf, rc + 1 ); /* +1 for \0 terminator */
return rc;
}
所有其他*.c文件都使用
typedef union {
max_align_t align;
size_t sz;
} my_header;
void* my_malloc(size_t size) {
my_header *p = malloc(sizeof *p + size);
if (p) {
p->sz = size;
p++;
}
return p;
}
size_t my_size(const void *p) {
if (p) {
const my_header *head = p;
return head[-1].sz;
}
return 0;
}
void my_free(void *p) {
if (p) {
my_header *head = p;
free(--head);
}
}
现在,当使用分配的指针调用my_sprintf()
时
此外,还可以预先设置一个幻数,以帮助识别传递的指针是否真的是my_allcoating()
1
包装分配函数也是确定各种分配关注点的一种方法:双重空闲、最大使用、所有指针空闲
[编辑]5年后
代码需要确保对齐-代码重新工作
对于C11之前的版本,使用宽类型的并集代替max\u align\u t
int my_sprintf (char *dest,char *format,...) {
va_list va;
va_start(va,format);
size_t n = my_size(dest);
return vsnprintf(dest,n,format,va);
}
当您的my\u sprintf()
被传递的不是真正的char*
时,您可以使用宏来提供帮助
typedef union {
double d;
long l;
void *p;
void (*fp)();
// With C99
complex long double cld;
long long ll;
size_t sz;
} my_header;
}
是的,它确实计算了dest
两次,但是在不知道代码库的情况下,我不能说这是否是一个问题。但它至少在使用数组进行调用的情况下会有所帮助
我还郑重建议您看看Purify之类的东西是否值得购买。大量缓冲区是动态分配的,所以大多数情况下我不能使用Size。。。真是一场噩梦:(@user327843-Hmm.“50k个位置”…那可能需要很长时间。@Eregrith该死的veracode:D我认为我不会在下一个月实现我的目标appraisal@user327843从一个grep-Ri--include=*.[ch]my|u sprintf | wc-l
开始,了解需要更改多少行:)如果dest太小,memcpy不会导致缓冲区溢出吗?是的,它会。但这不是为了防止坏的上游代码溢出缓冲区,而是为了找到违规者并清理它们。我继承了一个非常庞大的遗留代码(数百万LOC),没有自动的单元测试。在我完成之前,我已经死了。我喜欢这样做,因为这是处理问题规模的简单方法。或者,代码使用BA缓冲区并简单地记录全局变量中使用的最大长度,以便以后进行调试检查。IOWs,显示通常使用的my\u sprintf()
的上下文。不错,但有点不可靠。。。我认为,如果您首先在我的_标题中添加一些魔力,比如0xC0DE,从而尝试无限制地打印到
#define my_sprintf(dest,format,...) \
my_sprintf_func( (dest),sizeof(dest), ( format ), __VA_ARGS__ )
__inline int my_sprintf_func(char *dest,size_t size,char *format,...)
{
va_list va;
va_start(va,format);
if ( size == sizeof(dest) )
return vsprintf(dest,format,va);
return vsnprintf(dest,size,format,va);