问个C++的函数调用堆栈的问题

sopro 2006-12-16 05:37:18
linux环境下我写了程序,如下:
#include <iostream>
using namespace std;

char* func()
{
char cArray[] = "123456";
return cArray;
}

int main()
{
char* p = func();
cout << hex << reinterpret_cast<int>(p) << endl; //以16进制输出p的地址
}

结果:
bf8bcc41 (每次结果不一样)
bff442c1
...
(或者其他数值)

我觉的很奇怪!
我们暂且不论我返回的是一个局部数组的首地址(我知道这是绝对应该避免的),就现在出现的问题,我感到困惑。

一个进程再虚拟地址空间的分布我大概是了解的。在kernel下面,也就是0xc000000地址之下应该是进程的stack(根据函数调用的情况动态变化)。那么,认为,既然是相同的程序运行,进程在虚拟地址空间中的分布应该是一摸一样的,局部数组的首地址那么也应该是同一个虚拟地址(当然,物理地址很可能不一样,但是进程看到的应该只能是虚拟地址)
...全文
2243 51 打赏 收藏 转发到动态 举报
写回复
用AI写文章
51 条回复
切换为时间正序
请发表友善的回复…
发表回复
cyberkit 2006-12-27
  • 打赏
  • 举报
回复
估计是内核为了防止缓冲区溢出,故意使栈的地址不同。这样攻击者就难于预测函数的返回地址。

纯粹瞎猜
caocheng8230 2006-12-19
  • 打赏
  • 举报
回复
mark
陈硕 2006-12-19
  • 打赏
  • 举报
回复
这个称为“地址空间布局随机化(Address Space Layout Randomization)”,与缓冲区溢出攻击有关,是一项安全措施。
http://blog.csdn.net/Solstice/archive/2006/08/11/1051355.aspx
fengfeiwuwq 2006-12-19
  • 打赏
  • 举报
回复
mark
caitian6 2006-12-19
  • 打赏
  • 举报
回复
mark
bygreencn 2006-12-19
  • 打赏
  • 举报
回复
forget that you need to free p in main()
bygreencn 2006-12-19
  • 打赏
  • 举报
回复
局部数组的指针,等你调用结束都销毁了,那里还存在,
再说了,它也不是堆里申请得啊,它是在data里的


/* this is what you want */
#include <iostream>
using namespace std;

char* func()
{
char p[] = "123456";
char *cArray = (char *)malloc(7);
memset(cArray,0,7);
memcpy(cArray,p,7);
return cArray;
}

int main()
{
char* p = func();
cout << hex << reinterpret_cast<int>(p) << endl;
cout<< p<<endl;
}
lurenfu 2006-12-18
  • 打赏
  • 举报
回复
何况楼主在fun()函数中返回的是栈中的地址,在func函数返回时,栈中的那个地址不应该再被使用,想使用那个地址的想法是错误的
lurenfu 2006-12-18
  • 打赏
  • 举报
回复
地址每次都在变化才是正常的,因为最新的linux发行版中加了入防止缓冲区溢出的诸多措施,运行时的启动地址是变化的,这样就可以避免猜测缓冲区溢出地址的可能,还有其它几种方法,如设置stack区和data区不可执行等,具体可以参考secure enhanced linux
烂番 2006-12-18
  • 打赏
  • 举报
回复
正想说服楼主的说,,
却发现自己在win2003下编译的程序,每次运行都输出同样的结果,而且同时运行多个程序,竟然地址都是一样的,反而自己有点懵了=_=!!
jackiehb 2006-12-18
  • 打赏
  • 举报
回复
地址每次都在变,不懂,帮你顶一下
koolfool 2006-12-18
  • 打赏
  • 举报
回复
我用gdb调试了一下这段代码,发现一点有趣的现象

(gdb) print p
$1 = 0xfee63190 "123456"
(gdb) info proc map
process 3953
cmdline = '/home/koofool/c/test/stack'
cwd = '/home/koofool/c/test'
exe = '/home/koofool/c/test/stack'
Mapped address spaces:

