Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Cc: Simon Marchi <simark@simark.ca>,
	gdb-patches@sourceware.org, "Aktemur,
	Tankut Baris" <tankut.baris.aktemur@intel.com>,
	"Maciej W. Rozycki" <macro@orcam.me.uk>
Subject: Re: [PATCH 2/2] GDB: Use gdb::array_view for buffers used in register reading and unwinding
Date: Sat, 11 Jan 2025 17:01:37 +0100	[thread overview]
Message-ID: <28934663-54a6-47cf-8d30-dace9cde8d20@suse.de> (raw)
In-Reply-To: <87ikql8ibi.fsf@linaro.org>

On 1/11/25 16:46, Thiago Jung Bauermann wrote:
> 
> Hello Tom,
> 
> Tom de Vries <tdevries@suse.de> writes:
> 
>> On 1/10/25 23:28, Thiago Jung Bauermann wrote:
>>> +      gdb_assert (buffer.size () >= value->type ()->length ());
>>> +
>>
>> This causes a regression on s390x-linux for test-case gdb.base/return.exp:
>> ...
>> (gdb) PASS: gdb.base/return.exp: continue to return of -5
>> return 5^M
>> Make func2 return now? (y or n) y^M
>> /home/vries/gdb/src/gdb/frame.c:1207: internal-error: frame_register_unwind: Assertion
>> `buffer.size () >= value->type ()->length ()' failed.^M
>> A problem internal to GDB has been detected,^M
>> further debugging may prove unreliable.^M
>> ----- Backtrace -----^M
>> FAIL: gdb.base/return.exp: return value 5 (GDB internal error)
>> ...
>>
>> Before the commit, the test-case produces a fail, but doesn't assert:
>> ...
>> (gdb) PASS: gdb.base/return.exp: continue to return of -5
>> return 5^M
>> Make func2 return now? (y or n) y^M
>> value has been optimized out^M
>> (gdb) FAIL: gdb.base/return.exp: return value 5
>> ...
>>
>> Concretely, we're trying to read machine register r11, which according to the CFI is saved
>> in dwarf register 16:
>> ...
>>    DW_CFA_register: r11 in r16 (f0)
>> ...
>>
>> Dwarf register 16 corresponds to f0 / v0 according to the ABI, and since v0 is available,
>> v0 is picked by s390_dwarf_reg_to_regnum.
>>
>> The assert then fails because the buffer that should hold the value of 8 byte register
>> r11:
>> ...
>> (gdb) p buffer.size ()
>> $1 = 8
>> ...
>> is smaller than the size of register v0:
>> ...
>> (gdb) p value->type ()->length ()
>> $2 = 16
>> ...
>>
>> Removing the assert reverts back to previous behaviour.
> 
> Thank you for debugging the issue! So memcpy was overflowing the
> buffer. Nice to see the assert in action. :)
> 

Hi Thiago,

thanks for the quick review.

Not the memcpy, but the memset, because the optimized out branch was 
activated, but buffer overflow indeed.

>> Properly fixing this requires us to only look at the part that is relevant, copying the
>> value from there, and checking for optimized out and unavailable only there.
>>
>> This worked for s390x-linux:
>> ...
>> diff --git a/gdb/frame.c b/gdb/frame.c
>> index 10a32dcd896..02583857019 100644
>> --- a/gdb/frame.c
>> +++ b/gdb/frame.c
>> @@ -1193,8 +1193,14 @@ frame_register_unwind (const frame_info_ptr &next_frame, int
>> regnum,
>>
>>     gdb_assert (value != NULL);
>>
>> -  *optimizedp = value->optimized_out ();
>> -  *unavailablep = !value->entirely_available ();
>> +  if (value->lazy ())
>> +    value->fetch_lazy ();
>> +
>> +  *optimizedp
>> +    = value->bits_any_optimized_out (value->offset () * 8,
>> +				     buffer.size () * 8);
>> +  *unavailablep
>> +    = !value->bytes_available (value->offset (), buffer.size ());
>>     *lvalp = value->lval ();
>>     *addrp = value->address ();
>>     if (*lvalp == lval_register)
>> @@ -1204,13 +1210,17 @@ frame_register_unwind (const frame_info_ptr &next_frame, int
>> regnum,
>>
>>     if (!buffer.empty ())
>>       {
>> -      gdb_assert (buffer.size () >= value->type ()->length ());
>> +      gdb_assert (buffer.size ()
>> +		  <= value->type ()->length () - value->offset ());
> 
> It should be '>=' above.
> 

That doesn't work unfortunately:
- buffer.size () is 8
- value->type ()->length () is 16
- value->offset () == 0

So we'd have gdb_assert (8 >= 16).

FWIW, I've tried out a less intrusive fix which also works:
...
diff --git a/gdb/frame.c b/gdb/frame.c
index 10a32dcd896..96e0752888f 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1191,6 +1191,19 @@ frame_register_unwind (const frame_info_ptr 
&next_frame, int regnum,

    value = frame_unwind_register_value (next_frame, regnum);

+  frame_info_ptr this_frame = get_prev_frame (next_frame);
+  struct gdbarch *gdbarch = frame_unwind_arch (this_frame);
+  size_t reg_size = register_size (gdbarch, regnum);
+
+  if (value->type ()->length () > reg_size)
+    {
+      struct value *part_val
+	= value::allocate_register (this_frame, regnum);
+      value->contents_copy (part_val, 0, value->offset (), reg_size);
+      release_value (value);
+      value = part_val;
+    }
+
    gdb_assert (value != NULL);

    *optimizedp = value->optimized_out ();
...
but I have doubts whether reg_size is correct in case gdbarch changes 
between frames and register sizes are different.

Thanks,
- Tom

>>         if (!*optimizedp && !*unavailablep)
>> -	memcpy (buffer.data (), value->contents_all ().data (),
>> -		value->type ()->length ());
>> +	{
>> +	  auto value_part
>> +	    = value->contents_all ().slice (value->offset (), buffer.size ());
>> +	  memcpy (buffer.data (), value_part.data (), buffer.size ());
>> +	}
>>         else
>> -	memset (buffer.data (), 0, value->type ()->length ());
>> +	memset (buffer.data (), 0, buffer.size ());
>>       }
>>
>>     /* Dispose of the new value.  This prevents watchpoints from
>> ...
> 
> Thank you for the patch! With the fix in the assert comparison:
> 
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> 
> --
> Thiago


  reply	other threads:[~2025-01-11 16:02 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-10 16:43 [PATCH 0/2] Improvements to a few GDB frame unwind functions Thiago Jung Bauermann
2025-01-10 16:43 ` [PATCH 1/2] GDB: frame: Make VALUEP argument optional in frame_register_unwind Thiago Jung Bauermann
2025-01-10 17:02   ` Simon Marchi
2025-01-10 22:23     ` Thiago Jung Bauermann
2025-01-10 16:44 ` [PATCH 2/2] GDB: Use gdb::array_view for buffers used in register reading and unwinding Thiago Jung Bauermann
2025-01-10 17:45   ` Simon Marchi
2025-01-10 22:28     ` Thiago Jung Bauermann
2025-01-11 13:54       ` Tom de Vries
2025-01-11 15:46         ` Thiago Jung Bauermann
2025-01-11 16:01           ` Tom de Vries [this message]
2025-01-12  0:32             ` Thiago Jung Bauermann
2025-01-13 16:40               ` Tom de Vries

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=28934663-54a6-47cf-8d30-dace9cde8d20@suse.de \
    --to=tdevries@suse.de \
    --cc=gdb-patches@sourceware.org \
    --cc=macro@orcam.me.uk \
    --cc=simark@simark.ca \
    --cc=tankut.baris.aktemur@intel.com \
    --cc=thiago.bauermann@linaro.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