#include <string .h>
int main()
{
char src[50] = "abcdefghijklmnopqrstuvwxyz";
char buf[10] = "";
int len = sprintf(buf, "%s", src);
printf("src=%s\n", src);
printf("len=%d\n", len);
printf("buf=%s\n", buf);
return 0;
}
这段代码,有明显的 缓冲区溢出的现象,编译后运行,我预期会报堆栈溢出 之类的错误,
没想到结果是如下:
src=klmnopqrstuvwxyz
len=26
buf=abcdefghijklmnopqrstuvwxyz
搞不懂为何会这样 ?
***********************************************************
另外,像 sprintf 之类的函数,如果产生 缓冲区溢出,是终止整个进程,还是 返回-1之类的? 还是说编译器有关?
11 个解决方案
#1
其实电脑开机后物理内存的每个字节都是可读写的,从来不会因为所谓的new、delete或malloc、free而被创建、销毁。区别仅在于操作系统内存管理模块在你读写时是否能发现并是否采取相应动作而已。操作系统管理内存的粒度不是字节而是页,一页通常为4KB。
#2
栈破坏,是未定义行为。可能正确,可能错误,可能马上崩溃,可能运行到某个毫无关联的地方修改了数据。所有情况都可能发生。而且编译器基本帮不上忙,只能自己小心。
#3
的确有溢出,但不报错是因为其越界访问只是发生在程序自己内存地址空间内
程序结果出现这个问题是因为src和buf的地址空间是相邻的,而且src的地址比buf高,当进行sprintf时,此函数直到遇到src字符串中的'\0'才会结束(除非程序崩溃),因此sprintf只将abcdefghij(你会发现其长度等于buf的长度)拷到buf中,而将后面的字符串klmnopqrstuvwxyz拷到src中(因为src与buf的地址相邻)。
所以在printf时,该函数也是通过'\0'判断结尾的,所以会输出abcdefghijklmnopqrstuvwxyz,而src的数据已被更改,所以是klmnopqrstuvwxyz
程序结果出现这个问题是因为src和buf的地址空间是相邻的,而且src的地址比buf高,当进行sprintf时,此函数直到遇到src字符串中的'\0'才会结束(除非程序崩溃),因此sprintf只将abcdefghij(你会发现其长度等于buf的长度)拷到buf中,而将后面的字符串klmnopqrstuvwxyz拷到src中(因为src与buf的地址相邻)。
所以在printf时,该函数也是通过'\0'判断结尾的,所以会输出abcdefghijklmnopqrstuvwxyz,而src的数据已被更改,所以是klmnopqrstuvwxyz
#4
明白了,多谢!
#5
sprintf要慎用,
推荐使用sprintf_s
推荐使用sprintf_s
#6
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
#7
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
#8
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
#9
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。
发现某个变量值或某段内存值莫名其妙发生改变,请在该变量或该段内存设置数据改变断点后重新开始调试运行。
#include <time.h>
#include <stdlib.h>
#include <windows.h>
int main() {
int a,b[11];//本来是b[10],为判断哪句越界,故意声明为b[11]
srand((unsigned int)time(NULL));//按两次F11,等黄色右箭头指向本行时,调试、新建断点、新建数据断点,地址:&b[10],字节计数:4,确定。
while (1) {//按F5,会停在下面某句,此时a的值为10,b[10]已经被修改为对应0..4之一。
b[(a=rand()%11)]=0;
Sleep(100);
b[(a=rand()%11)]=1;
Sleep(100);
b[(a=rand()%11)]=2;
Sleep(100);
b[(a=rand()%11)]=3;
Sleep(100);
b[(a=rand()%11)]=4;
Sleep(100);
}
return 0;
}
#10
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。
发现某个变量值或某段内存值莫名其妙发生改变,请在该变量或该段内存设置数据改变断点后重新开始调试运行。#include <time.h>
#include <stdlib.h>
#include <windows.h>
int main() {
int a,b[11];//本来是b[10],为判断哪句越界,故意声明为b[11]
srand((unsigned int)time(NULL));//按两次F11,等黄色右箭头指向本行时,调试、新建断点、新建数据断点,地址:&b[10],字节计数:4,确定。
while (1) {//按F5,会停在下面某句,此时a的值为10,b[10]已经被修改为对应0..4之一。
b[(a=rand()%11)]=0;
Sleep(100);
b[(a=rand()%11)]=1;
Sleep(100);
b[(a=rand()%11)]=2;
Sleep(100);
b[(a=rand()%11)]=3;
Sleep(100);
b[(a=rand()%11)]=4;
Sleep(100);
}
return 0;
}
这些都知道,主要就那时候是c# 与 c/c++ 协作导致的内存问题,出得最多的就是越界,和破坏了申请到的内存的头部,崩溃通常在最终free处,所以看堆栈无效,后来发现原因,从哪以后才每次的内存操作谨慎起来
#11
我的例子代码已经明确地演示了如何利用数据断点捕获越界错误发生的代码行了。
#1
其实电脑开机后物理内存的每个字节都是可读写的,从来不会因为所谓的new、delete或malloc、free而被创建、销毁。区别仅在于操作系统内存管理模块在你读写时是否能发现并是否采取相应动作而已。操作系统管理内存的粒度不是字节而是页,一页通常为4KB。
#2
栈破坏,是未定义行为。可能正确,可能错误,可能马上崩溃,可能运行到某个毫无关联的地方修改了数据。所有情况都可能发生。而且编译器基本帮不上忙,只能自己小心。
#3
的确有溢出,但不报错是因为其越界访问只是发生在程序自己内存地址空间内
程序结果出现这个问题是因为src和buf的地址空间是相邻的,而且src的地址比buf高,当进行sprintf时,此函数直到遇到src字符串中的'\0'才会结束(除非程序崩溃),因此sprintf只将abcdefghij(你会发现其长度等于buf的长度)拷到buf中,而将后面的字符串klmnopqrstuvwxyz拷到src中(因为src与buf的地址相邻)。
所以在printf时,该函数也是通过'\0'判断结尾的,所以会输出abcdefghijklmnopqrstuvwxyz,而src的数据已被更改,所以是klmnopqrstuvwxyz
程序结果出现这个问题是因为src和buf的地址空间是相邻的,而且src的地址比buf高,当进行sprintf时,此函数直到遇到src字符串中的'\0'才会结束(除非程序崩溃),因此sprintf只将abcdefghij(你会发现其长度等于buf的长度)拷到buf中,而将后面的字符串klmnopqrstuvwxyz拷到src中(因为src与buf的地址相邻)。
所以在printf时,该函数也是通过'\0'判断结尾的,所以会输出abcdefghijklmnopqrstuvwxyz,而src的数据已被更改,所以是klmnopqrstuvwxyz
#4
的确有溢出,但不报错是因为其越界访问只是发生在程序自己内存地址空间内
程序结果出现这个问题是因为src和buf的地址空间是相邻的,而且src的地址比buf高,当进行sprintf时,此函数直到遇到src字符串中的'\0'才会结束(除非程序崩溃),因此sprintf只将abcdefghij(你会发现其长度等于buf的长度)拷到buf中,而将后面的字符串klmnopqrstuvwxyz拷到src中(因为src与buf的地址相邻)。
所以在printf时,该函数也是通过'\0'判断结尾的,所以会输出abcdefghijklmnopqrstuvwxyz,而src的数据已被更改,所以是klmnopqrstuvwxyz
明白了,多谢!
#5
sprintf要慎用,
推荐使用sprintf_s
推荐使用sprintf_s
#6
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
#7
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
#8
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
#9
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。
发现某个变量值或某段内存值莫名其妙发生改变,请在该变量或该段内存设置数据改变断点后重新开始调试运行。
#include <time.h>
#include <stdlib.h>
#include <windows.h>
int main() {
int a,b[11];//本来是b[10],为判断哪句越界,故意声明为b[11]
srand((unsigned int)time(NULL));//按两次F11,等黄色右箭头指向本行时,调试、新建断点、新建数据断点,地址:&b[10],字节计数:4,确定。
while (1) {//按F5,会停在下面某句,此时a的值为10,b[10]已经被修改为对应0..4之一。
b[(a=rand()%11)]=0;
Sleep(100);
b[(a=rand()%11)]=1;
Sleep(100);
b[(a=rand()%11)]=2;
Sleep(100);
b[(a=rand()%11)]=3;
Sleep(100);
b[(a=rand()%11)]=4;
Sleep(100);
}
return 0;
}
#10
这种情况运行结果本来就是未知的,何必呢,自己小心不要有这种问题就好了么;不然程序里突然在个未知的地方崩溃了,问题定位都烦死
烦死的原因是“和你一样的99%的码农都不会看Call Stack和使用Data Breakpoint”
Call Stack和Data Breakpoint 都没有呢,以前对内存没留意,不小心破坏以后,崩溃的地方和造成内存破坏的地方天各一方呢
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。
发现某个变量值或某段内存值莫名其妙发生改变,请在该变量或该段内存设置数据改变断点后重新开始调试运行。#include <time.h>
#include <stdlib.h>
#include <windows.h>
int main() {
int a,b[11];//本来是b[10],为判断哪句越界,故意声明为b[11]
srand((unsigned int)time(NULL));//按两次F11,等黄色右箭头指向本行时,调试、新建断点、新建数据断点,地址:&b[10],字节计数:4,确定。
while (1) {//按F5,会停在下面某句,此时a的值为10,b[10]已经被修改为对应0..4之一。
b[(a=rand()%11)]=0;
Sleep(100);
b[(a=rand()%11)]=1;
Sleep(100);
b[(a=rand()%11)]=2;
Sleep(100);
b[(a=rand()%11)]=3;
Sleep(100);
b[(a=rand()%11)]=4;
Sleep(100);
}
return 0;
}
这些都知道,主要就那时候是c# 与 c/c++ 协作导致的内存问题,出得最多的就是越界,和破坏了申请到的内存的头部,崩溃通常在最终free处,所以看堆栈无效,后来发现原因,从哪以后才每次的内存操作谨慎起来
#11
我的例子代码已经明确地演示了如何利用数据断点捕获越界错误发生的代码行了。