现在开发的项目是从solaris到linux的应用移植。经常用到popen函数,使用8192字节的数组读取popen输出,但没有进行溢出判断。
刚开始认为是一个简单的内存越界,但对popen和PIPE调查以后,疑惑越来越多了。
1)问题的引出
popen使用管道来记录被调用命令的输出,那么popen的最大写入字节数必然是管道的最大值。
使用linux的ulimit -a来查看系统限制:
[syscom@sysbase0-0 linux]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 16204
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
查看solaris的系统限制:
bash-3.00$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
open files (-n) 256
pipe size (512 bytes, -p) 10
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 29995
virtual memory (kbytes, -v) unlimited
无论是4096还是5120都是远远小于8912的。因此使用8912字节的buf来读取popen的输出时绝对不会出现内存越界问题了。
2)问题的深入
通过ulimit看似得到了正确的结果,但在实际测试中却让人大跌眼镜!
测试程序:
test_popen.c
#include
int main()
{
FILE *fp;
int i;
char *p;
char buf[128];
fp = popen("./test_print", "r");
if(fp ==NULL) {
printf("NG\n");
return -1;
}
fgets(buf, 128, fp);
pclose(fp);
return 0;
}
#include
int main()
{
unsigned int i;
for(i=0; i<0xffffffff;i++)
printf("a");
return 0;
}
3)探究原理。
通过man 7 pipe来理解PIPE(我的man版本是1.6f)
PIPE_BUF
POSIX.1-2001 says that write(2)s of less than PIPE_BUF bytes must be
atomic: the output data is written to the pipe as a contiguous
sequence. Writes of more than PIPE_BUF bytes may be non-atomic: the
kernel may interleave the data with data written by other processes.
POSIX.1-2001 requires PIPE_BUF to be at least 512 bytes. (On Linux,
PIPE_BUF is 4096 bytes.) The precise semantics depend on whether the
file descriptor is non-blocking (O_NONBLOCK), whether there are multi-
ple writers to the pipe, and on n, the number of bytes to be written:
在谷歌上搜索内核源码,在3.10的内核中有如下:
133 /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
134 memory allocation, whereas PIPE_BUF makes atomicity guarantees. */
135 #define PIPE_SIZE PAGE_SIZE
明确说明了PIPE_BUF和PIPE_SIZE的不同
进一步调查,发现在2.6.11之前,PIPE_SIZE为4k,之后内核中PIPE_SIZE为64k,那为何能写入多达4G的字符呢?
这时我想到了popen的源码,查看了popen在BSD的实现,除了使用pipe函数外,与system调用并无区别。
点我查看popen源代码
4)结论
依赖linux的pipe特性的程序并不是好的设计,非常容易出乱子(目前还未发生),最好还是老老实实地做内存越界判断,降低与系统的耦合性。
欢迎转载,转载请注明出处和链接
by judwenwen2009