kernel heap 3: msg_msg和pipe_buffer

2025-12-06

todo: mas_msgpipe_buffer在ctf/linux kernel漏洞利用的实际应用。

之前提到,在正常的linux kernel环境下,堆风水布局是十分困难的。主要原因是内核大都使用kmalloc来申请内存,有漏洞的驱动大概率也是用kmalloc申请的内存。
但是msg_msgpipe_buffer给我们提供了一个有效的进行堆布局的方法.

1. msg_msg

msg_msg是linux提供的一种进程间通信(IPC)的结构体。

1.1 msg常见用法

msg在linux本质上是通过内核消息队列来进行通信的。
主要函数如下:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

可以看一下demo:

//msg_send.c 用于发送msg
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <stdio.h>

int main(){
    // prepare message
    size_t target_cache_size = 128;
    struct msgbuf{
        long mtype;
        char message[100];
    };
    size_t msg_sz = target_cache_size-0x30;
    struct msgbuf msgbuf;
    strcpy(msgbuf.message,"hello world!");
    
    puts("before creating messages");
    getchar();

    int msqid;
    int key = ftok(".",0);
    msqid = msgget(key,0666| IPC_CREAT);
    for(int i=0;i<20;i++){
        msgbuf.mtype = i+1;//注意mtype不能为0,0会出问题。
        msgsnd(msqid,&msgbuf,msg_sz,0);
    } 
    puts("after creating messages");
    getchar();
}
//msg_recv.c 用于接收msg
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <stdio.h>

int main(){
    // prepare message
    size_t target_cache_size = 128;
    struct msgbuf{
        long mtype;
        char message[100];
    };
    size_t msg_sz = target_cache_size-0x30;
    struct msgbuf msgbuf;
    strcpy(msgbuf.message,"hello world!");
    
    puts("before creating messages");
    getchar();

    int msqid;
    int key = ftok(".",0);
    msqid = msgget(key,0666);
    for(int i=0;i<20;i++){
        msgrcv(msqid,&msgbuf,msg_sz,0,0);
        printf("received with type: %d, content: %s\n",msgbuf.mtype,msgbuf.message);
    } 
    puts("after creating messages");
    getchar();
}

1.2 为什么要介绍msg?

还记得上一节https://wsxk.github.io/kernel_heap2/中提到的理想的堆布局构造对象吗?
首先看内核中msg_msg对象的定义:

/* one msg_msg structure for each message */
struct msg_msg {
	struct list_head m_list;
	long m_type;
	size_t m_ts;		/* message text size */
	struct msg_msgseg *next;   //****完美满足第3点!ptr可覆盖!****
	void *security;
	/* the actual message follows immediately */
};

//m_list contains pointers to messages in the message queue 
//m_ts determines this size of the message text

我们接下来看一下msg_msg是如何分配的:

static struct msg_msg *alloc_msg(size_t len)
{
	struct msg_msg *msg;
	struct msg_msgseg **pseg;
	size_t alen;

	alen = min(len, DATALEN_MSG);
	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);//0x28+8(mtype)+实际消息长度
  //****完美满足第一点!size可控!!!****
}

在调用msgrcv函数时,发生如下事件:

struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) {
	struct msg_msgseg *dst_pseg, *src_pseg;
	size_t len = src->m_ts;
	size_t alen;
	if (src->m_ts > dst->m_ts)
		return ERR_PTR(-EINVAL);
	alen = min(len, DATALEN_MSG);
	memcpy(dst + 1, src + 1, alen);//****完美满足第二点!绕过harden_usercopy!!!****

}
//copy_msg can be triggered via  msgrcv(msgqid, msgp, msgsz, 0, MSG_COPY);

2. pipe_buffer

2.1 pipe_buffer 常见用法

pipe_buffer也是内核IPC通信的方法之一,通过管道pipe来进行通信;

2.2 为什么要介绍pipe_buffer?

pipe_buffer的定义如下:

struct pipe_buffer {
	struct page *page;
	unsigned int offset, len;
	const struct pipe_buf_operations *ops;//包含函数指针,越界写即可完成控制流劫持!
	unsigned int flags;
	unsigned long private;
};

3. 具体案例

todo,等到复现linux cve的时候应该就用上了