现在开发的项目是从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<stdio.h> 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 <stdio.h> 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特性的程序并不是好的设计,非常容易出乱子(目前还未发生),最好还是老老实实地做内存越界判断,降低与系统的耦合性。
linux中的PIPE_SIZE与PIPE_BUF,管道最大写入值问题
原文地址:http://blog.csdn.net/judwenwen2009/article/details/44134415