|
|
Segment fault 之永远的痛
|
|
写程序好多年了,Segment fault 是许多C程序员头疼的提示。指针是好东西,但是随着指针的使用却诞生了这个同样威力巨大的恶魔。
Segment fault 之所以能够流行于世,是与Glibc库中基本所有的函数都默认型参指针为非空有着密切关系的。
不知道什么时候才可以有能够处理NULL的glibc库诞生啊!
不得已,我现在为好多的函数做了衣服,避免glibc的函数被NULL给感染,导致我的Mem访问错误,而我还不知道NULL这个病毒已经在侵蚀我的身体了。
Segment fault 永远的痛......
像大虾一样的虾,像菜鸟一样的菜。
高手之中的高高手,菜鸟里的菜菜鸟。
www.socketchat.com
|
|
|
Re: Segment fault 之永远的痛
|
|
NULL怎么处理?
喜欢linux, 喜欢自由软件
|
|
|
Re: Segment fault 之永远的痛
|
|
给glibc函数做衣服啊!
例如 glibc 的 strcpy 函数
如果你的程序出现:
dst =getdst(...);
src=getsrc(...);
strcpy( dst, src );
这里假设,getsrc是从磁盘上读取文件,当读取错误的时候返回NULL
想想这可怕的后果吧。
给strcpy做件衣服:
char *StrCpy( char *dst, char *src )
{
if ( dst == NULL )
{
printf("StrCpy: dst==NULL!\n");
return(NULL);
}
if( src == NULL ) return(NULL);
{
printf("StrCpy: src==NULL!\n");
return( strcpy(dst,src) );
}
这样就暖和多了!
有时候我们在写函数的时候经常会判断型参的可能性,做了各种判断,但是glibc却不管,他总认为你给他的数据没有错误,结果导致很多的segment fault错误在用gdb看的时候不是在自己的程序里面出错,而是在glibc的函数库里面出错。
让你摸不着头绪,找不到错误的所在。
像大虾一样的虾,像菜鸟一样的菜。
高手之中的高高手,菜鸟里的菜菜鸟。
www.socketchat.com
|
|
|
Re: Segment fault 之永远的痛
|
|
对不起,我是刚刚开始在Linux下编程
我在调试程序的时候,发现即使在使用了:-g参数的时候,在程序崩溃时,没有core文件。
后来用:ulimit -c发现结果是:0
我咨询了红旗的技术支持,他们让我修改/etc/profile文件。将
ulimit -c 0
改为:
ulimit -c 1000
或更大的值
但是似乎仍然不起作用。
您能告诉我如何改变这个值,然后能得到调试信息。
|
|
|
Re: Segment fault 之永远的痛
|
|
这是从效率出发的,试想,假如strcpy库设计成你的那个样子,对每个参数进行检查,那么对于大多数参数正确的调用来说是浪费时间。所以库例程的设计者是把这些判定交给程序设计者来做。就象内核的缺页中断一样,内核并不检查进程访问的页是否存在,而是在访问失效的时候去解决失败的情况。同理,我认为没必要采用你所使用的那种包装,我的做法是在程序不能确定指针的正确性的时候,应该加上空指针检查处理,接管SIGSEGV信号去处理意外情况,可以避免Segment fault之痛。
人生得意须尽欢,
莫使金樽空对月。
天生我材必有用,
千金散尽还复来。
|
|
|
Re: Segment fault 之永远的痛
|
|
当然多余的检查是不必要的,在release的时候我就把检查给拿掉了。
在dev/debug的时候,这个衣服我是一定会给函数们穿上的。
我写的程序基本都是服务器程序,安全第一,我也受够了Segment fault之苦,在安全的基础之上提高效率。
捕获SIGSEGV没有什么用处吧?捕获了之后能做的也就是exit了,还能做什么?只不过是看不到Segment fault这行令人痛苦的文字。
我倒是希望glibc可以有两个版本,一个用于debug,一个用于release。
debug版本对参数进行严格检查,release版本提供最好的性能。
我是热切期盼这一天的到来。
现在我正在修改我的“衣服“,用#define 的方式改写我定义的那些“衣服“,release的时候就不会再套上一层没有用的函数了。
为了保证不出现缓冲区溢出,我把sprintf重新定义成了snprintf,默认的n=64,所以我申请内存的malloc函数的衣服多了一项检查,小于64字节的申请自动扩大到64字节。
string.h 中定义的函数更是全部使用带有n的函数,屏蔽不带n的函数。
原来不写服务器程序的时候,根本不去注意这些,strcpy多好用,strncpy还要用strlen算长度,键盘敲的多,运行起来还占用cpu时间。学习编程的书上也基本上不提这些,导致很多的编程人员重复一遍又一遍的吃亏上当。有些人根本都不知道有strncpy,因为在某些函数手册上这些函数都被放在整个章节的最后面,翻书的时候看到strcpy就不再往后看了。
痛苦了好多年了,去年在看一篇文章的时候提到使用带有n的函数,可以提高系统的稳定性,安全性。这才恍然大悟,翻开许久都没有看的函数手册,找到这些个基本从来都没有看过的函数。
突然感觉,过去的好几年就象从来都没有写过程序。浪费了太多的时间去debug,写完了还有许多的bug,让程序莫名的halt
痛啊!痛......痛......痛......痛......痛......痛......
像大虾一样的虾,像菜鸟一样的菜。
高手之中的高高手,菜鸟里的菜菜鸟。
www.socketchat.com
|
|
|
Re: Segment fault 之永远的痛
|
|
我也是写daemon的。你的大部分观点我都赞同,比如我也屏蔽掉string.h中所有的不检查长度的例程。
我在捕获SIGSEGV的时候一般是不退出,我认为有些Segment fault是意外,应该继续执行下去。这也是无奈。
对于调试不知兄台有什么好的经验,可以切磋一下,我一般是输出调试信息到log文件,然后慢慢分析。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
|
|
|
Re: Segment fault 之永远的痛
|
|
-g是向目标文件中增加调试信息,和产生core无关。
但是我也忘了怎么让core dump了。
喜欢linux, 喜欢自由软件
|
|
|
Re: Segment fault 之永远的痛
|
|
调试的时候我跑strace,先不做成daemon。
输出直接到console,然后用nohup 包起来。
输出信息就直接写在nohup.out里面了。
程序出问题了,就可以知道很多的信息。
出现Segment fault就有可能存在buffer overflow 的问题。一般的来说只要是遇到对NULL指针进行操作的都可以继续运行,因为在0x0位置的内存一般都禁止用户级的度写操作,如果你忽略信号的话,可以继续运行,不会影响其他的内存数据,但是你的程序里面肯定存在一个没有经过处理的函数返回值。
一般的来说我们在处理返回值是指针类型的函数时,遇到错误都会返回NULL,然后我们可能会拿这个返回的指针作为实参,传递给其他的函数,如果你在调用其它函数的时候将一个值为NULL的指针做为实参传递给另外一个函数,这里就会出问题。
最常见的就是,指针结构体,链表、二叉树、图等。如果p=NULL,而p是一个二叉树的结构指针,那么你引用 p->left,p->right,p->value的时候都会造成Segment fault
一般的来说Segment fault的出现,都预示着程序中存在指针的引用错误。
我认为Segment fault是绝对不能忽略的,碰到这样的信号就应到立即终止执行程序,保证整个系统的安全。
像大虾一样的虾,像菜鸟一样的菜。
高手之中的高高手,菜鸟里的菜菜鸟。
www.socketchat.com
|
|
|
Re: Segment fault 之永远的痛
|
|
写daemon时确实比较麻烦,输出到文件有时候无法做动态分析,实在不行还可以将它放在某个指定的消息队列中,然后用一个服务程序将它发送出来,在另外一台计算机上进行监视。对网络服务程序可以用这一招。
|
|
|
ulimit -c unlimited
|
|
.
--------------------------
什么俗就喜欢什么
|
|
|
Re: Segment fault 之永远的痛
|
|
这种方法我以前也用过,但我发现在调试多进程或者多线程时还是日志文件的方式比较方便,虽然实时性不太好,但是有利于我慢慢分析。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
|
|
|
Re: Segment fault 之永远的痛
|
|
Add the following into your .cshrc
unlimit coredumpsize
I think it will be okay!
|
|
|
Re: Segment fault 之永远的痛
|
|
http://irccrew.org/~cras/security/c-guide.html
glib is also good, but too big and too OO.
|
|
|
Re: Segment fault 之永远的痛
|
|
我是个菜鸟,我想问一下能不能用memcpy函数啊
|
|
|
这个文章非常不错,研读中......
|
|
|
|
|
Re: Segment fault 之永远的痛
|
|
”输出直接到console,然后用nohup 包起来“
这句话怎么解释啊?
用strace就是segmentation fault也看不到问题在哪儿,倒是
gdb可以看到点
|
|
|
Re: Segment fault 之永远的痛
|
|
strace当然可以看到很多的东西啊!
nohup [exefile]
cat nohup.out
[exefile] 在运行的时候产生大量调试信息在nohup.out里面都可以看到
|
|
|
Re: Segment fault 之永远的痛
|
|
#in etc/profile
ulimit -c unlimited
|
|
|
程序写成这样只能说是失败。
|
|
该在什么地方判断的东西就在什么地方判断,无限制地加保护对程序毫无好处,你判断得过来吗?如果你的src不是0(NULL),而是1呢?
|
|
|
Re: 程序写成这样只能说是失败。
|
|
就是啊,没有考虑边界的程序怎么能信任?从网络,外模块等外来的数据,都不能做理所当然的假设。
或者试用C++/Java,用NULL object模式(参考《敏捷开发》)可以实现一种外形紧凑的表达形式。
|
|
|
Re: 程序写成这样只能说是失败 ---- 说得好
|
|
我认为楼上说得有道理
strcpy没有义务替你检查你的指针是否是NULL
一个函数只完成一个功能,你要做的不是把strcpy变得功能越来越复杂,而是想办法
用别的方法来保证不会出错。
你可以看看 vsftpd和apache得代码是怎么写的,它们可没有把strcpy给包装得那么复杂
整个vsftpd的代码就4个地方用了strcpy/strncpy
看看vsftpd中对strcpy的包裹 :
void
vsf_sysutil_strcpy(char* p_dest, const char* p_src, unsigned int maxsize)
{
if (maxsize == 0)
{
return;
}
strncpy(p_dest, p_src, maxsize);
p_dest[maxsize - 1] = '\0';
}
|
|
|
Re: 程序写成这样只能说是失败 ---- 说得好
|
|
呵呵,精辟精辟。vsftpd 的代码,一定有很多值得学习的地方。
|