返回信息流如上题所述,为什么用va_start和vsprintf会告警可能指针越界?然后换成vsnprintf,用VC编译会说无法识别,是为什么?
这是一条镜像帖。来源:北邮人论坛 / cpp / #72999同步于 2013/8/3
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
va_start和vsprintf告警指针越界
bingyi
2013/8/3镜像同步12 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
change
vsnprintf
to
_vsnprintf
【 在 bingyi 的大作中提到: 】
: 如上题所述,为什么用va_start和vsprintf会告警可能指针越界?然后换成vsnprintf,用VC编译会说无法识别,是为什么?
在问下,有va_starts这种用法吗?
【 在 tonyjansan 的大作中提到: 】
: change
: vsnprintf
: to
: ...................
【 在 bingyi 的大作中提到: 】
: 如上题所述,为什么用va_start和vsprintf会告警可能指针越界?然后换成vsnprintf,用VC编译会说无法识别,是为什么?
这个和sprintf会导致拼装越界是一个原理。当然,即使使用snprintf也只能保证单次安全,连续拼装还是可能越界的。
我这儿用的都是自己封装的拼装函数,在snprintf的基础上多一个入参的检查。
能跟我详细说说吗?什么样的入参检查?一般va_start不都是两个参数吗?现在总说va_start的第二个参数可能会指针越界
【 在 eussac 的大作中提到: 】
: 这个和sprintf会导致拼装越界是一个原理。当然,即使使用snprintf也只能保证单次安全,连续拼装还是可能越界的。
: 我这儿用的都是自己封装的拼装函数,在snprintf的基础上多一个入参的检查。
看看va_start的实现你就懂了:
/*
* Use local definitions of C library macros and functions
* NOTE: The function implementations may not be as efficient
* as an inline or assembly code implementation provided by a
* native C library.
*/
#ifndef va_arg
#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif /* _VALIST */
/*
* Storage alignment properties
*/
#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)
/*
* Variable argument list macro definitions
*/
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
#endif /* va_arg */
其中va_start传入参数ap,宏将其指向定位到格式化串的第一个变长参数位置,但如果此时函数体本身未传入任何参数,则访问越界,输出内容不可控。
举例:
printf("%d\n");
【 在 bingyi 的大作中提到: 】
: 能跟我详细说说吗?什么样的入参检查?一般va_start不都是两个参数吗?现在总说va_start的第二个参数可能会指针越界
什么意思?就是说我在编译va_start所在函数时,函数还未被调用?这种情况如何解决呢?更改函数位置?但是有时候函数是被其他文件中的函数调用,我怎么才能控制va_start所在函数的调用呢?
【 在 tonyjansan 的大作中提到: 】
: 看看va_start的实现你就懂了:
: [code=c]
: /*
: ...................
【 在 bingyi 的大作中提到: 】
: 什么意思?就是说我在编译va_start所在函数时,函数还未被调用?这种情况如何解决呢?更改函数位置?但是有时候函数是被其他文件中的函数调用,我怎么才能控制va_start所在函数的调用呢?
你在说什么我已经读不懂了...别乱,举个具体的例子来逐行捋捋:
//
int MyPrintf(const char* format, ...) { // ...的内容是可变参数列表
va_list ap; // 指向可变参数列表的指针
char buf[512]; // 存放可变参数转化成的格式化字符串数组
int result; // 返回值变量
va_start(ap, format); // 将第一个可变参数的地址赋给ap,即ap指向可变参数列表(...)的开始
result = vsprintf(buf, format, ap); // 将参数ap和format进行转化形成格式化字符串,
// 即生成最终输出的字符串
va_end(ap); // 将ap复位
if (result >= 0) printf(buf);
return result;
}
如果你的format和可变参数(...)是一一对应的话不会出现问题,但如果你如下调用MyPrintf的话就会有问题:
MyPrintf("%d\n"); // 调用1
MyPrintf("%d%d\n", 1); // 调用2
调用1: 你会发现确实ap被指向了format参数的后边,但是format后边你并未传递其它参数进来,所以ap指向了一片不可控的内存(越界操作),而format中却有一个%d取代符,于是在buf里就变成了一个不可测的值输出出来了。
调用2: ap确是指向了第一个也是唯一一个可变参数1,但是在vsprintf(buf, format, ap);时会发现,format中有两个%d取代符,可传进来的可变参数却只有1个,于是在取代第2个%d时,ap又指向了一片不可控内存,造成越界操作。