Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Guinevere Larsen <guinevere@redhat.com>
To: Luis Machado <luis.machado@amd.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH] gdb: replace alloca with gdb::char_vector in remote-fileio.c
Date: Tue, 30 Jun 2026 18:07:30 -0300	[thread overview]
Message-ID: <07986ffa-b589-4661-a108-d6b27cf1a198@redhat.com> (raw)
In-Reply-To: <20260630095238.1700797-1-luis.machado@amd.com>

On 6/30/26 6:52 AM, Luis Machado wrote:
> remote_fileio_extract_ptr_w_len parsed a debuggee-controlled 64-bit
> length, narrowed it to int with no bounds check, and the value flowed
> directly into alloca() across RSP File-I/O handlers (Fopen, Frename,
> Funlink, Fstat, Fsystem).  A malicious inferior or remote
> stub could send a crafted packet with a path-length near INT_MAX,
> displacing the stack pointer by ~2 GiB and potentially crashing the debugger.
>
> remote_fileio_extract_ptr_w_len now rejects negative lengths, zero, and
> values above PATH_MAX before the int cast.  An allow_zero_length flag
> lets the Fsystem handler opt in to the zero-length sentinel that signals a
> NULL cmdline query.  All other handlers use the default and are protected
> without any per-call checks.  All alloca(length) calls have been replaced
> with gdb::char_vector.
>
> Add a selftest that exercises various problematic cases.
>
> Signed-off-by: Luis Machado <luis.machado@amd.com>

Hi Luis!

I took a look at this and everything looks good! And I'm always happy to 
see fewer uses of alloca

Reviewed-By: Guinevere Larsen <guinevere@redhat.com>

-- 
Cheers,
Guinevere Larsen
it/its
she/her (deprecated)

> ---
>   gdb/remote-fileio.c | 150 +++++++++++++++++++++++++++++++++++---------
>   1 file changed, 119 insertions(+), 31 deletions(-)
>
> diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c
> index ef608cabfab..50c7869a1a5 100644
> --- a/gdb/remote-fileio.c
> +++ b/gdb/remote-fileio.c
> @@ -31,8 +31,11 @@
>   #include "target.h"
>   #include "filenames.h"
>   #include "gdbsupport/filestuff.h"
> +#include "gdbsupport/byte-vector.h"
> +#include "gdbsupport/selftest.h"
>   
>   #include <fcntl.h>
> +#include <climits>
>   #include "gdbsupport/gdb_sys_time.h"
>   #ifdef __CYGWIN__
>   #include <sys/cygwin.h>
> @@ -194,7 +197,8 @@ remote_fileio_extract_int (char **buf, long *retint)
>   }
>   
>   static int
> -remote_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length)
> +remote_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length,
> +				  bool allow_zero_length = false)
>   {
>     char *c;
>     LONGEST retlong;
> @@ -211,6 +215,11 @@ remote_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length)
>     *buf = c;
>     if (remote_fileio_extract_long (buf, &retlong))
>       return -1;
> +  /* Reject negative lengths, zero (unless the caller permits it for the
> +     Fsystem NULL-cmdline sentinel), and lengths above PATH_MAX to prevent
> +     out-of-bounds memory reads or oversized heap allocations.  */
> +  if (retlong < 0 || (!allow_zero_length && retlong == 0) || retlong > PATH_MAX)
> +    return -1;
>     *length = (int) retlong;
>     return 0;
>   }
> @@ -305,7 +314,6 @@ remote_fileio_func_open (remote_target *remote, char *buf)
>     long num;
>     int flags, fd;
>     mode_t mode;
> -  char *pathname;
>     struct stat st;
>   
>     /* 1. Parameter: Ptr to pathname / length incl. trailing zero.  */
> @@ -339,8 +347,8 @@ remote_fileio_func_open (remote_target *remote, char *buf)
>       }
>   
>     /* Request pathname.  */
> -  pathname = (char *) alloca (length);
> -  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
> +  gdb::char_vector pathname (length);
> +  if (target_read_memory (ptrval, (gdb_byte *) pathname.data (), length) != 0)
>       {
>         remote_fileio_ioerror (remote);
>         return;
> @@ -349,7 +357,7 @@ remote_fileio_func_open (remote_target *remote, char *buf)
>     /* Check if pathname exists and is not a regular file or directory.  If so,
>        return an appropriate error code.  Same for trying to open directories
>        for writing.  */
> -  if (!stat (pathname, &st))
> +  if (!stat (pathname.data (), &st))
>       {
>         if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
>   	{
> @@ -364,7 +372,7 @@ remote_fileio_func_open (remote_target *remote, char *buf)
>   	}
>       }
>   
> -  fd = gdb_open_cloexec (pathname, flags, mode).release ();
> +  fd = gdb_open_cloexec (pathname.data (), flags, mode).release ();
>     if (fd < 0)
>       {
>         remote_fileio_return_errno (remote, -1);
> @@ -659,7 +667,6 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
>   {
>     CORE_ADDR old_ptr, new_ptr;
>     int old_len, new_len;
> -  char *oldpath, *newpath;
>     int ret, of, nf;
>     struct stat ost, nst;
>   
> @@ -678,24 +685,24 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
>       }
>   
>     /* Request oldpath using 'm' packet */
> -  oldpath = (char *) alloca (old_len);
> -  if (target_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
> +  gdb::char_vector oldpath (old_len);
> +  if (target_read_memory (old_ptr, (gdb_byte *) oldpath.data (), old_len) != 0)
>       {
>         remote_fileio_ioerror (remote);
>         return;
>       }
>   
>     /* Request newpath using 'm' packet */
> -  newpath = (char *) alloca (new_len);
> -  if (target_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
> +  gdb::char_vector newpath (new_len);
> +  if (target_read_memory (new_ptr, (gdb_byte *) newpath.data (), new_len) != 0)
>       {
>         remote_fileio_ioerror (remote);
>         return;
>       }
>   
>     /* Only operate on regular files and directories.  */
> -  of = stat (oldpath, &ost);
> -  nf = stat (newpath, &nst);
> +  of = stat (oldpath.data (), &ost);
> +  nf = stat (newpath.data (), &nst);
>     if ((!of && !S_ISREG (ost.st_mode) && !S_ISDIR (ost.st_mode))
>         || (!nf && !S_ISREG (nst.st_mode) && !S_ISDIR (nst.st_mode)))
>       {
> @@ -703,7 +710,7 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
>         return;
>       }
>   
> -  ret = rename (oldpath, newpath);
> +  ret = rename (oldpath.data (), newpath.data ());
>   
>     if (ret == -1)
>       {
> @@ -726,10 +733,10 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
>   		  char newfullpath[PATH_MAX];
>   		  int len;
>   
> -		  cygwin_conv_path (CCP_WIN_A_TO_POSIX, oldpath, oldfullpath,
> -				    PATH_MAX);
> -		  cygwin_conv_path (CCP_WIN_A_TO_POSIX, newpath, newfullpath,
> -				    PATH_MAX);
> +		  cygwin_conv_path (CCP_WIN_A_TO_POSIX, oldpath.data (),
> +				    oldfullpath, PATH_MAX);
> +		  cygwin_conv_path (CCP_WIN_A_TO_POSIX, newpath.data (),
> +				    newfullpath, PATH_MAX);
>   		  len = strlen (oldfullpath);
>   		  if (IS_DIR_SEPARATOR (newfullpath[len])
>   		      && !filename_ncmp (oldfullpath, newfullpath, len))
> @@ -752,7 +759,6 @@ remote_fileio_func_unlink (remote_target *remote, char *buf)
>   {
>     CORE_ADDR ptrval;
>     int length;
> -  char *pathname;
>     int ret;
>     struct stat st;
>   
> @@ -763,8 +769,8 @@ remote_fileio_func_unlink (remote_target *remote, char *buf)
>         return;
>       }
>     /* Request pathname using 'm' packet */
> -  pathname = (char *) alloca (length);
> -  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
> +  gdb::char_vector pathname (length);
> +  if (target_read_memory (ptrval, (gdb_byte *) pathname.data (), length) != 0)
>       {
>         remote_fileio_ioerror (remote);
>         return;
> @@ -772,13 +778,13 @@ remote_fileio_func_unlink (remote_target *remote, char *buf)
>   
>     /* Only operate on regular files (and directories, which allows to return
>        the correct return code).  */
> -  if (!stat (pathname, &st) && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
> +  if (!stat (pathname.data (), &st) && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
>       {
>         remote_fileio_reply (remote, -1, FILEIO_ENODEV);
>         return;
>       }
>   
> -  ret = unlink (pathname);
> +  ret = unlink (pathname.data ());
>   
>     if (ret == -1)
>       remote_fileio_return_errno (remote, -1);
> @@ -791,7 +797,6 @@ remote_fileio_func_stat (remote_target *remote, char *buf)
>   {
>     CORE_ADDR statptr, nameptr;
>     int ret, namelength;
> -  char *pathname;
>     LONGEST lnum;
>     struct stat st;
>     struct fio_stat fst;
> @@ -812,14 +817,14 @@ remote_fileio_func_stat (remote_target *remote, char *buf)
>     statptr = (CORE_ADDR) lnum;
>   
>     /* Request pathname using 'm' packet */
> -  pathname = (char *) alloca (namelength);
> -  if (target_read_memory (nameptr, (gdb_byte *) pathname, namelength) != 0)
> +  gdb::char_vector pathname (namelength);
> +  if (target_read_memory (nameptr, (gdb_byte *) pathname.data (), namelength) != 0)
>       {
>         remote_fileio_ioerror (remote);
>         return;
>       }
>   
> -  ret = stat (pathname, &st);
> +  ret = stat (pathname.data (), &st);
>   
>     if (ret == -1)
>       {
> @@ -997,24 +1002,28 @@ remote_fileio_func_system (remote_target *remote, char *buf)
>   {
>     CORE_ADDR ptrval;
>     int ret, length;
> -  char *cmdline = NULL;
>   
>     /* Parameter: Ptr to commandline / length incl. trailing zero */
> -  if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length))
> +  if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length, true))
>       {
>         remote_fileio_ioerror (remote);
>         return;
>       }
>   
> +  gdb::char_vector cmdline_buf;
> +  const char *cmdline = nullptr;
> +
>     if (length)
>       {
>         /* Request commandline using 'm' packet */
> -      cmdline = (char *) alloca (length);
> -      if (target_read_memory (ptrval, (gdb_byte *) cmdline, length) != 0)
> +      cmdline_buf.resize (length);
> +      if (target_read_memory (ptrval, (gdb_byte *) cmdline_buf.data (),
> +			      length) != 0)
>   	{
>   	  remote_fileio_ioerror (remote);
>   	  return;
>   	}
> +      cmdline = cmdline_buf.data ();
>       }
>   
>     /* Check if system(3) has been explicitly allowed using the
> @@ -1244,6 +1253,81 @@ show_system_call_allowed (const char *args, int from_tty)
>   	      remote_fio_system_call_allowed ? "" : "not ");
>   }
>   
> +#if GDB_SELF_TEST
> +
> +namespace selftests {
> +
> +/* Verify that remote_fileio_extract_ptr_w_len rejects lengths that are
> +   negative or exceed PATH_MAX, and accepts boundary values.  The packet
> +   buffer format is "ptr/len[,...]" with both fields as hex integers.  */
> +
> +static void
> +test_remote_fileio_extract_ptr_w_len ()
> +{
> +  CORE_ADDR ptrval;
> +  int length;
> +
> +  /* Build a writable "ptr/len" buffer and parse it, with and without
> +     the Fsystem zero-length allowance.  */
> +  auto parse = [&] (const char *s) -> int
> +    {
> +      std::string buf (s);
> +      char *p = buf.data ();
> +      return remote_fileio_extract_ptr_w_len (&p, &ptrval, &length);
> +    };
> +  auto parse_allow_zero = [&] (const char *s) -> int
> +    {
> +      std::string buf (s);
> +      char *p = buf.data ();
> +      return remote_fileio_extract_ptr_w_len (&p, &ptrval, &length, true);
> +    };
> +
> +  /* Valid: length 1 (minimum positive).  Verify both fields are parsed.  */
> +  SELF_CHECK (parse ("deadbeef/1") == 0);
> +  SELF_CHECK (ptrval == 0xdeadbeef);
> +  SELF_CHECK (length == 1);
> +
> +  /* Valid: packet with trailing fields.  */
> +  SELF_CHECK (parse ("deadbeef/4,0,1a4") == 0);
> +  SELF_CHECK (ptrval == 0xdeadbeef);
> +  SELF_CHECK (length == 4);
> +
> +  /* Valid: length 0 with allow_zero_length (Fsystem).  */
> +  SELF_CHECK (parse_allow_zero ("0/0") == 0);
> +  SELF_CHECK (length == 0);
> +
> +  /* Invalid: length 0 without allow_zero_length (pathname handlers).  */
> +  SELF_CHECK (parse ("0/0") == -1);
> +
> +  /* Valid: length exactly PATH_MAX.  */
> +  {
> +    char hex[32];
> +    xsnprintf (hex, sizeof (hex), "1/%x", PATH_MAX);
> +    SELF_CHECK (parse (hex) == 0);
> +    SELF_CHECK (length == PATH_MAX);
> +  }
> +
> +  /* Invalid: length PATH_MAX + 1.  */
> +  {
> +    char hex[32];
> +    xsnprintf (hex, sizeof (hex), "1/%x", PATH_MAX + 1);
> +    SELF_CHECK (parse (hex) == -1);
> +  }
> +
> +  /* Invalid: value well above PATH_MAX (INT_MAX as a stress case).  */
> +  SELF_CHECK (parse ("1/7fffffff") == -1);
> +
> +  /* Invalid: negative length.  */
> +  SELF_CHECK (parse ("1/-1") == -1);
> +
> +  /* Invalid: missing '/' separator.  */
> +  SELF_CHECK (parse ("deadbeef") == -1);
> +}
> +
> +} /* namespace selftests */
> +
> +#endif /* GDB_SELF_TEST */
> +
>   void
>   initialize_remote_fileio (struct cmd_list_element **remote_set_cmdlist,
>   			  struct cmd_list_element **remote_show_cmdlist)
> @@ -1256,4 +1340,8 @@ initialize_remote_fileio (struct cmd_list_element **remote_set_cmdlist,
>   	   show_system_call_allowed,
>   	   _("Show if the host system(3) call is allowed for the target."),
>   	   remote_show_cmdlist);
> +#if GDB_SELF_TEST
> +  selftests::register_test ("remote_fileio_extract_ptr_w_len",
> +			    selftests::test_remote_fileio_extract_ptr_w_len);
> +#endif
>   }


  reply	other threads:[~2026-06-30 21:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-30  9:52 Luis Machado
2026-06-30 21:07 ` Guinevere Larsen [this message]
2026-07-01 13:03 ` Pedro Alves
2026-07-01 15:08   ` Machado, Luis
2026-07-01 15:07 ` [PATCH v2] gdb: replace alloca with gdb::unique_xmalloc_ptr " Luis Machado
2026-07-01 18:20   ` Pedro Alves
2026-07-01 19:01     ` Machado, Luis
2026-07-01 19:01 ` [PATCH v3] " Luis Machado
2026-07-01 19:08   ` Pedro Alves
2026-07-03  8:35     ` Luis

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=07986ffa-b589-4661-a108-d6b27cf1a198@redhat.com \
    --to=guinevere@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=luis.machado@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox