关于sprintf 和 snprintf的自拷贝
char buf[100];
memset(buf, 0, 100);
strcpy(buf, "hello");
sprintf(buf, "%s world\n", buf);
printf("%s\n", buf);
结果是什么?
【实践】:
思想:在内部创建一个临时buffer,去代替des,然后,实际操作sprintf/snprint,最后,把buffer中的内容导入des即可。
#include <string.h>
#include <stdlib.h>
* des : 目标buffer
* size : 限定目标buffer的最大尺寸
* strategy : 当目标buffer与源buffer相同时,是否要拷贝原来的值
* 0 不拷贝
* 非0 拷贝
* return : 0 正确
* 非0 失败
*/
int mysnprintf(char *des, size_t size, int strategy, char* fmt, ...)
{
char* pArg=NULL; //......va_list
char c;
pArg += sizeof(fmt); //......va_start
// no change to des pointer until the end
char *tdes;
char *ptr;
int i=0;
return -1;
memset(tdes, 0, size);
{
c =*fmt;
if (c != '%')
{
*(tdes+i) = c;
i ++;
}
else
{
switch(*++fmt)
{
case 's':
ptr = *((char **)pArg);
if ( ptr == des)
{
printf("WARN:des equal to src\n");
}
if ( ptr == des && !strategy)
{
printf("remove the old value\n");
}
else {
while(*ptr != '\0' && *ptr !='\n' && i<size)
{
*(tdes+i)= *ptr;
i ++;
ptr ++;
}
}
break;
*(tdes+i)='%';
i ++;
break;
}
}
}while (*fmt != '\0' && *fmt != '\n' && i<size);
memcpy(des, tdes, size);
// clean
pArg = NULL; //...va_end
free(tdes);
return 0;
}
{
char des_buf[1024];
memset(des_buf, 0, 1024);
strcpy(des_buf, "hello");
if ( argc >1 )
stragety = atoi(argv[1]);
mysnprintf(des_buf, 512, stragety, "world %s haha %s\n",des_buf, "end");
printf("result:[%s]\n", des_buf);
return 0;
}
# ./test
WARN:des equal to src
remove the old value
result:[world haha end]
# ./test 1
WARN:des equal to src
result:[world hello haha end]
void f(const char *p)
{
char buf[11]={0};
sprintf(buf,"%10s",p); //very dangerous
printf("%sn",buf);
}
不要让格式标记“%10s”误导你。如果 p 的长度大于10个字符,那么sprintf() 的写操作就会越过 buf 的边界,从而产生一个缓冲区溢出。
f("hello world!"); //12 characters + nul
检测这类缺陷并不容易,因为它们只在 p 的长度大于10个字符的时候才会发生。黑客通常利用这类脆弱的代码来入侵看上去安全的系统。要修正这一缺陷,可以使用函数snprintf() 代替函数sprintf(),函数snprintf() 的原型为:
int
snprintf(char* buf, size_t maxlen, const char* fmt, ...);
第二个参数定义能被写到 buf 中的字符的最大个数,不考虑格式标志以及源字符串的大小:
snprintf(buf, 10, "%10s",p); //now safe
f("hello world!"); //string is chopped to "hello worl"
相似的,使用strncpy()、strncmp()、strncat()、strnicmp() 和 strnset() 相应地代替strcmp()、strcat()、stricmp() 和 strset()。例如:
const int LINE_SIZE=81;
char buf[LINE_SIZE]={0};
// write up to 80 chars to buf:
strncpy(buf, dest, LINE_SIZE-1);
// compare no more than 80 chars:
int equal= strncmp(buf, dest, LINE_SIZE-1);
使用缓冲区大小有限制的 C 函数版本可以降低缓冲区溢出发生的可能性,也不要求对原始代码进行实质的变化。