饼子堂比武贴--如何计算提交栈和保留栈的大小

laomai 2006-11-14 10:41:02
今天上午在饼子堂里讨论提交栈和保留栈,饼子们各舒己见,不过还是没有解答我的疑惑
故此开本比武贴请各方英豪同台献技,交流切磋,共同进益。
一、本此比武的内容
1、本贴主要讨论提交栈(commited stack)和保留栈(reserved stack)的具体含义,及
c/c++程序员应该如何设置这两个编译选项的值
2、名词解释
本文中所说的提交栈(commited stack)和保留栈是指windows平台下pe文件格式中_IMAGE_OPTIONAL_HEADER
结构中的两个成员
DWORD SizeOfStackReserve; //栈的保留大小 72-75
DWORD SizeOfStackCommit; //提交的栈的大小 76-79
pe文件的官方规范见
http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.doc
3、编译选项的设置
在vc6的菜单中,project->settings->link,在category中选择output,即可看到reserve和commit的输入框

二、讨论时的假设
为简化讨论,抓住问题的本质,对示例代码中的函数做如下要求
1、各函数的参数个数在编译时已知,函数的返回类型均为void
2、函数中的声明语句均为基本类型或者基本类型的数组,
诸如动态数组、结构数组、自定义类型数组不在考虑范围之内
3、函数间的调用均为直接调用,不存在递归关系,换句话,调用层数在编译时已知,阅读者从代码即可算出函数的调用层数
4、为了避免编译器优化掉未使用的变量,函数中的语句均为简单赋值语句,这样就不考虑临时对象的产生

综上所述,示例代码应保证函数栈中最好只为如下对象分配空间:
1、返回地址
2、数目固定的形参
3、数目已知的简单局部变量


三、示例代码
int main(int argc, char* argv[])
{
int buf[2<<20]={0}; //故意分配一个溢出的栈
buf [2<<19] = 1;
return 0;
}
这个代码在vc6可以编译通过,但是运行会提示stack overflow

四、比武要求
1、要求通过修改1.3中说的选项设置,使代码运行通过,并给出你设置的reserve和commit的值,
设置值最小者将获得50分的奖励
2、写出你的设置的根据,即在满足二、的假设的前提下,如何来计算reserve stck和commit stack
的取值范围,最好能给出一个通用的计算公式,达到此要求者奖励100分。
3、你可以自己写出其他测试代码说明你对这两个概念的理解,参与者奖励50分
...全文
2602 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
fixopen 2007-04-12
  • 打赏
  • 举报
回复
这个问题似乎应该完全没有讨论的基础,因为其实这是一个关于VMM的问题,跟C/C++的语法和语义没有任何关系,跟程序的结构和解析更没有任何关系,而且,这是一个随着实现的不同而完全不同的问题,所以,这个问题的前提或者环境就非常重要了。至于要计算其大小,更是非hack入kernel才能搞定的事情,而且,这样做带不来任何好处,只是1、让程序复杂了,2、让程序不可移植了。
KenYuan2016 2007-04-07
  • 打赏
  • 举报
回复
自己重载new还delete函数啊,申请释放空间自己决定如何操作。
thinkinnight 2007-04-01
  • 打赏
  • 举报
回复
要计算栈大小,就需要知道栈的组成,按我的理解,应该就是函数参数、寄存器ebp,esi等值和函数内部变量的值,这些都相加起来就可以计算出来最终需要栈的大小了。
thinkinnight 2007-03-31
  • 打赏
  • 举报
回复
产生了一下汇编

TITLE mm1.c
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT _main
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC _main
; COMDAT _main
_TEXT SEGMENT
_main PROC NEAR ; COMDAT
; File mm1.c
; Line 5
xor eax, eax
; Line 6
ret 0
_main ENDP
_TEXT ENDS
END

可以看到main没有push参数,因此8M都是int的buffer使用,此时不会overflow
thinkinnight 2007-03-31
  • 打赏
  • 举报
回复
汗,忽然发现错误了,分配的是int的,应该乘以4才是,这样就不错了
thinkinnight 2007-03-31
  • 打赏
  • 举报
回复
对于示例程序
int main(int argc, char* argv[])
{
int buf[2<<20]={0}; //故意分配一个溢出的栈
buf [2<<19] = 1;
return 0;
}
一开始以为分配buf为2^21,即1024*1024*2=2097152
再加上main函数的push,加上几个数
大概2^22肯定是可以的,但是根据实验,一直到设置
reserve=0x8fffff commit=0x8fffff
程序才运行通过
而reserve=0x7fffff commit=0x7fffff
程序继续stack overflow

