漏洞成因
- pipe维护了一个
struct pipe_buffer
的数组,每个pipe_buffer指向一个page,page里存的就是pipe的数据 - 正常情况下,往pipe里写数据时会申请一个page,把数据拷贝到page里后再让pipe_buffer指向这个page。splice系统调用实现了一种零拷贝的技术,直接让pipe_buffer指向这个原始的数据page,这样就省去了内存拷贝的过程,提升效率
- 往pipe里写数据时不可能每次都正好是page_size的整数倍,如果每次写数据都要重新分配一个新的page来存,必然会造成空间的浪费。但是如果pipe_buffer的
PIPE_BUF_FLAG_CAN_MERGE
flag被置位,数据就会接着上一次的数据在同一个page中写入,而不是申请新的page,减少了空间的浪费 - 但是splice在给pipe_buffer赋值时没有初始化flag,这就造成之前被置位的
PIPE_BUF_FLAG_CAN_MERGE
flag不会被清除,所以只要先让所有的pipe_buffer的PIPE_BUF_FLAG_CAN_MERGE
flag被置位,然后调用splice让pipe_buffer指向目标文件page cache,这时再向pipe里写数据就会直接修改page cache里的内容,造成任意文件覆盖漏洞