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
|
qemu-system-i386 registers clobbered after gdb set due to k_gs_base bug in gdbstub
Due to a bug in /target/i386/gdbstub.c, setting registers in gdb causes the ones following k_gs_base to get clobbered.
I'm using qemu version 4.2.50 on an msys64 and start qemu's i386 with a gdb server.
$ qemu-system-i386 -version
QEMU emulator version 4.2.50 (v4.2.0-363-gdd5b0f9549-dirty)
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
$ qemu-system-i386 -gdb tcp::29096 -S
C:\msys64\usr\local\qemu-system-i386.exe: invalid accelerator kvm
C:\msys64\usr\local\qemu-system-i386.exe: falling back to tcg
I start a gdb client, connect to the server, display the register state, set k_gs_base, display the register state again, and notice an issue. (Setting other registers also clobbers the ones after k_gs_base).
$ gdb -q
(gdb) target remote :29096
...
(gdb) info regs
...
gs_base 0x0 0
k_gs_base 0x0 0
cr0 0x60000010 [ CD NW ET ]
cr2 0x0 0
...
(gdb) set $k_gs_base = 0x41414141
(gdb) info regs
...
gs_base 0x0 0
k_gs_base 0x0 0
cr0 0x41414151 [ CD WP ET PE ]
cr2 0x60000010 1610612752
...
In the gdbstub code, I notice that the read and write functions are not symmetric for IDX_SEG_REGS + 8, which corresponds to k_gs_base.
$ cat /usr/local/src/qemu-4.2.0/target/i386/gdbstub.c
...
int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
{
...
case IDX_SEG_REGS + 8:
#ifdef TARGET_X86_64
if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) {
return gdb_get_reg64(mem_buf, env->kernelgsbase);
}
return gdb_get_reg32(mem_buf, env->kernelgsbase);
#else
return gdb_get_reg32(mem_buf, 0);
#endif
...
}
...
int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
{
...
#ifdef TARGET_X86_64
case IDX_SEG_REGS + 8:
if (env->hflags & HF_CS64_MASK) {
env->kernelgsbase = ldq_p(mem_buf);
return 8;
}
env->kernelgsbase = ldl_p(mem_buf);
return 4;
#endif
...
}
...
I change the write function, rebuild, and verify that the issue is resolved.
$ cat /usr/local/src/qemu-4.2.0/target/i386/gdbstub.c
int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
{
...
case IDX_SEG_REGS + 8:
#ifdef TARGET_X86_64
if (env->hflags & HF_CS64_MASK) {
env->kernelgsbase = ldq_p(mem_buf);
return 8;
}
env->kernelgsbase = ldl_p(mem_buf);
return 4;
#else
return 4;
#endif
...
}
...
$ make
...
$ make install
...
$ qemu-system-i386 -gdb tcp::29096 -S
$ gdb -q
(gdb) target remote :29096
...
(gdb) info regs
...
gs_base 0x0 0
k_gs_base 0x0 0
cr0 0x60000010 [ CD NW ET ]
cr2 0x0 0
...
(gdb) set $k_gs_base = 0x41414141
(gdb) info regs
...
gs_base 0x0 0
k_gs_base 0x0 0
cr0 0x60000010 [ CD NW ET ]
cr2 0x0 0
...
I'll submit the patch below.
$ diff gdbstub.c gdbstub.c.bkp
353d352
< case IDX_SEG_REGS + 8:
354a354
> case IDX_SEG_REGS + 8:
362,363d361
< #else
< return 4;
|