aboutsummaryrefslogtreecommitdiff
path: root/gdb/value.c
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@embecosm.com>2023-02-10 23:49:19 +0000
committerMaciej W. Rozycki <macro@embecosm.com>2023-02-10 23:49:19 +0000
commitaaab5fce4f25e0d3b1a85edc7d6ac8f7d06f599c (patch)
tree27e8bf3de78fc774e0318806087240c8dffd86ac /gdb/value.c
parent4f82620cc9fd1ef630b6b732a9d939d4e529a07f (diff)
downloadbinutils-aaab5fce4f25e0d3b1a85edc7d6ac8f7d06f599c.zip
binutils-aaab5fce4f25e0d3b1a85edc7d6ac8f7d06f599c.tar.gz
binutils-aaab5fce4f25e0d3b1a85edc7d6ac8f7d06f599c.tar.bz2
GDB: Only make data actually retrieved into value history available
While it makes sense to allow accessing out-of-bounds elements in the debuggee and see whatever there might happen to be there in memory (we are a debugger and not a programming rules enforcement facility and we want to make people's life easier in chasing bugs), e.g.: (gdb) print one_hundred[-1] $1 = 0 (gdb) print one_hundred[100] $2 = 0 (gdb) we shouldn't really pretend that we have any meaningful data around values recorded in history (what these commands really retrieve are current debuggee memory contents outside the original data accessed, really confusing in my opinion). Mark values recorded in history as such then and verify accesses to be in-range for them: (gdb) print one_hundred[-1] $1 = <unavailable> (gdb) print one_hundred[100] $2 = <unavailable> Add a suitable test case, which also covers integer overflows in data location calculation. Approved-By: Tom Tromey <tom@tromey.com>
Diffstat (limited to 'gdb/value.c')
-rw-r--r--gdb/value.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/gdb/value.c b/gdb/value.c
index 57625b0..fbdb079 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -185,6 +185,7 @@ struct value
initialized (1),
stack (0),
is_zero (false),
+ in_history (false),
type (type_),
enclosing_type (type_)
{
@@ -239,6 +240,9 @@ struct value
otherwise. */
bool is_zero : 1;
+ /* True if this a value recorded in value history; false otherwise. */
+ bool in_history : 1;
+
/* Location of value (if lval). */
union
{
@@ -385,7 +389,13 @@ value_bits_available (const struct value *value,
{
gdb_assert (!value->lazy);
- return !ranges_contain (value->unavailable, offset, length);
+ /* Don't pretend we have anything available there in the history beyond
+ the boundaries of the value recorded. It's not like inferior memory
+ where there is actual stuff underneath. */
+ ULONGEST val_len = TARGET_CHAR_BIT * value_enclosing_type (value)->length ();
+ return !((value->in_history
+ && (offset < 0 || offset + length > val_len))
+ || ranges_contain (value->unavailable, offset, length));
}
int
@@ -1797,6 +1807,7 @@ value_copy (const value *arg)
val->modifiable = arg->modifiable;
val->stack = arg->stack;
val->is_zero = arg->is_zero;
+ val->in_history = arg->in_history;
val->initialized = arg->initialized;
val->unavailable = arg->unavailable;
val->optimized_out = arg->optimized_out;
@@ -1950,6 +1961,10 @@ record_latest_value (struct value *val)
a value on the value history never changes. */
if (value_lazy (val))
value_fetch_lazy (val);
+
+ /* Mark the value as recorded in the history for the availability check. */
+ val->in_history = true;
+
/* We preserve VALUE_LVAL so that the user can find out where it was fetched
from. This is a bit dubious, because then *&$1 does not just return $1
but the current contents of that location. c'est la vie... */