- 浏览: 111207 次
- 性别:
- 来自: 昆明
-
文章分类
- 全部博客 (151)
- 120D02 (5)
- 直升机 (1)
- 我的技术资料收集 (82)
- 的技术资料收集 (4)
- .NET Solution (2)
- ASP.NET (1)
- Linq to sql (1)
- 数据库技术(MS SQL) (2)
- 架构/设计 (1)
- 敏捷/持续集成 (1)
- C#.NET开发 (1)
- Matlab开发 (1)
- WinForm开发 (1)
- 开源技术 (1)
- jQuery (1)
- 我的博文 (4)
- js (2)
- android (2)
- 9. 读书笔记 (1)
- CSS3 (1)
- HTML5 (1)
- JavaScript (5)
- 移动开发 (2)
- 编程心得 (1)
- Linux操作系统 (1)
- (BI)商业智能 (1)
- IOS (1)
- Windows Phone (2)
- C# API (1)
- JQuery系列 (1)
- TFS (1)
- C# (2)
- ExtJs (1)
- .NET (1)
- Nginx (1)
- WCF学习笔记 (1)
- Computer Graphic (1)
- IT产品 (1)
- 工具分享 (1)
- MySelf (1)
- C#专栏 (1)
- 管理 (1)
- 基于Oracle Logminer数据同步 (1)
- 日常 (1)
- 实用工具 (1)
- 网页设计 (1)
- avalon (1)
- flash (1)
- DDD (1)
- 01 技术Android (1)
- WCF (1)
- selenium (1)
最新评论
-
464410531:
三国杀。。。。。。。。。。。。。。。。。。。。。。。。。。。。 ...
实用的职场宝典:不提拔你,就因为你只想把工作做好
<style><!--<br/>/*--><![CDATA[/*><!--*/<br/> pre{background-color: #817A7B;}<br/> img{border: 1px solid gray;}<br/>--></style>
<script type="text/javascript">// <![CDATA[<br/>/*--><![CDATA[/*><!--*/<br/> function CodeHighlightOn(elem, id)<br/> {<br/> var target = document.getElementById(id);<br/> if(null != target) {<br/> elem.cacheClassElem = elem.className;<br/> elem.cacheClassTarget = target.className;<br/> target.className = "code-highlighted";<br/> elem.className = "code-highlighted";<br/> }<br/> }<br/> function CodeHighlightOff(elem, id)<br/> {<br/> var target = document.getElementById(id);<br/> if(elem.cacheClassElem)<br/> elem.className = elem.cacheClassElem;<br/> if(elem.cacheClassTarget)<br/> target.className = elem.cacheClassTarget;<br/> }<br/>/*]]>*/<br/>// ]]></script>
我的博客:http://blog.striveforfreedom.net
1 BUG描述
最近修改一C程序,在一个结构体里加入了几个新的字段,编译完一跑竟然出现段错误(segmentation fault)崩溃了。用gdb查看,引发崩溃的是一条这样的指令:mov register offset(%rsp)。
2 解决过程
从引发崩溃的指令可以看出,崩溃的原因是访问了栈上的内存,然而通常来说访问栈上内存是不会导致段错误的,因为栈上内存不需要程序员手动管理,一般来说很难出错。猜测有可能是栈溢出了,需要证实这个想法。发生崩溃的机器是X86_64+Linux,用ulimit -s得知进程栈默认的soft limit是10MB,因为程序代码并没有调用setrlimit调整过栈的soft limit,于是需要证明出现段错误的进程栈大于10MB了,导致崩溃的地址可以从gdb中查看,如果知道栈的起始地址(栈底),两者之差就是栈的大小。但如何才能知道栈的起始地址呢?我们知道Linux有个proc文件系统,系统里每个进程在/proc下都有一个以进程ID命名的文件夹,/proc/PID下包含的都是进程ID为PID的进程的相关信息,比如说该进程对应的可执行文件路径/当前目录/打开的文件等。其中/proc/PID/maps包含了该进程所有虚拟区域的起始和结束地址,包括栈(即文件maps里最后一个字段为[stack]对应的那一行)。拿到了栈的起始地址之后,用起始地址减去引发崩溃的那条指令中访问的内存地址(即rsp加一个偏移),得到的值果然大于10MB了。至于栈溢出的原因,原有代码在栈上定义了一个结构体的数组,而我在结构体里面加了几个size比较大的字段,因此溢出了。找到原因,BUG修改起来就很简单了,要么在shell里修改栈默认的soft limit,要么在代码里调用setrlimit,要么在堆上分配内存。
为了说明这个BUG,我写一段测试代码作为例子,代码如下:
int main(int argc, char* argv[])
{
const unsigned len = 10 * (1U << 20);
char data[len];
data[0] = 'a';
return 0;
}
编译完一运行,出乎意料的是,进程竟然没崩溃!这就非常奇怪了,因为我在main函数里定义了10MB大小的数组(并且访问了第一个元素,即最地址最小的那个),且不说环境变量所占空间,单这个数组加上C运行库调用序列所占空间就超过10MB了,而栈soft limit是10MB,按理说必然崩溃。然而实际却没有崩溃,一开始我怀疑代码被优化掉了,用objdump一看并没有优化掉,接着想了很久都没有头绪,最后终于想起去查看内核代码,看看内核到底是怎么处理栈溢出的。这包含两个方面,一是栈的soft limit是怎么读取出来的,二是内核怎么检查栈大小是否超过soft limit了。发生崩溃的机器上装的是CentOS 5.7,用uname -r得到的内核版本是2.6.18-308.el5,这个版本号跟官方内核版本号对应不上,因为感觉应该很接近2.6.18,于是就查看了官方内核2.6.18的代码(推荐下 lxr.linux.no ,查看某个版本内核代码很方便,不用去下载几十M的源码包了)。
读取资源限制(soft limit & hard limit)是由系统调用getrlimit完成的,getrlimit在内核中的入口是sys_getrlimit,代码如下:
asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim)
{
if (resource >= RLIM_NLIMITS)
return -EINVAL;
else {
struct rlimit value;
task_lock(current->group_leader);
value = current->signal->rlim[resource];
task_unlock(current->group_leader);
return copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
}
}
这个函数很简单,就是把当前进程的某种资源限制读出来,并复制到用户空间,没有发现什么问题。
对栈的大小限制的检查是在页面异常(page fault)处理中完成的,从页面异常入口page_fault开始,查看调用序列page_fault > do_page_fault > expand_stack > acct_stack_growth,在函数acct_stack_growth中发现了对栈大小限制进行检查的代码,如下(省略了跟我们这个例子无关的代码):
static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, unsigned long grow)
{
//...
struct rlimit *rlim = current->signal->rlim;
//...
/* Stack limit test */
if (size > rlim[RLIMIT_STACK].rlim_cur)
return -ENOMEM;
//...
}
其中,参数size是栈的起始地址减去当前引发页面异常的地址并按页大小向上对齐的,很显然,这里如果发现栈大小大于soft limit就返回错误,最终会给当前进程发送SIGSEGV信号,导致进程出现段错误崩溃。上面的内核代码说明我的想法是正确的,然而进程并未和我预料的那样崩溃,一想可能是内核版本不对,于是又查看了官方2.6.19版的代码,发现这两处的代码并没有改过。这就很奇怪了,过了一会我突然想到,机器上装的是CentOS,CentOS可能修改了这处的官方内核代码,于是我下载了和我系统对应的源码包kernel-2.6.18-308.el5.src.rpm,安装之后,发现该版本对应的官方内核版本是2.6.18.4,CentOS的修改过的代码放在一个patch文件kernel-2.6.18-redhat.patch里,运行patch之后,果然发现CentOS修改了acct_stack_growth函数,修改如下(该函数有多处修改,这里只列出了跟我们这个BUG相关的修改):
static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, unsigned long grow)
{
//...
struct rlimit *rlim = current->signal->rlim;
//...
/* Stack limit test */
if (over_stack_limit(size))
return -ENOMEM;
//...
}
一比较就可以发现官方内核代码是直接比较size和栈的soft limit,而CentOS把这个比较放进了函数over_stack_limit里,再来看函数over_stack_limit:
static int over_stack_limit(unsigned long sz)
{
if (sz < EXEC_STACK_BIAS)
return 0;
return (sz - EXEC_STACK_BIAS) >
current->signal->rlim[RLIMIT_STACK].rlim_cur;
}
其中EXEC_STACK_BIAS是一个整型常量,定义如下:
#define EXEC_STACK_BIAS (2*1024*1024)
很显然,CentOS把栈大小限制从soft limit往上提高了2MB,如果栈大小超过栈的soft limit+EXEC_STACK_BIAS(在我们这个例子中为12MB)则说明栈溢出。到此真相大白,把上面的测试代码修改一下(把数组大小改为12MB),再一运行进程果然崩溃。
3 小结
有时候接近问题的真相,但并没有发现问题的全部,就这个问题来说,如果我不写这篇文章,也就不会去写上面那段测试代码,就会想当然地认为在我的机器上栈的默认大小限制是10MB了。
发表评论
-
Javascript:猜猜弹出的是啥?为啥? - 幸福框架
2013-06-28 13:33 447原帖地址:http://www.cnblogs.com/hap ... -
C#中WindowsForm常见控件的运用 -- - 李晓峰
2013-06-28 13:27 1766原帖地址:http://www.cnblogs.com/liy ... -
海量数据处理利器之Hash——在线邮件地址过滤 - MyDetail
2013-06-27 12:00 672原帖地址:http://www.cnblo ... -
ASP.NET MVC 4 for Visual Studio 2010 下载地址 - 张鸿伟
2013-06-27 11:48 764原帖地址:http://www.cnblogs.com/wei ... -
【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化 - r01cn
2013-06-26 11:00 927原帖地址:http://www.cnblogs.com/r01 ... -
[珠玑之椟]估算的应用与Little定律 - 五岳
2013-06-26 10:54 651原帖地址:http://www.cnblogs.com/wuy ... -
30行,金额转人民币大写的代码 - 史蒂芬.王
2013-06-26 10:42 1038原帖地址:http://www.cnblogs.com/ste ... -
从银行的钱荒看一个公司的团队建设 产品线过多最终导致最赚钱的项目面临破产 - James Li
2013-06-26 10:36 646原帖地址:http://www.cnblogs.com/Jam ... -
Windows 8 动手实验系列教程 实验6:设置和首选项 - zigzagPath
2013-06-25 13:39 553原帖地址:http://www.cnblogs.com/zig ... -
闲聊可穿戴设备 - shawn.xie
2013-06-25 13:33 628原帖地址:http://www.cnblo ... -
如何使用开源库,吐在VS2013发布之前,顺便介绍下VS2013的新特性"Bootstrap" - 量子计算机
2013-06-25 13:27 883原帖地址:http://www.cnblogs.com/DSh ... -
一步一步将自己的代码转换为观察者模式 - 文酱
2013-06-23 11:36 630原帖地址:http://www.cnblo ... -
iOS内存错误EXC_BAD_ACCESS的解决方法(message sent to deallocated instance) - VicStudio
2013-06-23 11:30 560原帖地址:http://www.cnblogs.com/vic ... -
记录asp.net在IE10下事件丢失排错经过 - Adming
2013-06-23 11:24 727原帖地址:http://www.cnblogs.com/wea ... -
记 FineUI 官方论坛所遭受的一次真实网络攻击!做一个像 ice 有道德的黑客! - 三生石上
2013-06-23 11:18 812原帖地址:http://www.cnblogs.com/san ... -
3、使用Oracle Logminer同步Demo
2013-06-19 10:33 583原帖地址:http://www.cnblogs.com/shi ... -
算法实践——数独的基本解法
2013-06-19 10:27 1468原帖地址:http://www.cnblogs.com/gre ... -
简单实现TCP下的大文件高效传输
2013-06-19 10:21 705原帖地址:http://www.cnblogs.com/sma ... -
avalon - 初步接触
2013-06-18 10:06 796原帖地址:http://www.cnblogs.com/aar ... -
Nginx学习笔记(一) Nginx架构
2013-06-18 09:59 543原帖地址:http://www.cnblogs.com/cod ...
相关推荐
当创建一个线程的堆栈时,系统将会保留一个链接程序的/STACK开关指明的地址空间区域。如果想要增大堆栈的大小,可以重载原先提交的内存数量。例如,在Java中,可以使用thread函数来设置栈大小。 在Java中设置栈大小...
栈溢出在历史上是安全漏洞中的一个重要问题。 8. 栈调试工具: - OllyDbg:是一个流行的Windows平台下的汇编级调试器,可以帮助开发者分析程序的栈结构以及调试栈相关的bug。 9. 实际代码示例分析: 文档中的...
通过观察栈的变化,可以发现由于内存分配不当导致的错误,如栈溢出等。 #### 4.2 观察堆 堆是动态分配内存的地方。了解堆的使用情况对于防止内存泄漏等问题至关重要。例如,使用智能指针等技术可以自动管理内存,...
本文主要探讨一个关于软件开发中的低级bug排查案例,该bug虽然简单却导致了一个原本简单的程序耗时超过12小时才得以修复。通过深入分析这个案例,我们可以从中汲取宝贵的经验教训,了解在开发过程中如何避免此类问题...
递归可能导致栈溢出,尤其是在处理大量数据时。尽可能使用循环代替递归,或者优化递归算法以减少函数调用。 七、不恰当的集合操作 使用集合时,应选择适合数据类型的集合,如ArrayList适用于随机访问,而LinkedList...
3. **漏洞利用**:详细阐述如何利用漏洞,例如栈溢出漏洞的exploit编写,包括返回到libc、shellcode、NOP滑梯等技术。同时,可能会讨论如何避免被安全防护机制(如DEP、ASLR)阻止。 4. **漏洞报告与响应**:讲解负...
在这个有趣的BUG案例中,问题出在了一个对象的`toString()`方法上。通常,当我们打印一个对象时,Java会自动调用该对象的`toString()`方法来生成表示对象状态的字符串。开发者使用了Lombok库,该库能够自动生成如`...
”是一个典型的程序员面临的问题,特别是在软件开发中,堆栈溢出可能导致严重的系统崩溃和安全漏洞。这个问题涉及到计算机科学(标签为“cs”),特别是编程和内存管理领域。 堆栈溢出是由于程序在运行过程中,函数...
错误的数据结构可能导致逻辑错误,而低效的算法可能导致运行时间过长,甚至在大型数据集上导致栈溢出。 9. **多线程编程**:在多线程环境中,竞态条件、死锁和资源争抢可能导致bug。理解互斥量、条件变量和线程安全...
《Bug Hunter Diary_Exploit_Diary_assembly_》是一个关于漏洞利用开发的主题,结合了“Exploit Diary”和“assembly”两个关键概念,暗示着本书将深入探讨使用汇编语言进行漏洞挖掘与利用的技术。在IT安全领域,...
**示例**: 下面的代码会导致栈溢出异常: ```java public static void main(String[] args) { main(null); } ``` **解决方法**: 1. **添加终止条件**: 确保递归函数中有明确的终止条件。 2. **优化算法**: 使用非...
递归的关键在于递归终止条件的设定,否则容易导致无限递归甚至栈溢出(stack overflow)。 - **递归的优点**: - 代码简洁易懂。 - 对于某些特定问题(如树的遍历、分治算法等),递归是最佳解决方案。 - **递归的...
在压缩包文件中,"Apache 1.2.19 mod_jk 远程栈溢出漏洞.py"可能是一个针对Apache Tomcat服务器的mod_jk模块的漏洞利用脚本,虽然它与MS12-020不是直接相关,但显示了同一时期网络环境中其他可能的攻击面。...
LwIP 2.0是一个重要的更新,它在1.x版本的基础上增加了许多新特性并优化了原有功能。 LwIP的主要目标是为资源有限的嵌入式设备提供网络连接能力,如微控制器或物联网设备。它的轻量级特性使其能够在极小的内存占用...
5. **递归版本的栈溢出**:虽然递归实现二分查找很直观,但如果不正确地设置递归终止条件,可能会导致栈溢出。必须确保递归深度不超过数组长度的对数。 为了写出无bug的二分查找程序,我们需要对算法的每一步都进行...
其实后来我又在这个基础上改进了一版功能更加强大的扫雷,解决了初版的一些BUG(比如递归深度过大造成的栈溢出) 但是弄丢啦 ε=(´ο`*))),手头只有这一个最终版001了,哪天找到了的话我再发上来吧。当然如果你要...
2. **栈操作**:在后缀表达式计算过程中,使用一个栈来存储中间结果。遍历后缀表达式,遇到数字就入栈,遇到运算符就取出栈顶的两个元素进行运算,然后将结果压回栈中。最后,栈中的唯一元素就是表达式的值。 3. **...
1. `Apache 1.2.19 mod_jk 远程栈溢出漏洞.py`:这可能是针对Apache HTTP服务器的一个模块mod_jk的漏洞利用脚本。Apache mod_jk是用于连接Apache与Tomcat等Java应用服务器的模块。栈溢出漏洞通常涉及攻击者向程序...