Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Simon Marchi <simark@simark.ca>
To: Jielun Wu <firmiana402@gmail.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH][PR gdb/34239][PR gdb/34299] gdb: Check bounds before reading DWARF expression operands
Date: Thu, 25 Jun 2026 12:36:15 -0400	[thread overview]
Message-ID: <4c790bb0-a1cb-454c-8084-c6e2cfe8e3c9@simark.ca> (raw)
In-Reply-To: <20260618180158.2893540-1-firmiana402@gmail.com>



On 2026-06-18 14:01, Jielun Wu wrote:
> Some DWARF expression opcodes read fixed-width operands or block payloads
> from the expression buffer before checking that the bytes are available.
> With a malformed expression, this can read past the end of the expression
> buffer.
> 
> Add helpers that validate fixed-width integer reads and block payload
> skips, and use them in dwarf_expr_context::execute_stack_op.  Also guard
> the nested DW_OP_entry_value parser before reading the following opcode.
> 
> Add a DWARF assembler test that exercises truncated operands for these
> opcodes.
> 
> Tested on x86_64-linux.

I noted a few comments below.

> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34239
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34299
> ---
>  gdb/dwarf2/expr.c                             | 166 +++++++++------
>  .../gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp  | 190 ++++++++++++++++++
>  2 files changed, 300 insertions(+), 56 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index c71d4725b03..ead918388a4 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -1370,7 +1370,45 @@ safe_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
>      error (_("DWARF expression error: ran off end of buffer reading leb128 value"));
>    return buf;
>  }
> -\f
> +
> +/* Helper to skip BYTES bytes or throw an error.  */

Please precise the comment, "throw an error" in what situation?

> +
> +static const gdb_byte *
> +safe_skip_bytes (const gdb_byte *buf, const gdb_byte *buf_end,
> +		 ULONGEST bytes)
> +{
> +  if (buf > buf_end || bytes > (ULONGEST) (buf_end - buf))

I would make the "buf > buf_end" condition an assert:

  gdb_assert (buf <= buf_end);

I think that if "buf > buf_end" happens, something would have went wrong
earlier.

> +    error (_("DWARF expression error: ran off end of buffer reading bytes"));
> +  return buf + bytes;
> +}
> +
> +/* Helper to read a fixed-width unsigned integer or throw an error.  */
> +
> +static const gdb_byte *
> +safe_read_unsigned_integer (const gdb_byte *buf, const gdb_byte *buf_end,
> +			    int len, bfd_endian byte_order, uint64_t *r)

Make `r` a reference.

You appear to use this function most of the time like this:

	  op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1,
					       byte_order, &uoffset);
	  result = uoffset;


where `result` is a ULONGEST and uoffset a uint64_t.  If you made `r` a
pointer or reference to ULONGEST, wouldn't it make things simpler?  You
could just do:

	  op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1,
					       byte_order, result);

... and avoid the subsequent assignment.

But it is sometimes used to read into other types, like cu_offset:

	    op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4,
						 byte_order, &uoffset);
	    kind_u.param_cu_off = (cu_offset) uoffset;

So we could perhaps make the function templated, like this?

  template <typename T>
  static const gdb_byte *
  safe_read_unsigned_integer (const gdb_byte *buf, const gdb_byte *buf_end,
  			    int len, bfd_endian byte_order, T &r)
  {
    gdb_assert (len >= 0);
    gdb_assert (len <= sizeof (T));
  
    const gdb_byte *data = buf;
    buf = safe_skip_bytes (buf, buf_end, len);
    r = static_cast<T> (extract_unsigned_integer (data, len, byte_order));
    return buf;
  }

Then you'll be able to read the integer into whatever destination
integer type directly:

	op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4, byte_order,
						 &kind_u.param_cu_off);

> +{
> +  gdb_assert (len >= 0);

I would add an assert at len is <= the size of the destination type, as
shown above in my example.

> +  const gdb_byte *data = buf;
> +  buf = safe_skip_bytes (buf, buf_end, len);
> +  *r = extract_unsigned_integer (data, len, byte_order);
> +  return buf;
> +}
> +
> +/* Helper to read a fixed-width signed integer or throw an error.  */
> +
> +static const gdb_byte *
> +safe_read_signed_integer (const gdb_byte *buf, const gdb_byte *buf_end,
> +			  int len, bfd_endian byte_order, int64_t *r)
> +{
> +  gdb_assert (len >= 0);
> +
> +  const gdb_byte *data = buf;
> +  buf = safe_skip_bytes (buf, buf_end, len);
> +  *r = extract_signed_integer (data, len, byte_order);
> +  return buf;

Same comments as above.

>  /* Check that the current operator is either at the end of an
>     expression, or that it is followed by a composition operator or by
> @@ -1478,7 +1516,7 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view<const gdb_byte> block,
>        if (buf == NULL)
>  	return -1;
>        if ((int) dwarf_reg != dwarf_reg)
> -       return -1;
> +	return -1;

This is an unrelated whitespace change, I will push an obvious patch to
fix it.

>      }
>    else
>      return -1;
> @@ -1488,6 +1526,8 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view<const gdb_byte> block,
>      return -1;
>    if (offset != 0)
>      return -1;
> +  if (buf >= buf_end)
> +    return -1;

Can this really happen?  It seems to me like gdb_read_sleb128 guarantees
that it will not read past buf_end.  So it's perhaps possible that `buf
== buf_end`, but not `buf > buf_end`.  If so, I would do:

  gdb_assert (buf <= buf_end);

  if (buf == buf_end)
    return -1;

It should be possible to write "selftest" for
dwarf_block_to_dwarf_reg_deref (and dwarf_block_to_dwarf_reg), they look
like pure functions.  Can you add that?  Look at dwarf2/loc.c for
example to see how selftests are registered, we would do the same in
dwarf2/expr.c.

>    if (*buf == DW_OP_deref)
>      {
> @@ -1498,7 +1538,7 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view<const gdb_byte> block,
>      {
>        buf++;
>        if (buf >= buf_end)
> -       return -1;
> +	return -1;

Same as above, I'll fix it in an obvious patch.

> @@ -2016,7 +2066,13 @@ dwarf_expr_context::execute_stack_op (gdb::array_view<const gdb_byte> expr)
>  	case DW_OP_deref_type:
>  	case DW_OP_GNU_deref_type:
>  	  {
> -	    int addr_size = (op == DW_OP_deref ? this->m_addr_size : *op_ptr++);
> +	    int addr_size = this->m_addr_size;
> +	    if (op != DW_OP_deref)
> +	      {
> +		op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1,
> +						     byte_order, &uoffset);
> +		addr_size = uoffset;
> +	      }

Please write it with the two branches like this:

	    int addr_size;

	    if (op == DW_OP_deref)
	       addr_size = = this->m_addr_size;
	    else
	      {
		op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1,
						     byte_order, &uoffset);
		addr_size = uoffset;
	      }

Add an empty line after the curly brace that closes the scope.

Simon

  parent reply	other threads:[~2026-06-25 16:36 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-18 18:01 Jielun Wu
2026-06-23 18:56 ` Guinevere Larsen
2026-06-26  8:31   ` Firmiana
2026-06-26 14:30     ` Simon Marchi
2026-06-25 15:23 ` Simon Marchi
2026-06-25 16:36 ` Simon Marchi [this message]
2026-06-26  7:34   ` Firmiana
2026-06-26 14:23     ` Simon Marchi

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=4c790bb0-a1cb-454c-8084-c6e2cfe8e3c9@simark.ca \
    --to=simark@simark.ca \
    --cc=firmiana402@gmail.com \
    --cc=gdb-patches@sourceware.org \
    /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