2022 ciscn实践赛西南赛区半决赛只有两道pwn,一道简单vm栈溢出,还有一道1解kernel。
因为题目没有泄露函数,所以我依赖msg_msg
构造越界读&任意写的原语,同时借助pipe_buffer
完成内核地址泄露。
笔者对msg
源码进行了浅要的剖析,有基础 or 对源码不感兴趣 的读者可自行选择跳过。
消息队列是Linux的一种通信机制,这种通信机制传递的数据具有某种结构,而不是简单的字节流。消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了一个数据结构。
可以通过消息队列实现进程间通信等。
/include/linux/msg.h
中有关于msg_msg
结构体的定义:
其中list_head
为双向链表结构体,储存next
和prev
指针:
在ipc/msgutil.c
中有对msg_msgseg
的定义,还有申请msg_msg
结构体的函数:
可以看到msg_msgseg
就是一个嵌套的结构体指针。
其中参数含义:
调用msgget函数会创建新的消息队列,或者获取已有的消息队列,若创建新的消息队列,会创建一个msg_queue
结构体当消息队列msg_msg
双向循环链表的起始节点。
需要注意的是后续若某进程调用msgsnd
函数对消息队列进行写操作,需要该进程有写权限;同理msgrcv
需要有读权限。这是由msgget
函数中的第二个参数中的权限控制符所决定的。
引自Roland
师傅的图:(概括性的,若读者不想看我对源码的分析可以直接参考这个表)
调用 msgsnd 系统调用在指定消息队列上发送一条指定大小的 message 时,会建立msg_msg
结构体。
查看实现msgsnd系统调用的do_msgsnd
函数部分源码:
查看load_msg
函数:
调用了alloc_msg
函数分配空间,同时将用户数据拷贝到内核msg_msg
队列中。
再查看alloc_msg
函数:
从该函数源码我们可以知道:
通俗点来说,msg_msg
和msg_msgseg
结构体最大size均不能超过page_size
:
最后单个msg_msg
消息会形成如下的单向链表结构:
而msg_msg
之间则是用list_head
来链接,形成的是以msg_queue
为首节点的双向循环链表结构,大致如下:
申请msg_msg
的调用链:
同样引自Roland
师傅的图:(进行了一点小更正)
msgrcv
系统调用能从消息队列上接受指定大小的消息,并且选择性(是否)释放msg_msg
结构体。
具体实现源码在/ipc/msg.c
的do_msgrcv
中。
该函数中源码中使用了内核源码中常见的一个宏定义:list_for_each_entry
。该宏定义可以理解为一个for
循环。
它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head。
该循环遍历了msg_queue
为首节点的双向循环链表,也就是遍历了所有msg_msg
队列的头节点。
然后调用testmsg
,根据mode
和传入的msgtyp
来筛选:
其中mode
由convert_mode
决定:
综合起来,可以看到用户是通过控制msgtyp
来控制do_msg_rcv
拷贝/取得 哪条队列信息:
特例:MSG_COPY
位为1的时候,mode
为SEARCH-NUMBER
,在find_msg
中会返回msg_msg
双向循环链表中,第msgtyp
个msg_msg
,也就是返回第msgtyp
条消息,而不是上述表格中根据msgtyp
去和msg->m_type
进行匹配。
在/ipc/msg.c
中do_msgrcv
:
内核首先会调用 list_del()
将其从 msg_queue
的双向链表上 unlink,之后再调用 free_msg()
释放 msg_msg
单向链表上的所有消息。
在do_msg_rcv
函数最后,调用了msg_handler
,看参数像是进行内核-->用户的数据拷贝。
其中msg_handler
是do_msgrcv
传进来的参数,是一个函数指针,向上看调用do_msgrcv
的调用链:
可知msg_handler
具体函数指针为do_msg_fill
:
其中调用store_msg
进行数据拷贝:
可以看到拷贝过程和之前msg_msg
结构内存申请相对应:
ps:拷贝结束的标志均为seg->next
指针为NULL
拷贝的总长度则由msgsz
决定,而msgsz
:
可以看到若bufsz
足够的情况下,拷贝数据总长度是由msg->m_ts
决定的。
需要注意的是,若我们带有MSG_COPY
标志,则不会在双向链表上unlink,只会进行copy操作 ,具体实现在do_msgrcv
中部分源码:
若有MSG_COPY
标志,源码注释:If we are copying, then do not unlink message and do not update queue parameters.
不会调用list_del()
去进行unlink
,并且最后free_msg()
释放的是我们在内核中copy出来的堆块 。也就是说,我们可以通过设置MSG_COPY
多次读取一条消息。
在通过find_msg
定位一节已经讲过:
MSG_COPY
位为1的时候,mode
为SEARCH-NUMBER
,在find_msg
中会返回msg_msg
双向循环链表中,第msgtyp
个msg_msg
,也就是返回第msgtyp
条消息,而不是上述表格中根据msgtyp
去和msg->m_type
进行匹配。
由于MSG_COPY
位为1的时候,内核会调用prepare_copy
再申请一块内存出来。
申请内存大小为我们传入do_msgrcv
的bufsz
。
两个msg_msg
之间的拷贝则由copy_msg
负责,而在copy_msg
函数中有一段代码如下:
若源src->m_ts
大于目标dst->m_ts
,则会发生溢出,因此会直接返回不会拷贝。
同时copy_msg
函数末尾还有赋值操作:
因此我们要满足的条件是src->m_ts
<=dst->m_ts
即可。
即bufsz
>=src->m_ts
。
经过对源码的阅读和分析,可以想到我们可以做到的事情:
(1)改掉msg_msg->m_ts
。
可以读取最多一页的内存,实现越界读。
(2)改掉msg_msg->m_ts
和msg_msg->m_list
中的next
指针。
可以利用堆喷其他结构体+msg_msg
越界读,获得一些堆地址 or 内核地址。
可以堆喷一些一些消息队列,每个消息队列上只有一条消息:即msg->queue
双向循环链表里只有一个节点:
可以通过某个msg_msg
的越界读,有几率读到其他消息队列的msg_msg
的m_list
字段,而我们构造每条消息队列上只有一条消息
,泄露其m_list
,即为msg_queue
的地址。泄露完之后,继续伪造msg_msg->next
字段可泄露整个该消息队列中每个结构体的地址。
可以实现任意地址读。
但是需要注意的是,我们需要伪造我们需要读的地址target的next
指针为NULL,不然在store_msg
进行数据拷贝的时候,是以NULL指针为结束判断条件,因此我们需要满足target->next==NULL
or target->next->next==NULL
,反正需要我们伪造的任意读链表存在一个NULL节点
,且中途不能到达不可读地址,否则会造成kernel panic。
在do_msgsnd
函数中调用了load_msg
进行用户到内核的数据拷贝,若我们利用userfault
机制暂停一个线程,再在另一个线程中篡改掉msg->next
指针,则可以实现任意地址写。
模板采用的arttnba3
师傅的模板:
pipe是Linux系统跨进程通信的一种方式。管道是连接一个读进程和一个写进程,以实现它们之间通信的共享文件。基于pipe
族系统调用实现(而非open()
)。而这个文件不是真正的文件,向管道文件读写数据其实是在读写内核缓冲区 。
pipe() 创建一个管道,一个可用于进程间通信的单向数据通道。 数组 pipefd 用于返回两个指向管道末端的文件描述符。 pipefd[0] 是管道的读端fd。 pipefd[1] 是管道的写端fd。 写端把数据写入管道,直到读端读取数据。
管道不需要open
,但需要close
释放。
定义在/include/linux/pipe_fs_i.h
中:
size为0x30。
在建立管道时,内核首先会申请一个pipe_inode_info
结构体,然后在其pipe_inode_info->buf
字段申请pipe_buffer
结构体:
其中PIPE_DEF_BUFFERS
=16;因此会申请0x10*0x30(size of pipe_buffer)的内存,也就是会从kmalloc-1k中取。
主要关注其release
指针,在我们关闭一个管道的两端 之后,管道会被释放,同样pipe_buffer
也会被释放。调用的是函数表中的release
指针。调用路径为:free_pipe_info->pipe_buf_release
pipe_buffer
中的*pipe_buf_operations
成员能泄露内核基地址。
PS:需要注意的是,利用pipe
系统调用后需要调用一次写管道才能对函数表进行初始化:
覆写pipe_buffer->pipe_buf_operations->release
为某些栈迁移指针。将rsi
-->rsp
。
题目开启了KPTI
,SMAP
,SMEP
等正常保护。
内核版本:
从5.11
内核版本开始,就禁止非特权用户使用userfaultfd
了。所以这道题是userfaultfd
版本最后的荣光 (bushi
在kernel_release
函数中存在指针未清零的情况:
但是kernel_open
函数判断了flag
字段:
因此我们无法调用两次kernel_open
后利用kernel_release
的指针悬挂,来造成0x100 size的一个object的UAF。
但是由于kernel_read
和kernel_write
,kernel_open
与kernel_release
均未加锁,且read
与write
中含有类似于如下的copy_to_user
操作:
因此我们可以考虑使用userfaultfd
卡住当前进程,在另外一个线程中调用kernel_release
。这样同样可以达到一个0x100 size的UAF。
所以笔者是考虑用这个简单的洞来泄露内核基址,用的是0x100 size对应的timerfd_ctx
结构体。
不过笔者用这种方法无论如何都无法泄露出内核基址,后来咨询arttnba3
师傅后得知:
内核调用fput对文件描述符进行释放,对于文件描述符的关闭会被delay,直到我们读取数据后才会关闭 。
因此靠这种操作leak内核数据是不可行的。
程序除了module
自身的open
,release
,read
,write
操作。
提供了简单的菜单堆功能:
所有功能都未加锁,因此可以用userfaultfd
在edit时将进程卡死,在另一个线程中free掉这个堆块后申请某些object到该地址上,实现0x400 size object的UAF(只能更改一次值)。
申请一个消息队列,上面只放一条消息,且size为0x400。同时申请一个pipe_buffer
。
改大msg_msg->m_ts
,用带有MSG_COPY
位的msgrcv
进行越界读,泄露出pipe_buffer
上的函数表。
得到内核基址 。
leak内核地址分别消耗了一次add,delete,edit操作。因此我们还有一次UAF的机会。
笔者最先考虑的是用pipe_buffer
提权,刚好满足0x400的size,因此我们利用UAF将pipe_buffer->pipe_buf_operations->release
函数指针更改为某个栈迁移gadget即可。
但是我并没有找到可利用的gadget,其中有一条可能能达成的:
不过retf
是按32位pop eip
和cs
,的,而32位根本不足以储存一个内核地址。
同时还有例如mov esp,esi
类型的gadget
,由于intel x86&x64
的调用约定,当对32位寄存器进行赋值操作的时候,会将高32位寄存器值清零,因此也不可用。
同时注意到程序没有开启CONFIG_STATIC_USERMODEHELPER
保护,因此笔者选择用UAF
劫持0x400 size的freelist
到modprobe_path
附近,更改modprobe_path
。
笔者是申请msg_msg
结构体申请到modprobe_path
附近,由于size太大,会将modprobe_path
附近的所有内容全部清空。
直接进行接下来的提权or 读取flag操作会在成功前引起kernel panic
,因此我们需要恢复modprobe_path
附近的函数指针。
其中,kmod
的函数指针恢复是必要的:
因为modprobe_path
是一个Linux程序,最初由Rusty Russell编写,用于在Linux内核中添加一个可加载的内核模块,或者从内核中移除一个可加载的内核模块,因此modprobe
是安装某个内核模块,而kmod
是一个用于控制linux内核模块的程序,因此在后续调用中需要用到
最后直接利用modprobe_path_hijack
更改flag权限后读取即可。
这里由于gcc
编译的poc
文件过大,远程超时,因此我选择musl-gcc
进行编译。
但是奇怪的是,按理来说两种编译方式不会对poc
造成影响,gcc的可以正常运行,而musl-gcc
在modprobe_path_hijack
后,第一次调用system
时,内核会panic在slub里。
估计是system系统调用execve
申请内存时,寄在了某一个没有修复好的freelist
里,但是我的这种解法,应该是无法修复freelist
的。
后续选择uclibc
进行编译就成功了。如果有读者了解为什么musl-gcc
编译出来会有这种情况,请务必教教我。
struct msg_msg {
struct list_head m_list;
long
m_type;
size_t m_ts;
/
*
message text size
*
/
struct msg_msgseg
*
next
;
void
*
security;
/
*
the actual message follows immediately
*
/
};
struct msg_msg {
struct list_head m_list;
long
m_type;
size_t m_ts;
/
*
message text size
*
/
struct msg_msgseg
*
next
;
void
*
security;
/
*
the actual message follows immediately
*
/
};
struct list_head {
struct list_head
*
next
,
*
prev;
};
struct list_head {
struct list_head
*
next
,
*
prev;
};
struct msg_msgseg {
struct msg_msgseg
*
next
;
/
*
the
next
part of the message follows immediately
*
/
};
struct msg_msgseg {
struct msg_msgseg
*
next
;
/
*
the
next
part of the message follows immediately
*
/
};
int
msgget(key_t key,
int
msgflag)
int
msgget(key_t key,
int
msgflag)
参数
参数意义
key
key的值为函数ftok的返回值或IPC_PRIVATE
,若为IPC_PRIVATE
则直接创建新的消息队列
msgflag
IPC_CREAT
:创建新的消息队列。 IPC_EXCL
:与IPC_CREAT
一同使用,表示如果要创建的消息队列已经存在,则返回错误。(IPC_EXCL
没有什么实质性的意义,但是可以帮我们确定是新建了消息队列而不是返回已经存在的消息队列) IPC_NOWAIT
:读写消息队列要求无法满足时,不阻塞。返回值: 调用成功返回队列标识符,否则返回-1. 其中该参数需要配合权限控制符,例如0666 OR IPC_CREAT
int
msgsnd(
int
msqid, const void
*
msgp, size_t msgsz,
int
msgflg)
int
msgsnd(
int
msqid, const void
*
msgp, size_t msgsz,
int
msgflg)
static
long
do_msgsnd(
int
msqid,
long
mtype, void __user
*
mtext,
size_t msgsz,
int
msgflg)
{
struct msg_queue
*
msq;
struct msg_msg
*
msg;
int
err;
struct ipc_namespace
*
ns;
DEFINE_WAKE_Q(wake_q);
ns
=
current
-
>nsproxy
-
>ipc_ns;
if
(msgsz > ns
-
>msg_ctlmax || (
long
) msgsz <
0
|| msqid <
0
)
return
-
EINVAL;
if
(mtype <
1
)
return
-
EINVAL;
msg
=
load_msg(mtext, msgsz);
...........
static
long
do_msgsnd(
int
msqid,
long
mtype, void __user
*
mtext,
size_t msgsz,
int
msgflg)
{
struct msg_queue
*
msq;
struct msg_msg
*
msg;
int
err;
struct ipc_namespace
*
ns;
DEFINE_WAKE_Q(wake_q);
ns
=
current
-
>nsproxy
-
>ipc_ns;
if
(msgsz > ns
-
>msg_ctlmax || (
long
) msgsz <
0
|| msqid <
0
)
return
-
EINVAL;
if
(mtype <
1
)
return
-
EINVAL;
msg
=
load_msg(mtext, msgsz);
...........
struct msg_msg
*
load_msg(const void __user
*
src, size_t
len
)
{
struct msg_msg
*
msg;
struct msg_msgseg
*
seg;
int
err
=
-
EFAULT;
size_t alen;
msg
=
alloc_msg(
len
);
if
(msg
=
=
NULL)
return
ERR_PTR(
-
ENOMEM);
alen
=
min
(
len
, DATALEN_MSG);
if
(copy_from_user(msg
+
1
, src, alen))
goto out_err;
for
(seg
=
msg
-
>
next
; seg !
=
NULL; seg
=
seg
-
>
next
) {
len
-
=
alen;
src
=
(char __user
*
)src
+
alen;
alen
=
min
(
len
, DATALEN_SEG);
if
(copy_from_user(seg
+
1
, src, alen))
goto out_err;
}
err
=
security_msg_msg_alloc(msg);
if
(err)
goto out_err;
return
msg;
out_err:
free_msg(msg);
return
ERR_PTR(err);
}
struct msg_msg
*
load_msg(const void __user
*
src, size_t
len
)
{
struct msg_msg
*
msg;
struct msg_msgseg
*
seg;
int
err
=
-
EFAULT;
size_t alen;
msg
=
alloc_msg(
len
);
if
(msg
=
=
NULL)
return
ERR_PTR(
-
ENOMEM);
alen
=
min
(
len
, DATALEN_MSG);
if
(copy_from_user(msg
+
1
, src, alen))
goto out_err;
for
(seg
=
msg
-
>
next
; seg !
=
NULL; seg
=
seg
-
>
next
) {
len
-
=
alen;
src
=
(char __user
*
)src
+
alen;
alen
=
min
(
len
, DATALEN_SEG);
if
(copy_from_user(seg
+
1
, src, alen))
goto out_err;
}
err
=
security_msg_msg_alloc(msg);
if
(err)
goto out_err;
return
msg;
out_err:
free_msg(msg);
return
ERR_PTR(err);
}
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);
if
(msg
=
=
NULL)
return
NULL;
msg
-
>
next
=
NULL;
msg
-
>security
=
NULL;
len
-
=
alen;
pseg
=
&msg
-
>
next
;
while
(
len
>
0
) {
struct msg_msgseg
*
seg;
cond_resched();
alen
=
min
(
len
, DATALEN_SEG);
seg
=
kmalloc(sizeof(
*
seg)
+
alen, GFP_KERNEL_ACCOUNT);
if
(seg
=
=
NULL)
goto out_err;
*
pseg
=
seg;
seg
-
>
next
=
NULL;
pseg
=
&seg
-
>
next
;
len
-
=
alen;
}
return
msg;
out_err:
free_msg(msg);
return
NULL;
}
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);
if
(msg
=
=
NULL)
return
NULL;
msg
-
>
next
=
NULL;
msg
-
>security
=
NULL;
len
-
=
alen;
pseg
=
&msg
-
>
next
;
while
(
len
>
0
) {
struct msg_msgseg
*
seg;
cond_resched();
alen
=
min
(
len
, DATALEN_SEG);
seg
=
kmalloc(sizeof(
*
seg)
+
alen, GFP_KERNEL_ACCOUNT);
if
(seg
=
=
NULL)
goto out_err;
*
pseg
=
seg;
seg
-
>
next
=
NULL;
pseg
=
&seg
-
>
next
;
len
-
=
alen;
}
return
msg;
out_err:
free_msg(msg);
return
NULL;
}
do_msgsnd
-
-
>load_msg
-
-
>alloc_msg
do_msgsnd
-
-
>load_msg
-
-
>alloc_msg
ssize_t msgrcv(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp,
int
msgflg)
ssize_t msgrcv(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp,
int
msgflg)
static struct msg_msg
*
find_msg(struct msg_queue
*
msq,
long
*
msgtyp,
int
mode)
{
struct msg_msg
*
msg,
*
found
=
NULL;
long
count
=
0
;
list_for_each_entry(msg, &msq
-
>q_messages, m_list)
{
if
(testmsg(msg,
*
msgtyp, mode) &&
!security_msg_queue_msgrcv(&msq
-
>q_perm, msg, current,
*
msgtyp, mode))
{
if
(mode
=
=
SEARCH_LESSEQUAL && msg
-
>m_type !
=
1
)
{
*
msgtyp
=
msg
-
>m_type
-
1
;
found
=
msg;
}
else
if
(mode
=
=
SEARCH_NUMBER)
{
if
(
*
msgtyp
=
=
count)
return
msg;
}
else
return
msg;
count
+
+
;
}
}
return
found ?: ERR_PTR(
-
EAGAIN);
}
static struct msg_msg
*
find_msg(struct msg_queue
*
msq,
long
*
msgtyp,
int
mode)
{
struct msg_msg
*
msg,
*
found
=
NULL;
long
count
=
0
;
list_for_each_entry(msg, &msq
-
>q_messages, m_list)
{
if
(testmsg(msg,
*
msgtyp, mode) &&
!security_msg_queue_msgrcv(&msq
-
>q_perm, msg, current,
*
msgtyp, mode))
{
if
(mode
=
=
SEARCH_LESSEQUAL && msg
-
>m_type !
=
1
)
{
*
msgtyp
=
msg
-
>m_type
-
1
;
found
=
msg;
}
else
if
(mode
=
=
SEARCH_NUMBER)
{
if
(
*
msgtyp
=
=
count)
return
msg;
}
else
return
msg;
count
+
+
;
}
}
return
found ?: ERR_PTR(
-
EAGAIN);
}
static
int
testmsg(struct msg_msg
*
msg,
long
type
,
int
mode)
{
switch (mode) {
case SEARCH_ANY:
case SEARCH_NUMBER:
return
1
;
case SEARCH_LESSEQUAL:
if
(msg
-
>m_type <
=
type
)
return
1
;
break
;
case SEARCH_EQUAL:
if
(msg
-
>m_type
=
=
type
)
return
1
;
break
;
case SEARCH_NOTEQUAL:
if
(msg
-
>m_type !
=
type
)
return
1
;
break
;
}
return
0
;
}
static
int
testmsg(struct msg_msg
*
msg,
long
type
,
int
mode)
{
switch (mode) {
case SEARCH_ANY:
case SEARCH_NUMBER:
return
1
;
case SEARCH_LESSEQUAL:
if
(msg
-
>m_type <
=
type
)
return
1
;
break
;
case SEARCH_EQUAL:
if
(msg
-
>m_type
=
=
type
)
return
1
;
break
;
case SEARCH_NOTEQUAL:
if
(msg
-
>m_type !
=
type
)
return
1
;
break
;
}
return
0
;
}
static inline
int
convert_mode(
long
*
msgtyp,
int
msgflg)
{
if
(msgflg & MSG_COPY)
return
SEARCH_NUMBER;
/
*
*
find message of correct
type
.
*
msgtyp
=
0
=
> get first.
*
msgtyp >
0
=
> get first message of matching
type
.
*
msgtyp <
0
=
> get message with least
type
must be <
abs
(msgtype).
*
/
if
(
*
msgtyp
=
=
0
)
return
SEARCH_ANY;
if
(
*
msgtyp <
0
) {
if
(
*
msgtyp
=
=
LONG_MIN)
/
*
-
LONG_MIN
is
undefined
*
/
*
msgtyp
=
LONG_MAX;
else
*
msgtyp
=
-
*
msgtyp;
return
SEARCH_LESSEQUAL;
}
if
(msgflg & MSG_EXCEPT)
return
SEARCH_NOTEQUAL;
return
SEARCH_EQUAL;
}
static inline
int
convert_mode(
long
*
msgtyp,
int
msgflg)
{
if
(msgflg & MSG_COPY)
return
SEARCH_NUMBER;
/
*
*
find message of correct
type
.
*
msgtyp
=
0
=
> get first.
*
msgtyp >
0
=
> get first message of matching
type
.
*
msgtyp <
0
=
> get message with least
type
must be <
abs
(msgtype).
*
/
if
(
*
msgtyp
=
=
0
)
return
SEARCH_ANY;
if
(
*
msgtyp <
0
) {
if
(
*
msgtyp
=
=
LONG_MIN)
/
*
-
LONG_MIN
is
undefined
*
/
*
msgtyp
=
LONG_MAX;
else
*
msgtyp
=
-
*
msgtyp;
return
SEARCH_LESSEQUAL;
}
if
(msgflg & MSG_EXCEPT)
return
SEARCH_NOTEQUAL;
return
SEARCH_EQUAL;
}
msgtyp
mode
效果
<0
SEARCH_LESSEQUAL
找到一个msg->m_type
小于msgtyp
且msg->m_type
最小的 msg_msg
=0
SEARCH_ANY
找到msg_msg
队列中第一个 msg_msg
>0
SEARCH_EQUAL\
\
SEARCH_NOTEQUAL
找到第一个 msg->m_type
等于/不等于msgtyp
的msg_msg
static
long
do_msgrcv(
int
msqid, void __user
*
buf, size_t bufsz,
long
msgtyp,
int
msgflg,
long
(
*
msg_handler)(void __user
*
, struct msg_msg
*
, size_t))
{
int
mode;
struct msg_queue
*
msq;
struct ipc_namespace
*
ns;
struct msg_msg
*
msg,
*
copy
=
NULL;
...........
...........
list_del(&msg
-
>m_list);
...........
...........
free_msg(msg);
return
bufsz;
static
long
do_msgrcv(
int
msqid, void __user
*
buf, size_t bufsz,
long
msgtyp,
int
msgflg,
long
(
*
msg_handler)(void __user
*
, struct msg_msg
*
, size_t))
{
int
mode;
struct msg_queue
*
msq;
struct ipc_namespace
*
ns;
struct msg_msg
*
msg,
*
copy
=
NULL;
...........
...........
list_del(&msg
-
>m_list);
...........
...........
free_msg(msg);
return
bufsz;
bufsz
=
msg_handler(buf, msg, bufsz);
bufsz
=
msg_handler(buf, msg, bufsz);
long
ksys_msgrcv(
int
msqid, struct msgbuf __user
*
msgp, size_t msgsz,
long
msgtyp,
int
msgflg)
{
return
do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
}
long
ksys_msgrcv(
int
msqid, struct msgbuf __user
*
msgp, size_t msgsz,
long
msgtyp,
int
msgflg)
{
return
do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
}
static
long
do_msg_fill(void __user
*
dest, struct msg_msg
*
msg, size_t bufsz)
{
struct msgbuf __user
*
msgp
=
dest;
size_t msgsz;
if
(put_user(msg
-
>m_type, &msgp
-
>mtype))
return
-
EFAULT;
msgsz
=
(bufsz > msg
-
>m_ts) ? msg
-
>m_ts : bufsz;
if
(store_msg(msgp
-
>mtext, msg, msgsz))
return
-
EFAULT;
return
msgsz;
}
static
long
do_msg_fill(void __user
*
dest, struct msg_msg
*
msg, size_t bufsz)
{
struct msgbuf __user
*
msgp
=
dest;
size_t msgsz;
if
(put_user(msg
-
>m_type, &msgp
-
>mtype))
return
-
EFAULT;
msgsz
=
(bufsz > msg
-
>m_ts) ? msg
-
>m_ts : bufsz;
if
(store_msg(msgp
-
>mtext, msg, msgsz))
return
-
EFAULT;
return
msgsz;
}
int
store_msg(void __user
*
dest, struct msg_msg
*
msg, size_t
len
)
{
size_t alen;
struct msg_msgseg
*
seg;
alen
=
min
(
len
, DATALEN_MSG);
if
(copy_to_user(dest, msg
+
1
, alen))
return
-
1
;
for
(seg
=
msg
-
>
next
; seg !
=
NULL; seg
=
seg
-
>
next
) {
len
-
=
alen;
dest
=
(char __user
*
)dest
+
alen;
alen
=
min
(
len
, DATALEN_SEG);
if
(copy_to_user(dest, seg
+
1
, alen))
return
-
1
;
}
return
0
;
}
int
store_msg(void __user
*
dest, struct msg_msg
*
msg, size_t
len
)
{
size_t alen;
struct msg_msgseg
*
seg;
alen
=
min
(
len
, DATALEN_MSG);
if
(copy_to_user(dest, msg
+
1
, alen))
return
-
1
;
for
(seg
=
msg
-
>
next
; seg !
=
NULL; seg
=
seg
-
>
next
) {
len
-
=
alen;
dest
=
(char __user
*
)dest
+
alen;
alen
=
min
(
len
, DATALEN_SEG);
if
(copy_to_user(dest, seg
+
1
, alen))
return
-
1
;
}
return
0
;
}
msgsz
=
(bufsz > msg
-
>m_ts) ? msg
-
>m_ts : bufsz;
msgsz
=
(bufsz > msg
-
>m_ts) ? msg
-
>m_ts : bufsz;
if
(!IS_ERR(msg)) {
/
*
*
Found a suitable message.
*
Unlink it
from
the queue.
*
/
if
((bufsz < msg
-
>m_ts) && !(msgflg & MSG_NOERROR)) {
msg
=
ERR_PTR(
-
E2BIG);
goto out_unlock0;
}
/
*
*
If we are copying, then do
not
unlink message
and
do
*
not
update queue parameters.
*
/
if
(msgflg & MSG_COPY) {
msg
=
copy_msg(msg, copy);
goto out_unlock0;
}
list_del(&msg
-
>m_list);
msq
-
>q_qnum
-
-
;
msq
-
>q_rtime
=
ktime_get_real_seconds();
ipc_update_pid(&msq
-
>q_lrpid, task_tgid(current));
msq
-
>q_cbytes
-
=
msg
-
>m_ts;
atomic_sub(msg
-
>m_ts, &ns
-
>msg_bytes);
atomic_dec(&ns
-
>msg_hdrs);
ss_wakeup(msq, &wake_q, false);
goto out_unlock0;
}
if
(!IS_ERR(msg)) {
/
*
*
Found a suitable message.
*
Unlink it
from
the queue.
*
/
if
((bufsz < msg
-
>m_ts) && !(msgflg & MSG_NOERROR)) {
msg
=
ERR_PTR(
-
E2BIG);
goto out_unlock0;
}
/
*
*
If we are copying, then do
not
unlink message
and
do
*
not
update queue parameters.
*
/
if
(msgflg & MSG_COPY) {
msg
=
copy_msg(msg, copy);
goto out_unlock0;
}
list_del(&msg
-
>m_list);
msq
-
>q_qnum
-
-
;
msq
-
>q_rtime
=
ktime_get_real_seconds();
ipc_update_pid(&msq
-
>q_lrpid, task_tgid(current));
msq
-
>q_cbytes
-
=
msg
-
>m_ts;
atomic_sub(msg
-
>m_ts, &ns
-
>msg_bytes);
atomic_dec(&ns
-
>msg_hdrs);
ss_wakeup(msq, &wake_q, false);
goto out_unlock0;
}
static inline struct msg_msg
*
prepare_copy(void __user
*
buf, size_t bufsz)
{
struct msg_msg
*
copy;
/
*
*
Create dummy message to copy real message to.
*
/
copy
=
load_msg(buf, bufsz);
if
(!IS_ERR(copy))
copy
-
>m_ts
=
bufsz;
return
copy;
static inline struct msg_msg
*
prepare_copy(void __user
*
buf, size_t bufsz)
{
struct msg_msg
*
copy;
/
*
*
Create dummy message to copy real message to.
*
/
copy
=
load_msg(buf, bufsz);
if
(!IS_ERR(copy))
copy
-
>m_ts
=
bufsz;
return
copy;
if
(src
-
>m_ts > dst
-
>m_ts)
return
ERR_PTR(
-
EINVAL);
if
(src
-
>m_ts > dst
-
>m_ts)
return
ERR_PTR(
-
EINVAL);
dst
-
>m_ts
=
src
-
>m_ts
struct list_head {
uint64_t
next
;
uint64_t prev;
};
struct msg_msg {
struct list_head m_list;
uint64_t m_type;
uint64_t m_ts;
uint64_t
next
;
uint64_t security;
};
struct msg_msgseg {
uint64_t
next
;
};
struct msgbuf {
long
mtype;
char mtext[
0
];
};
int
getMsgQueue(void)
{
return
msgget(IPC_PRIVATE,
0666
| IPC_CREAT);
}
int
readMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
return
msgrcv(msqid, msgp, msgsz, msgtyp,
0
);
}
/
*
*
*
the msgp should be a pointer to the `struct msgbuf`,
*
and
the data should be stored
in
msgbuf.mtext
*
/
int
writeMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
((struct msgbuf
*
)msgp)
-
>mtype
=
msgtyp;
return
msgsnd(msqid, msgp, msgsz,
0
);
}
/
*
for
MSG_COPY, `msgtyp` means to read no.msgtyp msg_msg on the queue
*
/
int
peekMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
return
msgrcv(msqid, msgp, msgsz, msgtyp,
MSG_COPY | IPC_NOWAIT | MSG_NOERROR);
}
void buildMsg(struct msg_msg
*
msg, uint64_t m_list_next, uint64_t m_list_prev,
uint64_t m_type, uint64_t m_ts, uint64_t
next
, uint64_t security)
{
msg
-
>m_list.
next
=
m_list_next;
msg
-
>m_list.prev
=
m_list_prev;
msg
-
>m_type
=
m_type;
msg
-
>m_ts
=
m_ts;
msg
-
>
next
=
next
;
msg
-
>security
=
security;
}
struct list_head {
uint64_t
next
;
uint64_t prev;
};
struct msg_msg {
struct list_head m_list;
uint64_t m_type;
uint64_t m_ts;
uint64_t
next
;
uint64_t security;
};
struct msg_msgseg {
uint64_t
next
;
};
struct msgbuf {
long
mtype;
char mtext[
0
];
};
int
getMsgQueue(void)
{
return
msgget(IPC_PRIVATE,
0666
| IPC_CREAT);
}
int
readMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
return
msgrcv(msqid, msgp, msgsz, msgtyp,
0
);
}
/
*
*
*
the msgp should be a pointer to the `struct msgbuf`,
*
and
the data should be stored
in
msgbuf.mtext
*
/
int
writeMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
((struct msgbuf
*
)msgp)
-
>mtype
=
msgtyp;
return
msgsnd(msqid, msgp, msgsz,
0
);
}
/
*
for
MSG_COPY, `msgtyp` means to read no.msgtyp msg_msg on the queue
*
/
int
peekMsg(
int
msqid, void
*
msgp, size_t msgsz,
long
msgtyp)
{
return
msgrcv(msqid, msgp, msgsz, msgtyp,
MSG_COPY | IPC_NOWAIT | MSG_NOERROR);
}
void buildMsg(struct msg_msg
*
msg, uint64_t m_list_next, uint64_t m_list_prev,
uint64_t m_type, uint64_t m_ts, uint64_t
next
, uint64_t security)
{
msg
-
>m_list.
next
=
m_list_next;
msg
-
>m_list.prev
=
m_list_prev;
msg
-
>m_type
=
m_type;
msg
-
>m_ts
=
m_ts;
msg
-
>
next
=
next
;
msg
-
>security
=
security;
}
int
pipe(
int
pipefd[
2
]);
struct pipe_buffer {
struct page
*
page;
unsigned
int
offset,
len
;
const struct pipe_buf_operations
*
ops;
unsigned
int
flags;
unsigned
long
private;
};
struct pipe_buffer {
struct page
*
page;
unsigned
int
offset,
len
;
const struct pipe_buf_operations
*
ops;
unsigned
int
flags;
unsigned
long
private;
};
struct pipe_inode_info
*
alloc_pipe_info(void)
{
struct pipe_inode_info
*
pipe;
unsigned
long
pipe_bufs
=
PIPE_DEF_BUFFERS;
struct user_struct
*
user
=
get_current_user();
unsigned
long
user_bufs;
unsigned
int
max_size
=
READ_ONCE(pipe_max_size);
pipe
=
kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_ACCOUNT);
if
(pipe
=
=
NULL)
goto out_free_uid;
if
(pipe_bufs
*
PAGE_SIZE > max_size && !capable(CAP_SYS_RESOURCE))
pipe_bufs
=
max_size >> PAGE_SHIFT;
user_bufs
=
account_pipe_buffers(user,
0
, pipe_bufs);
if
(too_many_pipe_buffers_soft(user_bufs) && pipe_is_unprivileged_user()) {
user_bufs
=
account_pipe_buffers(user, pipe_bufs, PIPE_MIN_DEF_BUFFERS);
pipe_bufs
=
PIPE_MIN_DEF_BUFFERS;
}
if
(too_many_pipe_buffers_hard(user_bufs) && pipe_is_unprivileged_user())
goto out_revert_acct;
pipe
-
>bufs
=
kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
GFP_KERNEL_ACCOUNT);
if
(pipe
-
>bufs) {
init_waitqueue_head(&pipe
-
>rd_wait);
init_waitqueue_head(&pipe
-
>wr_wait);
pipe
-
>r_counter
=
pipe
-
>w_counter
=
1
;
pipe
-
>max_usage
=
pipe_bufs;
pipe
-
>ring_size
=
pipe_bufs;
pipe
-
>nr_accounted
=
pipe_bufs;
pipe
-
>user
=
user;
mutex_init(&pipe
-
>mutex);
return
pipe;
}
out_revert_acct:
(void) account_pipe_buffers(user, pipe_bufs,
0
);
kfree(pipe);
out_free_uid:
free_uid(user);
return
NULL;
}
struct pipe_inode_info
*
alloc_pipe_info(void)
{
struct pipe_inode_info
*
pipe;
unsigned
long
pipe_bufs
=
PIPE_DEF_BUFFERS;
struct user_struct
*
user
=
get_current_user();
unsigned
long
user_bufs;
unsigned
int
max_size
=
READ_ONCE(pipe_max_size);
pipe
=
kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_ACCOUNT);
if
(pipe
=
=
NULL)
goto out_free_uid;
if
(pipe_bufs
*
PAGE_SIZE > max_size && !capable(CAP_SYS_RESOURCE))
pipe_bufs
=
max_size >> PAGE_SHIFT;
user_bufs
=
account_pipe_buffers(user,
0
, pipe_bufs);
if
(too_many_pipe_buffers_soft(user_bufs) && pipe_is_unprivileged_user()) {
user_bufs
=
account_pipe_buffers(user, pipe_bufs, PIPE_MIN_DEF_BUFFERS);
pipe_bufs
=
PIPE_MIN_DEF_BUFFERS;
}
if
(too_many_pipe_buffers_hard(user_bufs) && pipe_is_unprivileged_user())
goto out_revert_acct;
pipe
-
>bufs
=
kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
GFP_KERNEL_ACCOUNT);
if
(pipe
-
>bufs) {
init_waitqueue_head(&pipe
-
>rd_wait);
init_waitqueue_head(&pipe
-
>wr_wait);
pipe
-
>r_counter
=
pipe
-
>w_counter
=
1
;
pipe
-
>max_usage
=
pipe_bufs;
pipe
-
>ring_size
=
pipe_bufs;
pipe
-
>nr_accounted
=
pipe_bufs;
pipe
-
>user
=
user;
mutex_init(&pipe
-
>mutex);
return
pipe;
}
out_revert_acct:
(void) account_pipe_buffers(user, pipe_bufs,
0
);
kfree(pipe);
out_free_uid:
free_uid(user);
return
NULL;
}
unsigned
long
pipe_bufs
=
PIPE_DEF_BUFFERS;
pipe
-
>bufs
=
kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
GFP_KERNEL_ACCOUNT);
unsigned
long
pipe_bufs
=
PIPE_DEF_BUFFERS;
pipe
-
>bufs
=
kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
GFP_KERNEL_ACCOUNT);
struct pipe_buf_operations {
/
*
*
-
>confirm() verifies that the data
in
the pipe
buffer
is
there
*
and
that the contents are good. If the pages
in
the pipe belong
*
to a
file
system, we may need to wait
for
IO completion
in
this
*
hook. Returns
0
for
good,
or
a negative error value
in
case of
*
error. If
not
present
all
pages are considered good.
*
/
int
(
*
confirm)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
/
*
*
When the contents of this pipe
buffer
has been completely
*
consumed by a reader,
-
>release()
is
called.
*
/
void (
*
release)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
/
*
*
Attempt to take ownership of the pipe
buffer
and
its contents.
*
-
>try_steal() returns
%
true
for
success,
in
which case the contents
*
of the pipe (the buf
-
>page)
is
locked
and
now completely owned by the
*
caller. The page may then be transferred to a different mapping, the
*
most often used case
is
insertion into different
file
address space
*
cache.
*
/
bool
(
*
try_steal)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
/
*
*
Get a reference to the pipe
buffer
.
*
/
bool
(
*
get)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
};
struct pipe_buf_operations {
/
*
*
-
>confirm() verifies that the data
in
the pipe
buffer
is
there
*
and
that the contents are good. If the pages
in
the pipe belong
*
to a
file
system, we may need to wait
for
IO completion
in
this
*
hook. Returns
0
for
good,
or
a negative error value
in
case of
*
error. If
not
present
all
pages are considered good.
*
/
int
(
*
confirm)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
/
*
*
When the contents of this pipe
buffer
has been completely
*
consumed by a reader,
-
>release()
is
called.
*
/
void (
*
release)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
/
*
*
Attempt to take ownership of the pipe
buffer
and
its contents.
*
-
>try_steal() returns
%
true
for
success,
in
which case the contents
*
of the pipe (the buf
-
>page)
is
locked
and
now completely owned by the
*
caller. The page may then be transferred to a different mapping, the
*
most often used case
is
insertion into different
file
address space
*
cache.
*
/
bool
(
*
try_steal)(struct pipe_inode_info
*
, struct pipe_buffer
*
);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-4-7 15:15
被kanxue编辑
,原因:
上传的附件: