[PATCH] system: fix buffer overflow in tdx_parse_panic_message

Maxim Levitsky posted 1 patch 3 days, 18 hours ago
system/runstate.c | 35 +++++++----------------------------
1 file changed, 7 insertions(+), 28 deletions(-)
[PATCH] system: fix buffer overflow in tdx_parse_panic_message
Posted by Maxim Levitsky 3 days, 18 hours ago
tdx_parse_panic_message has code that detects if the guest supplied
message contains some unprintable characters and in this case prints a hex dump.

The code has a bug: it allocates 3 bytes for 'sprintf("%02x ")',
but sprintf in this case actually writes 4 bytes, because it adds the NULL
terminator.

To fix this, just use qemu_hexdump_line instead.
This patch was only compile tested.

Reported-by: Jordy Zomer <jordyzomer@google.com>
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3905
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 system/runstate.c | 35 +++++++----------------------------
 1 file changed, 7 insertions(+), 28 deletions(-)

diff --git a/system/runstate.c b/system/runstate.c
index d3e64d2b6258..ebf09b192b4e 100644
--- a/system/runstate.c
+++ b/system/runstate.c
@@ -50,6 +50,7 @@
 #include "qemu/sockets.h"
 #include "qemu/timer.h"
 #include "qemu/thread.h"
+#include "qemu/cutils.h"
 #include "qom/object.h"
 #include "qom/object_interfaces.h"
 #include "system/cpu-timers.h"
@@ -760,10 +761,9 @@ static void qemu_system_wakeup(void)
     }
 }
 
-static char *tdx_parse_panic_message(char *message)
+static GString *tdx_parse_panic_message(char *message)
 {
     bool printable = false;
-    char *buf = NULL;
     int len = 0, i;
 
     /*
@@ -784,32 +784,11 @@ static char *tdx_parse_panic_message(char *message)
         }
     }
 
-    if (len == 0) {
-        buf = g_malloc(1);
-        buf[0] = '\0';
+    if (printable) {
+        return g_string_new(message);
     } else {
-        if (!printable) {
-            /* 3 = length of "%02x " */
-            buf = g_malloc(len * 3);
-            for (i = 0; i < len; i++) {
-                if (message[i] == '\0') {
-                    break;
-                } else {
-                    sprintf(buf + 3 * i, "%02x ", message[i]);
-                }
-            }
-            if (i > 0) {
-                /* replace the last ' '(space) to NULL */
-                buf[i * 3 - 1] = '\0';
-            } else {
-                buf[0] = '\0';
-            }
-        } else {
-            buf = g_strdup(message);
-        }
+        return qemu_hexdump_line(NULL, message, len, 1, 0);
     }
-
-    return buf;
 }
 
 void qemu_system_guest_panicked(GuestPanicInformation *info)
@@ -854,11 +833,11 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
                           info->u.s390.psw_mask,
                           info->u.s390.psw_addr);
         } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) {
-            char *message = tdx_parse_panic_message(info->u.tdx.message);
+            GString *message = tdx_parse_panic_message(info->u.tdx.message);
             qemu_log_mask(LOG_GUEST_ERROR,
                           "\nTDX guest reports fatal error."
                           " error code: 0x%" PRIx32 " error message:\"%s\"\n",
-                          info->u.tdx.error_code, message);
+                          info->u.tdx.error_code, message->str);
             g_free(message);
             if (info->u.tdx.has_gpa) {
                 qemu_log_mask(LOG_GUEST_ERROR, "Additional error information "
-- 
2.54.0
Re: [PATCH] system: fix buffer overflow in tdx_parse_panic_message
Posted by Paolo Bonzini 3 days, 4 hours ago
On 9/23/26 02:45, Maxim Levitsky wrote:
> tdx_parse_panic_message has code that detects if the guest supplied
> message contains some unprintable characters and in this case prints a hex dump.
> 
> The code has a bug: it allocates 3 bytes for 'sprintf("%02x ")',
> but sprintf in this case actually writes 4 bytes, because it adds the NULL
> terminator.
> 
> To fix this, just use qemu_hexdump_line instead.
> This patch was only compile tested.
> 
> Reported-by: Jordy Zomer <jordyzomer@google.com>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3905
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>   system/runstate.c | 35 +++++++----------------------------
>   1 file changed, 7 insertions(+), 28 deletions(-)
> 
> diff --git a/system/runstate.c b/system/runstate.c
> index d3e64d2b6258..ebf09b192b4e 100644
> --- a/system/runstate.c
> +++ b/system/runstate.c
> @@ -50,6 +50,7 @@
>   #include "qemu/sockets.h"
>   #include "qemu/timer.h"
>   #include "qemu/thread.h"
> +#include "qemu/cutils.h"
>   #include "qom/object.h"
>   #include "qom/object_interfaces.h"
>   #include "system/cpu-timers.h"
> @@ -760,10 +761,9 @@ static void qemu_system_wakeup(void)
>       }
>   }
>   
> -static char *tdx_parse_panic_message(char *message)
> +static GString *tdx_parse_panic_message(char *message)
>   {
>       bool printable = false;
> -    char *buf = NULL;
>       int len = 0, i;
>   
>       /*
> @@ -784,32 +784,11 @@ static char *tdx_parse_panic_message(char *message)
>           }
>       }
>   
> -    if (len == 0) {
> -        buf = g_malloc(1);
> -        buf[0] = '\0';
> +    if (printable) {
> +        return g_string_new(message);
>       } else {
> -        if (!printable) {
> -            /* 3 = length of "%02x " */
> -            buf = g_malloc(len * 3);
> -            for (i = 0; i < len; i++) {
> -                if (message[i] == '\0') {
> -                    break;
> -                } else {
> -                    sprintf(buf + 3 * i, "%02x ", message[i]);
> -                }
> -            }
> -            if (i > 0) {
> -                /* replace the last ' '(space) to NULL */
> -                buf[i * 3 - 1] = '\0';
> -            } else {
> -                buf[0] = '\0';
> -            }
> -        } else {
> -            buf = g_strdup(message);
> -        }
> +        return qemu_hexdump_line(NULL, message, len, 1, 0);
>       }
> -
> -    return buf;
>   }
>   
>   void qemu_system_guest_panicked(GuestPanicInformation *info)
> @@ -854,11 +833,11 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
>                             info->u.s390.psw_mask,
>                             info->u.s390.psw_addr);
>           } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) {
> -            char *message = tdx_parse_panic_message(info->u.tdx.message);
> +            GString *message = tdx_parse_panic_message(info->u.tdx.message);
>               qemu_log_mask(LOG_GUEST_ERROR,
>                             "\nTDX guest reports fatal error."
>                             " error code: 0x%" PRIx32 " error message:\"%s\"\n",
> -                          info->u.tdx.error_code, message);
> +                          info->u.tdx.error_code, message->str);
>               g_free(message);

Freeing the GString* leaks message->str.

The easiest way to do it, which I have squashed on top of your patch, is

    g_autoptr(GString) message = ...

and dropping the g_free(message) line.  See for example sdbus_write_dump().

Paolo
Re: [PATCH] system: fix buffer overflow in tdx_parse_panic_message
Posted by mlevitsk@redhat.com 3 days, 3 hours ago
On Wed, 2026-09-23 at 16:36 +0200, Paolo Bonzini wrote:
> On 9/23/26 02:45, Maxim Levitsky wrote:
> > tdx_parse_panic_message has code that detects if the guest supplied
> > message contains some unprintable characters and in this case prints a hex dump.
> > 
> > The code has a bug: it allocates 3 bytes for 'sprintf("%02x ")',
> > but sprintf in this case actually writes 4 bytes, because it adds the NULL
> > terminator.
> > 
> > To fix this, just use qemu_hexdump_line instead.
> > This patch was only compile tested.
> > 
> > Reported-by: Jordy Zomer <jordyzomer@google.com>
> > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3905
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >   system/runstate.c | 35 +++++++----------------------------
> >   1 file changed, 7 insertions(+), 28 deletions(-)
> > 
> > diff --git a/system/runstate.c b/system/runstate.c
> > index d3e64d2b6258..ebf09b192b4e 100644
> > --- a/system/runstate.c
> > +++ b/system/runstate.c
> > @@ -50,6 +50,7 @@
> >   #include "qemu/sockets.h"
> >   #include "qemu/timer.h"
> >   #include "qemu/thread.h"
> > +#include "qemu/cutils.h"
> >   #include "qom/object.h"
> >   #include "qom/object_interfaces.h"
> >   #include "system/cpu-timers.h"
> > @@ -760,10 +761,9 @@ static void qemu_system_wakeup(void)
> >       }
> >   }
> >   
> > -static char *tdx_parse_panic_message(char *message)
> > +static GString *tdx_parse_panic_message(char *message)
> >   {
> >       bool printable = false;
> > -    char *buf = NULL;
> >       int len = 0, i;
> >   
> >       /*
> > @@ -784,32 +784,11 @@ static char *tdx_parse_panic_message(char *message)
> >           }
> >       }
> >   
> > -    if (len == 0) {
> > -        buf = g_malloc(1);
> > -        buf[0] = '\0';
> > +    if (printable) {
> > +        return g_string_new(message);
> >       } else {
> > -        if (!printable) {
> > -            /* 3 = length of "%02x " */
> > -            buf = g_malloc(len * 3);
> > -            for (i = 0; i < len; i++) {
> > -                if (message[i] == '\0') {
> > -                    break;
> > -                } else {
> > -                    sprintf(buf + 3 * i, "%02x ", message[i]);
> > -                }
> > -            }
> > -            if (i > 0) {
> > -                /* replace the last ' '(space) to NULL */
> > -                buf[i * 3 - 1] = '\0';
> > -            } else {
> > -                buf[0] = '\0';
> > -            }
> > -        } else {
> > -            buf = g_strdup(message);
> > -        }
> > +        return qemu_hexdump_line(NULL, message, len, 1, 0);
> >       }
> > -
> > -    return buf;
> >   }
> >   
> >   void qemu_system_guest_panicked(GuestPanicInformation *info)
> > @@ -854,11 +833,11 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
> >                             info->u.s390.psw_mask,
> >                             info->u.s390.psw_addr);
> >           } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) {
> > -            char *message = tdx_parse_panic_message(info->u.tdx.message);
> > +            GString *message = tdx_parse_panic_message(info->u.tdx.message);
> >               qemu_log_mask(LOG_GUEST_ERROR,
> >                             "\nTDX guest reports fatal error."
> >                             " error code: 0x%" PRIx32 " error message:\"%s\"\n",
> > -                          info->u.tdx.error_code, message);
> > +                          info->u.tdx.error_code, message->str);
> >               g_free(message);
> 
> Freeing the GString* leaks message->str.
> 
> The easiest way to do it, which I have squashed on top of your patch, is
> 
>     g_autoptr(GString) message = ...
> 
> and dropping the g_free(message) line.  See for example sdbus_write_dump().

Oops, thanks for fixing this, I thought about that g_free(), but I guess not enough.

Best regards,
	Maxim Levitsky

> 
> Paolo