Re: [PATCH 10/13] gdbsupport: add xstrcpy

Andrew Burgess <[email protected]>
Newsgroups gmane.comp.gdb.patches,gmane.comp.gnu.binutils
Message-ID <[email protected]>
Simon Marchi <[email protected]> writes:

> Add xstrcpy, a "safe" alternative to strcpy.  It works like strcpy, but
> accepts the size of the destination buffer, and asserts that the string
> fits in it.
>
> Return the number of characters copied, so that it's possible to easily
> chain calls like this:
>
>   p += xstrcpy (p, end - p, ",C");

Don't functions that take a buffer size usually include an 'n' in the
name.  That seems to be true for libc, but also throughout GDB.  Would
it not be a good idea to adopt that here too.

This isn't exactly strncpy, but it seems similar.  While strncpy can
result in a non-null terminated output string, this "xstrncpy" asserts
that the source fits into the output buffer without being truncated.
But otherwise, it's the same function I think?

Thanks,
Andrew


>
> Context: I want to replace some code that uses strcpy and strcat to
> build strings with something that has bound checks.  We already have
> xsnprintf, but sometimes we want to add fixed strings, so xsnprintf is a
> bit overkill.  xstrcpy is essentially xsnprintf but without the
> formatting.
>
> I looked around and changed a bunch of calls to xsnprintf that can be
> replaced one for one with xstrcpy.
>
> Change-Id: Icff6978b7431581732184d045c6ec047e2c78bcb
> ---
>  gdb/fbsd-nat.c                         |  2 +-
>  gdb/nat/netbsd-nat.c                   |  2 +-
>  gdb/remote.c                           | 67 ++++++++++++--------------
>  gdb/unittests/common-utils-selftests.c | 29 +++++++++++
>  gdbsupport/agent.cc                    |  2 +-
>  gdbsupport/common-utils.cc             | 13 +++++
>  gdbsupport/common-utils.h              |  9 ++++
>  gdbsupport/ptid.cc                     |  4 +-
>  8 files changed, 88 insertions(+), 40 deletions(-)
>
> diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
> index cf59ae21efc0..2c914c0e75be 100644
> --- a/gdb/fbsd-nat.c
> +++ b/gdb/fbsd-nat.c
> @@ -897,7 +897,7 @@ fbsd_nat_target::thread_name (struct thread_info *thr)
>      return nullptr;
>    if (streq (kp.ki_comm, pl.pl_tdname))
>      return NULL;
> -  xsnprintf (buf, sizeof buf, "%s", pl.pl_tdname);
> +  xstrcpy (buf, sizeof buf, pl.pl_tdname);
>    return buf;
>  }
>  #endif
> diff --git a/gdb/nat/netbsd-nat.c b/gdb/nat/netbsd-nat.c
> index 2fe2889d7129..d88dd05cbf57 100644
> --- a/gdb/nat/netbsd-nat.c
> +++ b/gdb/nat/netbsd-nat.c
> @@ -135,7 +135,7 @@ thread_name (ptid_t ptid)
>        {
>  	if (kl->l_lid == lwp)
>  	  {
> -	    xsnprintf (buf, sizeof buf, "%s", kl->l_name);
> +	    xstrcpy (buf, sizeof buf, kl->l_name);
>  	    return true;
>  	  }
>  	return false;
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 194c4cbd9bb0..3d38a9c7c8c9 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -3045,7 +3045,7 @@ remote_target::remote_query_attached (int pid)
>    if (m_features.remote_multi_process_p ())
>      xsnprintf (rs->buf.data (), size, "qAttached:%x", pid);
>    else
> -    xsnprintf (rs->buf.data (), size, "qAttached");
> +    xstrcpy (rs->buf.data (), size, "qAttached");
>  
>    putpkt (rs->buf);
>    getpkt (&rs->buf);
> @@ -3509,11 +3509,11 @@ remote_target::set_thread (ptid_t ptid, int gen)
>    *buf++ = 'H';
>    *buf++ = gen ? 'g' : 'c';
>    if (ptid == magic_null_ptid)
> -    xsnprintf (buf, endbuf - buf, "0");
> +    xstrcpy (buf, endbuf - buf, "0");
>    else if (ptid == any_thread_ptid)
> -    xsnprintf (buf, endbuf - buf, "0");
> +    xstrcpy (buf, endbuf - buf, "0");
>    else if (ptid == minus_one_ptid)
> -    xsnprintf (buf, endbuf - buf, "-1");
> +    xstrcpy (buf, endbuf - buf, "-1");
>    else
>      write_ptid (buf, endbuf, ptid);
>    putpkt (rs->buf);
> @@ -4633,8 +4633,7 @@ remote_target::extra_thread_info (thread_info *tp)
>        char *b = rs->buf.data ();
>        char *endb = b + get_remote_packet_size ();
>  
> -      xsnprintf (b, endb - b, "qThreadExtraInfo,");
> -      b += strlen (b);
> +      b += xstrcpy (b, endb - b, "qThreadExtraInfo,");
>        write_ptid (b, endb, tp->ptid);
>  
>        putpkt (rs->buf);
> @@ -4682,8 +4681,7 @@ remote_target::static_tracepoint_marker_at (CORE_ADDR addr,
>    struct remote_state *rs = get_remote_state ();
>    char *p = rs->buf.data ();
>  
> -  xsnprintf (p, get_remote_packet_size (), "qTSTMat:");
> -  p += strlen (p);
> +  p += xstrcpy (p, get_remote_packet_size (), "qTSTMat:");
>    p += hexnumstr (p, addr);
>    putpkt (rs->buf);
>    getpkt (&rs->buf);
> @@ -7215,14 +7213,14 @@ remote_target::append_resumption (char *p, char *endp,
>  				   addr_size));
>  	}
>        else
> -	p += xsnprintf (p, endp - p, ";s");
> +	p += xstrcpy (p, endp - p, ";s");
>      }
>    else if (step)
> -    p += xsnprintf (p, endp - p, ";s");
> +    p += xstrcpy (p, endp - p, ";s");
>    else if (siggnal != GDB_SIGNAL_0)
>      p += xsnprintf (p, endp - p, ";C%02x", siggnal);
>    else
> -    p += xsnprintf (p, endp - p, ";c");
> +    p += xstrcpy (p, endp - p, ";c");
>  
>    if (m_features.remote_multi_process_p () && ptid.is_pid ())
>      {
> @@ -7231,12 +7229,12 @@ remote_target::append_resumption (char *p, char *endp,
>        /* All (-1) threads of process.  */
>        nptid = ptid_t (ptid.pid (), -1);
>  
> -      p += xsnprintf (p, endp - p, ":");
> +      p += xstrcpy (p, endp - p, ":");
>        p = write_ptid (p, endp, nptid);
>      }
>    else if (ptid != minus_one_ptid)
>      {
> -      p += xsnprintf (p, endp - p, ":");
> +      p += xstrcpy (p, endp - p, ":");
>        p = write_ptid (p, endp, ptid);
>      }
>  
> @@ -7357,7 +7355,7 @@ remote_target::remote_resume_with_vcont (ptid_t scope_ptid, int step,
>       about overflowing BUF.  Should there be a generic
>       "multi-part-packet" packet?  */
>  
> -  p += xsnprintf (p, endp - p, "vCont");
> +  p += xstrcpy (p, endp - p, "vCont");
>  
>    if (scope_ptid == magic_null_ptid)
>      {
> @@ -7535,7 +7533,7 @@ vcont_builder::restart ()
>  
>    m_p = rs->buf.data ();
>    m_endp = m_p + m_remote->get_remote_packet_size ();
> -  m_p += xsnprintf (m_p, m_endp - m_p, "vCont");
> +  m_p += xstrcpy (m_p, m_endp - m_p, "vCont");
>    m_first_action = m_p;
>  }
>  
> @@ -7889,12 +7887,12 @@ remote_target::remote_stop_ns (ptid_t ptid)
>  
>    if (ptid == minus_one_ptid
>        || (!m_features.remote_multi_process_p () && ptid.is_pid ()))
> -    p += xsnprintf (p, endp - p, "vCont;t");
> +    p += xstrcpy (p, endp - p, "vCont;t");
>    else
>      {
>        ptid_t nptid;
>  
> -      p += xsnprintf (p, endp - p, "vCont;t:");
> +      p += xstrcpy (p, endp - p, "vCont;t:");
>  
>        if (ptid.is_pid ())
>  	  /* All (-1) threads of process.  */
> @@ -7955,7 +7953,7 @@ remote_target::remote_interrupt_ns ()
>    char *p = rs->buf.data ();
>    char *endp = p + get_remote_packet_size ();
>  
> -  xsnprintf (p, endp - p, "vCtrlC");
> +  xstrcpy (p, endp - p, "vCtrlC");
>  
>    /* In non-stop, we get an immediate OK reply.  The stop reply will
>       come in asynchronously by notification.  */
> @@ -9313,7 +9311,7 @@ remote_target::send_g_packet ()
>    struct remote_state *rs = get_remote_state ();
>    int buf_len;
>  
> -  xsnprintf (rs->buf.data (), get_remote_packet_size (), "g");
> +  xstrcpy (rs->buf.data (), get_remote_packet_size (), "g");
>    putpkt (rs->buf);
>    getpkt (&rs->buf);
>    packet_result result = packet_check_result (rs->buf);
> @@ -11342,8 +11340,8 @@ remote_target::extended_remote_set_inferior_cwd ()
>  	{
>  	  /* An empty inferior_cwd means that the user wants us to
>  	     reset the remote server's inferior's cwd.  */
> -	  xsnprintf (rs->buf.data (), get_remote_packet_size (),
> -		     "QSetWorkingDir:");
> +	  xstrcpy (rs->buf.data (), get_remote_packet_size (),
> +		   "QSetWorkingDir:");
>  	}
>  
>        putpkt (rs->buf);
> @@ -11529,8 +11527,7 @@ remote_add_target_side_condition (struct gdbarch *gdbarch,
>      return 0;
>  
>    buf += strlen (buf);
> -  xsnprintf (buf, buf_end - buf, "%s", ";");
> -  buf++;
> +  buf += xstrcpy (buf, buf_end - buf, ";");
>  
>    /* Send conditions to the target.  */
>    for (agent_expr *aexpr : bp_tgt->conditions)
> @@ -14990,7 +14987,7 @@ remote_target::get_min_fast_tracepoint_insn_len ()
>    /* Make sure the remote is pointing at the right process.  */
>    set_general_process ();
>  
> -  xsnprintf (rs->buf.data (), get_remote_packet_size (), "qTMinFTPILen");
> +  xstrcpy (rs->buf.data (), get_remote_packet_size (), "qTMinFTPILen");
>    putpkt (rs->buf);
>    reply = remote_get_noisy_reply ();
>    if (*reply == '\0')
> @@ -15015,7 +15012,7 @@ remote_target::set_trace_buffer_size (LONGEST val)
>        char *endbuf = buf + get_remote_packet_size ();
>  
>        gdb_assert (val >= 0 || val == -1);
> -      buf += xsnprintf (buf, endbuf - buf, "QTBuffer:size:");
> +      buf += xstrcpy (buf, endbuf - buf, "QTBuffer:size:");
>        /* Send -1 as literal "-1" to avoid host size dependency.  */
>        if (val < 0)
>  	{
> @@ -15049,24 +15046,24 @@ remote_target::set_trace_notes (const char *user, const char *notes,
>    char *endbuf = buf + get_remote_packet_size ();
>    int nbytes;
>  
> -  buf += xsnprintf (buf, endbuf - buf, "QTNotes:");
> +  buf += xstrcpy (buf, endbuf - buf, "QTNotes:");
>    if (user)
>      {
> -      buf += xsnprintf (buf, endbuf - buf, "user:");
> +      buf += xstrcpy (buf, endbuf - buf, "user:");
>        nbytes = bin2hex ((gdb_byte *) user, buf, strlen (user));
>        buf += 2 * nbytes;
>        *buf++ = ';';
>      }
>    if (notes)
>      {
> -      buf += xsnprintf (buf, endbuf - buf, "notes:");
> +      buf += xstrcpy (buf, endbuf - buf, "notes:");
>        nbytes = bin2hex ((gdb_byte *) notes, buf, strlen (notes));
>        buf += 2 * nbytes;
>        *buf++ = ';';
>      }
>    if (stop_notes)
>      {
> -      buf += xsnprintf (buf, endbuf - buf, "tstop:");
> +      buf += xstrcpy (buf, endbuf - buf, "tstop:");
>        nbytes = bin2hex ((gdb_byte *) stop_notes, buf, strlen (stop_notes));
>        buf += 2 * nbytes;
>        *buf++ = ';';
> @@ -15661,8 +15658,8 @@ remote_target::enable_btrace (thread_info *tp,
>    ptid_t ptid = tp->ptid;
>    set_general_thread (ptid);
>  
> -  buf += xsnprintf (buf, endbuf - buf, "%s",
> -		    packets_descriptions[which_packet].name);
> +  buf += xstrcpy (buf, endbuf - buf,
> +		  packets_descriptions[which_packet].name);
>    putpkt (rs->buf);
>    getpkt (&rs->buf);
>  
> @@ -15702,8 +15699,8 @@ remote_target::disable_btrace (struct btrace_target_info *tinfo)
>  
>    set_general_thread (tinfo->ptid);
>  
> -  buf += xsnprintf (buf, endbuf - buf, "%s",
> -		    packets_descriptions[PACKET_Qbtrace_off].name);
> +  buf += xstrcpy (buf, endbuf - buf,
> +		  packets_descriptions[PACKET_Qbtrace_off].name);
>    putpkt (rs->buf);
>    getpkt (&rs->buf);
>  
> @@ -16074,8 +16071,8 @@ remote_target::commit_requested_thread_options ()
>        char *obuf_endp = obuf + max_options_size;
>  
>        *obuf_p++ = ';';
> -      obuf_p += xsnprintf (obuf_p, obuf_endp - obuf_p, "%s",
> -			   phex_nz (options));
> +      obuf_p += xstrcpy (obuf_p, obuf_endp - obuf_p,
> +			 phex_nz (options));
>        if (tp.ptid != magic_null_ptid)
>  	{
>  	  *obuf_p++ = ':';
> diff --git a/gdb/unittests/common-utils-selftests.c b/gdb/unittests/common-utils-selftests.c
> index eb9c83616f08..4940e412a41a 100644
> --- a/gdb/unittests/common-utils-selftests.c
> +++ b/gdb/unittests/common-utils-selftests.c
> @@ -125,6 +125,34 @@ string_vappendf_tests ()
>    test_appendf_func (string_vappendf_wrapper);
>  }
>  
> +static void
> +xstrcpy_tests ()
> +{
> +  char buf[8];
> +  char *p;
> +  char *end = buf + sizeof (buf);
> +
> +  memset (buf, 'x', sizeof (buf));
> +  p = buf;
> +  p += xstrcpy (p, end - p, "ab");
> +  SELF_CHECK (p == buf + 2);
> +  p += xstrcpy (p, end - p, "cd");
> +  SELF_CHECK (p == buf + 4);
> +  SELF_CHECK (strcmp (buf, "abcd") == 0);
> +
> +  /* A string of exactly SIZE - 1 characters fits.  */
> +  memset (buf, 'x', sizeof (buf));
> +  p = buf;
> +  SELF_CHECK (xstrcpy (p, end - p, "1234567") == 7);
> +  SELF_CHECK (strcmp (buf, "1234567") == 0);
> +
> +  /* An empty string is fine, even in a buffer of size 1.  */
> +  memset (buf, 'x', sizeof (buf));
> +  p = buf;
> +  SELF_CHECK (xstrcpy (p, 1, "") == 0);
> +  SELF_CHECK (strcmp (p, "") == 0);
> +}
> +
>  } /* namespace selftests */
>  
>  INIT_GDB_FILE (common_utils_selftests)
> @@ -134,4 +162,5 @@ INIT_GDB_FILE (common_utils_selftests)
>    selftests::register_test ("string_appendf", selftests::string_appendf_tests);
>    selftests::register_test ("string_vappendf",
>  			    selftests::string_vappendf_tests);
> +  selftests::register_test ("xstrcpy", selftests::xstrcpy_tests);
>  }
> diff --git a/gdbsupport/agent.cc b/gdbsupport/agent.cc
> index 44b6fcdcf5c7..2054815bfc8b 100644
> --- a/gdbsupport/agent.cc
> +++ b/gdbsupport/agent.cc
> @@ -154,7 +154,7 @@ gdb_connect_sync_socket (int pid)
>  
>    addr.sun_family = AF_UNIX;
>  
> -  res = xsnprintf (addr.sun_path, UNIX_PATH_MAX, "%s", path);
> +  res = xstrcpy (addr.sun_path, UNIX_PATH_MAX, path);
>    if (res >= UNIX_PATH_MAX)
>      {
>        warning (_("string overflow allocating socket name"));
> diff --git a/gdbsupport/common-utils.cc b/gdbsupport/common-utils.cc
> index f31699be13a1..4aeaaf99f787 100644
> --- a/gdbsupport/common-utils.cc
> +++ b/gdbsupport/common-utils.cc
> @@ -86,6 +86,19 @@ xsnprintf (char *str, size_t size, const char *format, ...)
>    return ret;
>  }
>  
> +/* See common-utils.h.  */
> +
> +int
> +xstrcpy (char *str, size_t size, const char *src)
> +{
> +  size_t len = strlen (src);
> +
> +  gdb_assert (len < size);
> +  memcpy (str, src, len + 1);
> +
> +  return len;
> +}
> +
>  /* See documentation in common-utils.h.  */
>  
>  std::string
> diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h
> index de83a715ac45..0c4dcb8efe86 100644
> --- a/gdbsupport/common-utils.h
> +++ b/gdbsupport/common-utils.h
> @@ -51,6 +51,15 @@ gdb::unique_xmalloc_ptr<char> xstrvprintf (const char *format, va_list ap)
>  int xsnprintf (char *str, size_t size, const char *format, ...)
>       ATTRIBUTE_PRINTF (3, 4);
>  
> +/* Like strcpy, but takes the size of the destination buffer STR as SIZE,
> +   and throws an error if SRC does not fit in it.
> +
> +   Return the number of characters copied, excluding the terminating null
> +   character.
> +
> +   This is equivalent to xsnprintf when no formatting is needed.  */
> +int xstrcpy (char *str, size_t size, const char *src);
> +
>  /* Returns a std::string built from a printf-style format string.  */
>  std::string string_printf (const char* fmt, ...)
>    ATTRIBUTE_PRINTF (1, 2);
> diff --git a/gdbsupport/ptid.cc b/gdbsupport/ptid.cc
> index 933e441f9b87..d5a5fce4c123 100644
> --- a/gdbsupport/ptid.cc
> +++ b/gdbsupport/ptid.cc
> @@ -38,12 +38,12 @@ ptid_t::to_rsp_string (bool multi) const
>    if (multi)
>      {
>        if (m_pid == -1)
> -	buf += xsnprintf (buf, endbuf - buf, "p-1.");
> +	buf += xstrcpy (buf, endbuf - buf, "p-1.");
>        else
>  	buf += xsnprintf (buf, endbuf - buf, "p%x.", (unsigned) m_pid);
>      }
>    if (m_lwp == -1)
> -    xsnprintf (buf, endbuf - buf, "-1");
> +    xstrcpy (buf, endbuf - buf, "-1");
>    else
>      xsnprintf (buf, endbuf - buf, "%lx", (unsigned long) m_lwp);
>  
> -- 
> 2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.