* [Qemu-devel] [PATCH] fix gdbserver_state pointer validation @ 2018-07-09 12:54 stephane duverger 2018-07-09 15:42 ` Alex Bennée 0 siblings, 1 reply; 7+ messages in thread From: stephane duverger @ 2018-07-09 12:54 UTC (permalink / raw) To: qemu-devel Hi, This is a small patch to gdbstub rather insignificant at first sight: fix null pointer dereference. It actually allows to take benefit of gdb features (breakpoints/sstep) internally (ie. special purpose board) without connecting a gdb client to the Qemu instance gdbserver stub. Regards, Signed-off-by: Stephane Duverger <stephane.duverger@gmail.com> --- gdbstub.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d6ab95006c..788fd625ab 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1412,6 +1412,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { + if (!gdbserver_state) { + return; + } gdbserver_state->c_cpu = cpu; gdbserver_state->g_cpu = cpu; } -- 2.14.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-09 12:54 [Qemu-devel] [PATCH] fix gdbserver_state pointer validation stephane duverger @ 2018-07-09 15:42 ` Alex Bennée 2018-07-10 11:44 ` stephane duverger 0 siblings, 1 reply; 7+ messages in thread From: Alex Bennée @ 2018-07-09 15:42 UTC (permalink / raw) To: stephane duverger; +Cc: qemu-devel stephane duverger <stephane.duverger@gmail.com> writes: > Hi, > > This is a small patch to gdbstub rather insignificant at first sight: > fix null pointer dereference. It actually allows to take benefit of > gdb features (breakpoints/sstep) internally (ie. special purpose > board) without connecting a gdb client to the Qemu instance gdbserver > stub. There don't seem to be any other patches attached? I would NACK a patch that isn't actually used in-tree. I would also like to see how this would be used because we certainly have different paths for KVM and TCG break-point emulation that don't need to go through the gdbstub to achieve what they are doing. > > Regards, > > Signed-off-by: Stephane Duverger <stephane.duverger@gmail.com> > --- > gdbstub.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/gdbstub.c b/gdbstub.c > index d6ab95006c..788fd625ab 100644 > --- a/gdbstub.c > +++ b/gdbstub.c > @@ -1412,6 +1412,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) > > void gdb_set_stop_cpu(CPUState *cpu) > { > + if (!gdbserver_state) { > + return; > + } > gdbserver_state->c_cpu = cpu; > gdbserver_state->g_cpu = cpu; > } -- Alex Bennée ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-09 15:42 ` Alex Bennée @ 2018-07-10 11:44 ` stephane duverger 2018-07-10 21:14 ` Philippe Mathieu-Daudé 0 siblings, 1 reply; 7+ messages in thread From: stephane duverger @ 2018-07-10 11:44 UTC (permalink / raw) To: alex.bennee, qemu-devel Hi Alex, There don't seem to be any other patches attached? I would NACK a patch > that isn't actually used in-tree. No there isn't ! I should have not been so prolix. Actually the patch corrects a possible null pointer dereference in the gdbserver code. That's all folks. Below is how I discovered it and why it matters, imho, to be fixed (out of a pending issue). This null deref does not happen in normal operation because of the way gdbserver is initialized. However what I was telling you, is that it may be really interesting to be able to take benefit of some gdb features internally without starting a gdbserver and the overhead of the gdb protocol when there is no need for a client/server interaction while debugging. For instance, I developed a special purpose x86 board where I needed to break at some instructions, do some automation (snapshots, vm memory access, cpu analysis) and then resume the VM. Implementing a "vm_change_state handler" and dealing with RUN_STATE_DEBUG was perfect for my need. The debug events (#BP, #DB) are tied to the gdbserver stub, at some point when "cpu_handle_guest_debug()" is called, a call to "gdb_set_stop_cpu()" triggers the NULL deref because the gdbserver is not initialized. My little fix addresses this error and allows to make use of the following Qemu breakpoint functions without touching "cpus.c" or "exec.c": - cpu_breakpoint_remove/insert() - kvm_remove/insert_breakpoint() Notice, that in the particular case of KVM, I had to async_run_on_cpu() my debug handler instead of directly executing it in the context of vm_change_state(). The vCPU started to significantly slow down (low irq rate), while this never happens with the TCG. Is that more clear to you Alex ? (and hopefully lead to patch ACK :) Regards, stephane > Signed-off-by: Stephane Duverger <stephane.duverger@gmail.com> > > --- > > gdbstub.c | 3 +++ > > 1 file changed, 3 insertions(+) > > > > diff --git a/gdbstub.c b/gdbstub.c > > index d6ab95006c..788fd625ab 100644 > > --- a/gdbstub.c > > +++ b/gdbstub.c > > @@ -1412,6 +1412,9 @@ static int gdb_handle_packet(GDBState *s, const > char *line_buf) > > > > void gdb_set_stop_cpu(CPUState *cpu) > > { > > + if (!gdbserver_state) { > > + return; > > + } > > gdbserver_state->c_cpu = cpu; > > gdbserver_state->g_cpu = cpu; > > } > > > -- > Alex Bennée > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-10 11:44 ` stephane duverger @ 2018-07-10 21:14 ` Philippe Mathieu-Daudé 2018-07-11 7:52 ` stephane duverger 0 siblings, 1 reply; 7+ messages in thread From: Philippe Mathieu-Daudé @ 2018-07-10 21:14 UTC (permalink / raw) To: stephane duverger, alex.bennee, qemu-devel On 07/10/2018 08:44 AM, stephane duverger wrote: > Hi Alex, > > There don't seem to be any other patches attached? I would NACK a patch >> that isn't actually used in-tree. > > > No there isn't ! I should have not been so prolix. Actually the patch > corrects a > possible null pointer dereference in the gdbserver code. That's all folks. > > Below is how I discovered it and why it matters, imho, to be fixed > (out of a pending issue). > > This null deref does not happen in normal operation because of the way > gdbserver is initialized. However what I was telling you, is that it may be > really interesting to be able to take benefit of some gdb features > internally > without starting a gdbserver and the overhead of the gdb protocol when > there is no need for a client/server interaction while debugging. To reach gdb_set_stop_cpu() with gdbserver_state == NULL, you previously entered gdb_vm_state_change() with and use CPUState *cpu = gdbserver_state->c_cpu = NULL deref, which shouldn't happen. Also in gdb_set_stop_cpu() you finally call cpu_single_step(cpu=crap) which then deref crap->singlestep_enabled. So I think this is not the correct fix. Since this shouldn't happen, I'd add an assert(gdbserver_state) in gdb_set_stop_cpu(), and clean gdb_vm_state_change()'s code flow to not reach this. > For instance, I developed a special purpose x86 board where I needed to > break at some instructions, do some automation (snapshots, vm memory > access, cpu analysis) and then resume the VM. Implementing a > "vm_change_state handler" and dealing with RUN_STATE_DEBUG was > perfect for my need. I recommend you to use "configure --enable-sanitizers" to build your special purpose board, that will show up those issues. > The debug events (#BP, #DB) are tied to the gdbserver stub, at some point > when "cpu_handle_guest_debug()" is called, a call to "gdb_set_stop_cpu()" > triggers the NULL deref because the gdbserver is not initialized. > > My little fix addresses this error and allows to make use of the following > Qemu > breakpoint functions without touching "cpus.c" or "exec.c": > - cpu_breakpoint_remove/insert() > - kvm_remove/insert_breakpoint() > > Notice, that in the particular case of KVM, I had to async_run_on_cpu() my > debug handler instead of directly executing it in the context of > vm_change_state(). The vCPU started to significantly slow down > (low irq rate), while this never happens with the TCG. > > Is that more clear to you Alex ? (and hopefully lead to patch ACK :) > > Regards, > > stephane > >> Signed-off-by: Stephane Duverger <stephane.duverger@gmail.com> >>> --- >>> gdbstub.c | 3 +++ >>> 1 file changed, 3 insertions(+) >>> >>> diff --git a/gdbstub.c b/gdbstub.c >>> index d6ab95006c..788fd625ab 100644 >>> --- a/gdbstub.c >>> +++ b/gdbstub.c >>> @@ -1412,6 +1412,9 @@ static int gdb_handle_packet(GDBState *s, const >> char *line_buf) >>> >>> void gdb_set_stop_cpu(CPUState *cpu) >>> { >>> + if (!gdbserver_state) { >>> + return; >>> + } >>> gdbserver_state->c_cpu = cpu; >>> gdbserver_state->g_cpu = cpu; I find it safer the opposite way, if (s) { s-> ... } >>> } Regards, Phil. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-10 21:14 ` Philippe Mathieu-Daudé @ 2018-07-11 7:52 ` stephane duverger 2018-07-11 13:58 ` Philippe Mathieu-Daudé 0 siblings, 1 reply; 7+ messages in thread From: stephane duverger @ 2018-07-11 7:52 UTC (permalink / raw) To: f4bug; +Cc: Alex Bennée, qemu-devel To reach gdb_set_stop_cpu() with gdbserver_state == NULL, you previously > entered gdb_vm_state_change() with and use CPUState *cpu = > gdbserver_state->c_cpu = NULL deref, which shouldn't happen. > Also in gdb_set_stop_cpu() you finally call cpu_single_step(cpu=crap) > which then deref crap->singlestep_enabled. > > So I think this is not the correct fix. > I think you are wrong. You can enter gdb_vm_state_change() only if it has been registered through qemu_add_vm_change_state_handler(). And this happens in gdbserver_start() which is called only when you start the gdbserver stub. This is exactly what we don't want to do here: use qemu breakpoints and debug events forwarding without the need to enable a gdb stub. There might be an historical reason that vCPU debug events are always forwarded to the gdbserver (from cpu_handle_guest_debug()) but this should not be mandatory. One could consider a check to the gdbserver state right before: if (gdbserver_enabled()) gdb_set_stop_cpu(cpu); As found in other part of qemu code: kvm_enabled, hax_enabled, ... > Since this shouldn't happen, I'd add an assert(gdbserver_state) in > gdb_set_stop_cpu(), and clean gdb_vm_state_change()'s code flow to not > reach this. > Correct if you previously add the gdbserver_enabled() check. Else this would abort the qemu instance each time a debug event is triggered and forwarded to a vm_change_state handler. >>> void gdb_set_stop_cpu(CPUState *cpu) > >>> { > >>> + if (!gdbserver_state) { > >>> + return; > >>> + } > >>> gdbserver_state->c_cpu = cpu; > >>> gdbserver_state->g_cpu = cpu; > > I find it safer the opposite way, if (s) { s-> ... } > Sincerely, this argument is subjective. If it's part of Qemu coding standard, i would agree. Again there is a lot of situations in the Qemu code where exit conditions are checked first and then lead to a return, preventing an unneeded level of indentation. Seriously, we are talking about a 2 lines function. stephane ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-11 7:52 ` stephane duverger @ 2018-07-11 13:58 ` Philippe Mathieu-Daudé 2018-07-12 8:30 ` stephane duverger 0 siblings, 1 reply; 7+ messages in thread From: Philippe Mathieu-Daudé @ 2018-07-11 13:58 UTC (permalink / raw) To: stephane duverger; +Cc: Alex Bennée, qemu-devel [-- Attachment #1: Type: text/plain, Size: 2606 bytes --] Hi Stephane, On 07/11/2018 04:52 AM, stephane duverger wrote: > To reach gdb_set_stop_cpu() with gdbserver_state == NULL, you previously >> entered gdb_vm_state_change() with and use CPUState *cpu = >> gdbserver_state->c_cpu = NULL deref, which shouldn't happen. >> Also in gdb_set_stop_cpu() you finally call cpu_single_step(cpu=crap) >> which then deref crap->singlestep_enabled. >> >> So I think this is not the correct fix. >> > > I think you are wrong. You can enter gdb_vm_state_change() only if it has > been registered through qemu_add_vm_change_state_handler(). And this > happens in gdbserver_start() which is called only when you start the > gdbserver stub. > > This is exactly what we don't want to do here: use qemu breakpoints and > debug events forwarding without the need to enable a gdb stub. Well, at least we agree the gdb stub code is not straightforward. And apparently without seeing the bigger picture about how you are using this, I am missing something. > There might be an historical reason that vCPU debug events are always > forwarded to the gdbserver (from cpu_handle_guest_debug()) but this > should not be mandatory. > > One could consider a check to the gdbserver state right before: > > if (gdbserver_enabled()) > gdb_set_stop_cpu(cpu); > > As found in other part of qemu code: kvm_enabled, hax_enabled, ... > > >> Since this shouldn't happen, I'd add an assert(gdbserver_state) in >> gdb_set_stop_cpu(), and clean gdb_vm_state_change()'s code flow to not >> reach this. >> > > Correct if you previously add the gdbserver_enabled() check. Else this > would abort the qemu instance each time a debug event is triggered > and forwarded to a vm_change_state handler. > >>>> void gdb_set_stop_cpu(CPUState *cpu) >>>>> { >>>>> + if (!gdbserver_state) { >>>>> + return; >>>>> + } >>>>> gdbserver_state->c_cpu = cpu; >>>>> gdbserver_state->g_cpu = cpu; >> >> I find it safer the opposite way, if (s) { s-> ... } >> > > Sincerely, this argument is subjective. If it's part of Qemu coding > standard, > i would agree. Again there is a lot of situations in the Qemu code where > exit conditions are checked first and then lead to a return, preventing an > unneeded level of indentation. Seriously, we are talking about a 2 lines > function. This is indeed a personal subjective suggestion, not part of QEMU Coding standard. Rational is, if in the future someone needs to modify gdb_set_stop_cpu(), it would be easier/safer for him. No worries ;) Regards, Phil. [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH] fix gdbserver_state pointer validation 2018-07-11 13:58 ` Philippe Mathieu-Daudé @ 2018-07-12 8:30 ` stephane duverger 0 siblings, 0 replies; 7+ messages in thread From: stephane duverger @ 2018-07-12 8:30 UTC (permalink / raw) To: f4bug; +Cc: Alex Bennée, qemu-devel Hi Phil, Well, at least we agree the gdb stub code is not straightforward. > > And apparently without seeing the bigger picture about how you are using > this, I am missing something. Sorry for that, i will try to explain it clearly. This is something rather simple indeed: 1 - during MachineState init, install a BP1 at an "OS_ready" location 2 - handle BP1 hit (thanks to vm_state_change()) 3 - delete BP1 and save the VM 4 - inject some generated code 5 - install BP2 at injected code end 6 - resume the VM 7 - handle BP2 hit and restore the VM 8 - go to 4 So as you may guess, I didn't need a full gdbstub with a gdbclient to do that. I just wanted to install breakpoints and handle them internally. To be fast, no overhead with the gdb protocol. I didn't find something ready to use in Qemu API, but if there exists something to do it, I would be glad to know. By the time, I decided to make use of the GDB breakpoint type. It was a perfect candidate, but unfortunately any time cpu_handle_guest_debug() is called, gdb_set_stop_cpu() is systematically called too. That was my issue. As gdbserver is not up and running, gdbserver_state is NULL and Qemu segfaults. Out of this gdb centric function call, the standard Qemu vm stop notification process seems perfect to me: - qemu_system_debug_request - do_vm_stop - vm_state_notify - and then my "vm_state_change_handler" is called So that's why I thought it would be great to be able to install breakpoints with either cpu_breakpoint_insert() or kvm_insert_breakpoint(). What could have been done to be pragmatic: 1 - implement a new breakpoint type for internal purpose ? 2 - implement a gdbserver_enabled state ? 3 - check for the gdbserver_state NULL pointer case and return ? The point 3 was the most simple, tied to only one gdb function, didn't touch anything else and actually "secured" that gdb_set_stop_cpu() function. This is indeed a personal subjective suggestion, not part of QEMU Coding > standard. Rational is, if in the future someone needs to modify > gdb_set_stop_cpu(), it would be easier/safer for him. > I do understand that point. So what do we do right now : 1 - ACK the patch as is ? 2 - do you want a new patch with: - a gdbserver_enabled state - a safe execution condition in gdb_set_stop_cpu() - an abort() in it if something is wrong Regards, stephane ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-07-12 8:30 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-07-09 12:54 [Qemu-devel] [PATCH] fix gdbserver_state pointer validation stephane duverger 2018-07-09 15:42 ` Alex Bennée 2018-07-10 11:44 ` stephane duverger 2018-07-10 21:14 ` Philippe Mathieu-Daudé 2018-07-11 7:52 ` stephane duverger 2018-07-11 13:58 ` Philippe Mathieu-Daudé 2018-07-12 8:30 ` stephane duverger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).