ql query for CVE-2021-30660 XNU Kernel Memory Disclosure

CVE-2021-30660 - XNU Kernel Memory Disclosure

Vuln

msgsz 可控

msginfo.msgssz 是 8

如果控制 msgsz 不是 8的 整数倍,比如9,就会导致在第二次循环的时候 leak出来 7字节的内核数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
next = msghdr->msg_spot;
for (len = 0; len < msgsz; len += msginfo.msgssz) {
size_t tlen;

/* compare input (size_t) value against restrict (int) value */
if (msgsz > (size_t)msginfo.msgssz) {
tlen = msginfo.msgssz;
} else {
tlen = msgsz;
}
if (next <= -1) {
panic("next too low #3");
}
if (next >= msginfo.msgseg) {
panic("next out of range #3");
}
SYSV_MSG_SUBSYS_UNLOCK();
eval = copyout(&msgpool[next * msginfo.msgssz],
user_msgp, tlen);
SYSV_MSG_SUBSYS_LOCK();
if (eval != 0) {
#ifdef MSG_DEBUG_OK
printf("error (%d) copying out message segment\\n",
eval);
#endif
msg_freehdr(msghdr);
wakeup((caddr_t)msqptr);
goto msgrcvout;
}
user_msgp = user_msgp + tlen; /* ptr math */
next = msgmaps[next].next;
}

Patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (len = 0; len < msgsz; len += msginfo.msgssz) {
size_t tlen;

/*
* copy the full segment, or less if we're at the end
* of the message
*/
tlen = MIN(msgsz - len, (size_t)msginfo.msgssz);
if (next <= -1) {
panic("next too low #3");
}
if (next >= msginfo.msgseg) {
panic("next out of range #3");
}
SYSV_MSG_SUBSYS_UNLOCK();
eval = copyout(&msgpool[next * msginfo.msgssz],
user_msgp, tlen);

补丁保证了,在非8 整数倍的时候,只拷贝剩余的长度的数据。

CodeQL query

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph

// 存在误报 IOKit
predicate isSYSCall(Function f) {
exists(Macro m |
m.getName().toUpperCase().regexpMatch("SYS(.)*") and
m.getLocation().getFile().getBaseName() = "syscall.h" and
m.getName().indexOf(f.getName()) > 0
)
}

/*
syscall -> copyout
source : syscall fucntion 's params
sink : copyout 3rd param(size)
*/

class Config extends TaintTracking::Configuration {
Config() { this = "taint size to copy size" }

override predicate isSource(DataFlow::Node source) {
exists(LocalVariable lv, Function f |
isSYSCall(f) and
lv.getFunction() = f and
(
not source.asExpr().(Literal).isConstant()
) and
lv.getAnAccess() = source.asExpr()
)
}

override predicate isSink(DataFlow::Node sink) {
exists (FunctionCall fc |
fc.getTarget().getName() = "copyout" and
fc.getArgument(2) = sink.asExpr()
)
}
}

from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select source, " to ", sink, " in ", source.getNode().getFunction().getName()

有误报,但是够用了,替换成copyin,也可以看看其他的调用路径,不过笔者没发现什么有价值的东西 : (

reference

CVE-2021-30660 - XNU Kernel Memory Disclosure