Start Addr End Addr Size Offset objfile
0x1e4000 0x1f9000 0x15000 0 /lib/ld-2.3.3.so
0x1f9000 0x1fa000 0x1000 0x14000 /lib/ld-2.3.3.so
0x1fa000 0x1fb000 0x1000 0x15000 /lib/ld-2.3.3.so
0x1fd000 0x31e000 0x121000 0 /lib/tls/libc-2.3.3.so
0x31e000 0x320000 0x2000 0x120000 /lib/tls/libc-2.3.3.so
0x320000 0x322000 0x2000 0x122000 /lib/tls/libc-2.3.3.so
0x322000 0x324000 0x2000 0x322000
0x32c000 0x34d000 0x21000 0 /lib/tls/libm-2.3.3.so
0x34d000 0x34e000 0x1000 0x20000 /lib/tls/libm-2.3.3.so
0x34e000 0x34f000 0x1000 0x21000 /lib/tls/libm-2.3.3.so
0x351000 0x358000 0x7000 0 /lib/libgcc_s-3.4.2-20041018.so.1
0x358000 0x359000 0x1000 0x6000 /lib/libgcc_s-3.4.2-20041018.so.1
0x517000 0x5d8000 0xc1000 0 /usr/lib/libstdc++.so.6.0.3
---Type <return> to continue, or q <return> to quit---
0x5d8000 0x5dd000 0x5000 0xc1000 /usr/lib/libstdc++.so.6.0.3
0x5dd000 0x5e3000 0x6000 0x5dd000
0x8048000 0x8049000 0x1000 0 /home/koofool/c/test/stack
0x8049000 0x804a000 0x1000 0 /home/koofool/c/test/stack
0xf6ff5000 0xf6ff7000 0x2000 0xf6ff5000
0xfee62000 0xff000000 0x19e000 0xfee62000 //只有这段地址大小每次都不一样
0xffffe000 0xfffff000 0x1000 0
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) run
Starting program: /home/koofool/c/test/stack

Breakpoint 1, main () at memorymap.cpp:13
13 cout << hex << reinterpret_cast<int>(p) << endl; //以16进制输出p的地址
(gdb) print p
$2 = 0xfee93720 "123456"
(gdb) info proc map
process 3956
cmdline = '/home/koofool/c/test/stack'
cwd = '/home/koofool/c/test'
exe = '/home/koofool/c/test/stack'
Mapped address spaces:

Start Addr End Addr Size Offset objfile
0x1e4000 0x1f9000 0x15000 0 /lib/ld-2.3.3.so
0x1f9000 0x1fa000 0x1000 0x14000 /lib/ld-2.3.3.so
0x1fa000 0x1fb000 0x1000 0x15000 /lib/ld-2.3.3.so
0x1fd000 0x31e000 0x121000 0 /lib/tls/libc-2.3.3.so
0x31e000 0x320000 0x2000 0x120000 /lib/tls/libc-2.3.3.so
0x320000 0x322000 0x2000 0x122000 /lib/tls/libc-2.3.3.so
0x322000 0x324000 0x2000 0x322000
0x32c000 0x34d000 0x21000 0 /lib/tls/libm-2.3.3.so
0x34d000 0x34e000 0x1000 0x20000 /lib/tls/libm-2.3.3.so
0x34e000 0x34f000 0x1000 0x21000 /lib/tls/libm-2.3.3.so
0x351000 0x358000 0x7000 0 /lib/libgcc_s-3.4.2-20041018.so.1
0x358000 0x359000 0x1000 0x6000 /lib/libgcc_s-3.4.2-20041018.so.1
0x517000 0x5d8000 0xc1000 0 /usr/lib/libstdc++.so.6.0.3
---Type <return> to continue, or q <return> to quit---
0x5d8000 0x5dd000 0x5000 0xc1000 /usr/lib/libstdc++.so.6.0.3
0x5dd000 0x5e3000 0x6000 0x5dd000
0x8048000 0x8049000 0x1000 0 /home/koofool/c/test/stack
0x8049000 0x804a000 0x1000 0 /home/koofool/c/test/stack
0xf6ff5000 0xf6ff7000 0x2000 0xf6ff5000
0xfee92000 0xff000000 0x16e000 0xfee92000 //只有这段地址大小每次都不一样
0xffffe000 0xfffff000 0x1000 0
(gdb)
其中进程映射的地址空间内倒数第二段每次运行的大小都不一样,而指针所指向的空间恰恰就指向这段空间,它每次执行的开始地址都不同,但是结束地址都是0xff000000,而p指向的空间是从靠近开始地址那一端开始分配的,并且指针指向的空间距离这个开始地址每次的偏移也都是不同的
------------------------- 0xff000000



