0x8fffff=2^3*2^20=1024*1024*8=9437183

至于为什么,还是不了解,等看完mLee79()等大侠的程序再来看看是否可以解决出来。
thinkinnight 2007-03-31
  • 打赏
  • 举报
回复
因为连续回复不能超过3次,只能等到现在再发。

还是错了,看来/FA生成的还是少了
这是debug window中的汇编
还是要push edi,esi等4个

00401010 push ebp
00401011 mov ebp,esp
00401013 mov eax,800040h
00401018 call $$$00001 (00401080)
0040101D push ebx
0040101E push esi
0040101F push edi
00401020 lea edi,[ebp-800040h]
00401026 mov ecx,200010h
0040102B mov eax,0CCCCCCCCh
00401030 rep stos dword ptr [edi]
5: int buf[2<<20]={0}; //故意分配一个溢出的栈
00401032 mov dword ptr [ebp-800000h],0
0040103C mov ecx,1FFFFFh
00401041 xor eax,eax
00401043 lea edi,[ebp-7FFFFCh]
00401049 rep stos dword ptr [edi]
6: buf [2<<19] = 1;
0040104B mov dword ptr [ebp-400000h],1
7: return 0;
00401055 xor eax,eax
8: }
00401057 pop edi
00401058 pop esi
00401059 pop ebx
0040105A mov esp,ebp
0040105C pop ebp
0040105D ret


而且上面也计算错了,现在答案为
reserve:0x00800004
而commit可以不设,也可以设置为小于或者等于0x00800004的值。

但是上面mLee79()的程序,还是没看懂是什么意思。
为什么stack:0x1000
输出为 COMMIT: 0X2000 , RESERVED: 0X3C000
程序没看懂。希望有人能够帮着解释一下就好了。
eqyao 2007-03-31
  • 打赏
  • 举报
回复
up
wshcdr 2007-02-26
  • 打赏
  • 举报
回复
mLee79() 同学写得不错,小小地鼓掌一下
wshcdr 2007-02-26
  • 打赏
  • 举报
回复
看来要翻翻 某些 操作系统 的书本
linguangcan 2007-02-23
  • 打赏
  • 举报
回复
学习
ReverseEngineering 2006-12-24
  • 打赏
  • 举报
回复
WINDOWS的内存管理永远那么神秘!
DrawText 2006-12-22
  • 打赏
  • 举报
回复
学习一下!
ruidiisy 2006-12-12
  • 打赏
  • 举报
回复
reserve stack是保留栈的大小,当使用栈达到这个数值就会触发异常
示例程序中使用了2^21的大小,最小要设置为2^21+1才能运行通过。

commit stack是初始栈的意思吧,当超过这个大小的时候,会引发换页,这个值会影响程序运行的速度性能,不导致程序崩溃。
shileiyu 2006-11-17
  • 打赏
  • 举报
回复
1024*64*4=0x40000
使用了建立堆栈桢使用4 byte
所以40000不能过,40004能过
Leomaxking 2006-11-17
  • 打赏
  • 举报
回复
zyl910 2006-11-16
  • 打赏
  • 举报
回复
当创建一个新线程时(假设没有在调用CreateThread时设置栈大小),操作系统会这样初始化栈空间:
操作系统先为该线程的栈预留一片空间,这就是栈的保留大小(SizeOfStackReserve)。
然后为了避免浪费内存,操作系统只将其中的一部分(SizeOfStackCommit)映射到进程的虚拟内存空间去。


当程序递归调用时,会不断占用栈空间,最终会访问到SizeOfStackCommit尺寸之外的空间,此时会触发缺页中断。
操作系统对该缺页中断是这样处理的:增大栈空间,直至栈尺寸达到SizeOfStackReserve。若地址超过SizeOfStackReserve,就触发异常。

实际的处理还有很多细节
比如最后两页不可访问,专用于检测栈溢出
一般会在栈空间的最后保留几页,在普通情况下不可访问,只有在触发异常时才可访问,这是为了保证异常处理程序有足够的栈空间。


可以这样理解:
SizeOfStackCommit:栈的初始大小
SizeOfStackReserve:栈的最大尺寸
goldendreams 2006-11-16
  • 打赏
  • 举报
回复
高手
数组中的2<<20何意呀?int buf[2<<20]={0};
robin97 2006-11-15
  • 打赏
  • 举报
回复
老迈,你试试把那两个值都设为0x1000000,然后运行你那个测试程序
做鸡真好吃 2006-11-15
  • 打赏
  • 举报
回复
mark~
加载更多回复(10)

3,882

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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