<-----p 在靠近开始地址这段被分配
A
| 这段偏移量每次执行都不一样
V
------------------------- 0xfee92000 (或者0xfee62000) 这个开始地址每次执行也都不一样

如果说这就是进程的栈空间的话,似乎又太小了,只有0x16e000字节,估计这个现象的原因得深入研究一下linux的内核才行
renshuming 2006-12-18
  • 打赏
  • 举报
回复
mark
sopro 2006-12-18
  • 打赏
  • 举报
回复
-------------------------------
C++中一个大忌就是返回指针,堆栈中指针是动态分配,一旦函数执行完返回的指针就被注销。
每次当然返回不一样了。除非你用static,把函数指针弄成全局的,不过就算这样程序的稳定性大大折扣
-------------------------------

开头我就说过了,这个问题和返回局部对象的指针是否错误无关,我在意的是局部对象在虚拟空间中的布局问题


----------------
支持二楼cmail
----------------

你没明白我的意思


感谢大家的回复。
如果结合内核深入研究这个问题还是很有意思的。
我写这个代码的目标在于描述我的问题所在(不关注“返回局部对象的地址是错误的”这种浅层次的初级问题),很多人没看清楚,“野指针”、“乱码”、“不确定”这些词汇一个一个蹦出来了,呵呵。
还是谢谢大家。如果有更近一步的讨论,欢迎大家继续发贴~
CPP_CHEN 2006-12-18
  • 打赏
  • 举报
回复
支持二楼cmail
aiguozhe 2006-12-18
  • 打赏
  • 举报
回复
C++中一个大忌就是返回指针,堆栈中指针是动态分配,一旦函数执行完返回的指针就被注销。
每次当然返回不一样了。除非你用static,把函数指针弄成全局的,不过就算这样程序的稳定性大大折扣
lurenfu 2006-12-18
  • 打赏
  • 举报
回复
关于SE Linux,在下面有很多链接,感兴趣的自己去看看

http://fedoraproject.org/wiki/SELinux
sopro 2006-12-18
  • 打赏
  • 举报
回复
to:lurenfu(我是你的男佣)
-------------------------
地址每次都在变化才是正常的,因为最新的linux发行版中加了入防止缓冲区溢出的诸多措施,运行时的启动地址是变化的,这样就可以避免猜测缓冲区溢出地址的可能,还有其它几种方法,如设置stack区和data区不可执行等,具体可以参考secure enhanced linux
-------------------------

你说的很有道理!
一般的linux内核的书有描写这个特性么?Security-Enhanced Linux好像是美国NSA提出的安全理念把?
不管如何,给分!



to koolfool():
使用GDB查看proc map给了我一个很好的思路,谢谢,给分!

corn8888 2006-12-18
  • 打赏
  • 举报
回复
猜測可能是某段邏輯地址沒有映射到內存,所以給數組分配的內存地址可能往後挪了一下。並不一定非得由小到大。
------------------
os kernel必须保证virtual memory具有linear address的性质,不管虚拟地址和物理地址如何变化,每个进程所看到的虚拟地址空间应该都是一样的把? 都是0x00000000 - 0xffffffff
悄悄说一句:windows上面使用.net编个类似的程序,数组的首地址重来没变过。
所以,好挠头的问题啊!!!
likethis_way 2006-12-17
  • 打赏
  • 举报
回复
UP,感觉应该返回一样才对.
看起来4G空间使用方式还不是每次一样的?
是不是使用debug的原因,里面加了不少跟踪的信息
release试试?

也是不明白, 瞎猜的
加载更多回复(31)

64,666

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