[PATCH 1/3] gdb: New set/show max-value-size command.

Andrew Burgess
2015-12-11T21:38:59Z

For languages with dynamic types, an incorrect program could result in
an incorrect, overly large type being associated with a value.
Currently, attempting to print such a variable will result in gdb
attempting to allocate an overly large buffer.

This could result in the allocation failing, and gdb terminating, or the
buffer might be allocated, but the machine on which gdb is being run
might struggle due to the high memory requirement of gdb.

A new user visible variable in gdb helps guard against such problems,
two new commands are available:

set max-value-size
show max-value-size

The 'max-value-size' is the maximum size in bytes that the contents of a
value may allocate. Any attempt to allocate a value with size greater
than this will result in an error. The default for this variable is
currently unlimited.

Setting the default to unlimited reduces the use of this variable a
little bit, if a user hits one of these rogue types without realising
it, then gdb will still allocate the large buffer, with all the problems
that this entails. The user will then be forced to probably restart
their gdb session, and then set the 'max-value-size' variable to guard
against future crashes.

However, it's not clear what a good default would actually be, given the
huge range of resources that are available on different machines. One
solution might be to introduce platform specific code that attempts to
figure out what memory resources are available on the machine running
gdb, we could then set the max-value-size to some percentage of the
available memory, while still defaulting to unlimited for those machines
where we can't figure out a good alternative. Such a feature is not
implemented in this commit, but could be added later.

gdb/ChangeLog:

* value.c (max_value_size): New variable.
(show_max_value_size): New function.
(check_type_length_before_alloc): New function.
(allocate_value_contents): Call check_type_length_before_alloc.
(set_value_enclosing_type): Likewise.
(_initialize_values): Add set/show handler for max-value-size.
* NEWS: Mention new set/show command.

gdb/doc/ChangeLog:

* gdb.texinfo (Value Sizes): New section.

gdb/testsuite/ChangeLog:

* gdb.base/max-value-size.c: New file.
* gdb.base/max-value-size.exp: New file.
---
gdb/ChangeLog | 11 +++++
gdb/NEWS | 6 +++
gdb/doc/ChangeLog | 4 ++
gdb/doc/gdb.texinfo | 35 +++++++++++++++
gdb/testsuite/ChangeLog | 5 +++
gdb/testsuite/gdb.base/max-value-size.c | 26 +++++++++++
gdb/testsuite/gdb.base/max-value-size.exp | 68 +++++++++++++++++++++++++++++
gdb/value.c | 71 +++++++++++++++++++++++++++++--
8 files changed, 222 insertions(+), 4 deletions(-)
create mode 100644 gdb/testsuite/gdb.base/max-value-size.c
create mode 100644 gdb/testsuite/gdb.base/max-value-size.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 03ae010..8e36907 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,14 @@
+2015-12-11 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * value.c (max_value_size): New variable.
+ (set_max_value_size): New function.
+ (show_max_value_size): New function.
+ (check_type_length_before_alloc): New function.
+ (allocate_value_contents): Call check_type_length_before_alloc.
+ (set_value_enclosing_type): Likewise.
+ (_initialize_values): Add set/show handler for max-value-size.
+ * NEWS: Mention new set/show command.
+
2015-12-10 Antoine Tremblay <antoine.tremblay@ericsson.com>

* linux-thread-db.c (find_new_threads_callback): Use record_thread.
diff --git a/gdb/NEWS b/gdb/NEWS
index 9ca7f49..2fb264a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -60,6 +60,12 @@ show ada print-signatures"
Control whether parameter types and return types are displayed in overloads
selection menus. It is activaled (@code{on}) by default.

+set max-value-size
+show max-value-size
+ Control the maximum size, in bytes, that GDB will allocate for value
+ contents. Prevent incorrect programs from causing GDB to allocate
+ overly large buffers. Default is unlimited.
+
* The "disassemble" command accepts a new modifier: /s.
It prints mixed source+disassembly like /m with two differences:
- disassembled instructions are now printed in program order, and
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index fc81d09..796e2cd 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@
+2015-12-11 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.texinfo (Value Sizes): New section.
+
2015-12-10 Pedro Alves <palves@redhat.com>

* gdb.texinfo (Threads): Replace warning with explanation
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index bb68e21..5d8b579 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -11592,6 +11592,41 @@ $1 = 1
$2 = (void *) 0x8049560
@end smallexample

+@node Value Sizes
+@section Value Sizes
+
+Whenever @value{GDBN} prints a value memory will be allocated within
+@value{GDBN} to hold the contents of the value. It is possible in
+some languages with dynamic typing systems, that an invalid program
+may indicate a value that is incorrectly large, this in turn may cause
+@value{GDBN} to try and allocate an overly large ammount of memory.
+
+@table @code
+@kindex set max-value-size
+@itemx set max-value-size @var{bytes}
+@itemx set max-value-size unlimited
+Set the maximum size, in bytes, that @value{GDBN} will allocate for
+the contents of a value to @var{bytes}. Any value whose contents
+require more than this number of bytes can't be displayed by
+@value{GDBN}, and trying to display the value will result in an error.
+
+Setting this variable does not effect values that have already been
+allocated within gdb, only future allocations.
+
+By default this variable is set to @var{unlimited}, meaning
+@value{GDBN} will always attempt to allocate space for the values
+contents.
+
+There's a minimum size that @code{max-value-size} can be set too in
+order that @value{GDBN} can still operate correctly. This varies by
+target, but will generally be around 8 bytes.
+
+@kindex show max-value-size
+@item show max-value-size
+Show the maximum size, in bytes, that @value{GDBN} will allocate for
+the contents of a value.
+@end table
+
@node Optimized Code
@chapter Debugging Optimized Code
@cindex optimized code, debugging
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ab7a324..93bb3bb 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2015-12-11 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.base/max-value-size.c: New file.
+ * gdb.base/max-value-size.exp: New file.
+
2015-12-10 Pedro Alves <palves@redhat.com>

* gdb.multi/base.exp: Remove stale "spaces" references.
diff --git a/gdb/testsuite/gdb.base/max-value-size.c b/gdb/testsuite/gdb.base/max-value-size.c
new file mode 100644
index 0000000..4d47280
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-value-size.c
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2015 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+char one;
+char ten [10];
+char one_hundred [100];
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.base/max-value-size.exp b/gdb/testsuite/gdb.base/max-value-size.exp
new file mode 100644
index 0000000..cb09ad8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-value-size.exp
@@ -0,0 +1,68 @@
+# Copyright 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile debug]} {
+ untested $testfile.exp
+ return -1
+}
+
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+gdb_test "show max-value-size" \
+ "Maximum value size is unlimited." \
+ "the initial value of max-value-size is unlimited"
+
+with_test_prefix "max-value-size is 'unlimited'" {
+ gdb_test "p/d one" " = 0"
+ gdb_test "p/d one_hundred" " = \\{0 <repeats 100 times>\\}"
+}
+
+# Check that setting it low does prevent values being allocated.
+gdb_test_no_output "set max-value-size 25"
+with_test_prefix "max-value-size is '25'" {
+ gdb_test "p/d one" " = 0"
+ gdb_test "p/d one_hundred" "value contents too large \\(100 bytes\\)"
+}
+
+# Check that we can't set the maximum size stupidly low.
+gdb_test "set max-value-size 1" \
+ "max-value-size set too low, increasing to \[0-9\]+ bytes"
+gdb_test "set max-value-size 0" \
+ "max-value-size set too low, increasing to \[0-9\]+ bytes"
+
+# Check we can set it to something "large", and then view our values.
+gdb_test_no_output "set max-value-size 200"
+gdb_test "show max-value-size" \
+ "Maximum value size is 200 bytes." \
+ "check that the value shows as 200 bytes"
+with_test_prefix "max-value-size is '200'" {
+ gdb_test "p/d one" " = 0"
+ gdb_test "p/d one_hundred" " = \\{0 <repeats 100 times>\\}"
+}
+
+# Check we can restore it too unlimited.
+gdb_test_no_output "set max-value-size unlimited"
+gdb_test "show max-value-size" \
+ "Maximum value size is unlimited." \
+ "check that the unlimited maximum restored correctly"
+with_test_prefix "max-value-size is 'unlimited' again" {
+ gdb_test "p/d one" " = 0"
+ gdb_test "p/d one_hundred" " = \\{0 <repeats 100 times>\\}"
+}
diff --git a/gdb/value.c b/gdb/value.c
index 91bf49e..9554333 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -955,13 +955,60 @@ allocate_value_lazy (struct type *type)
return val;
}

+/* The maximum size, in bytes, that the contents of a value might have.
+ Setting this to -1 indicates unlimited. Adjust this variable does not
+ invalidate already allocated values, only prevents future large values
+ being allocated. */
+
+static int max_value_size = -1;
+static void
+set_max_value_size (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ if (max_value_size > -1 && max_value_size < sizeof (LONGEST))
+ {
+ max_value_size = sizeof (LONGEST);
+ error (_("max-value-size set too low, increasing to %u bytes"),
+ ((unsigned int) max_value_size));
+ }
+}
+static void
+show_max_value_size (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ if (max_value_size == -1)
+ fprintf_filtered (file, _("Maximum value size is unlimited.\n"));
+ else
+ fprintf_filtered (file, _("Maximum value size is %d bytes.\n"),
+ max_value_size);
+}
+
+/* Called before we attempt to allocate or reallocate a buffer for the
+ contents of a value. TYPE is the type of the value for which we are
+ allocating the buffer. If the buffer is too large (based on the user
+ controllable setting) then throw an error. If this function returns
+ then we should attempt to allocate the buffer. */
+
+static void
+check_type_length_before_alloc (const struct type *type)
+{
+ int length = TYPE_LENGTH (type);
+ if (max_value_size > -1 && (length < 0 || length > max_value_size))
+ error (_("value contents too large (%u bytes)"),
+ ((unsigned int) length));
+}
+
/* Allocate the contents of VAL if it has not been allocated yet. */

static void
allocate_value_contents (struct value *val)
{
if (!val->contents)
- val->contents = (gdb_byte *) xzalloc (TYPE_LENGTH (val->enclosing_type));
+ {
+ check_type_length_before_alloc (val->enclosing_type);
+ val->contents =
+ (gdb_byte *) xzalloc (TYPE_LENGTH (val->enclosing_type));
+ }
}

/* Allocate a value and its contents for type TYPE. */
@@ -2986,9 +3033,12 @@ value_static_field (struct type *type, int fieldno)
void
set_value_enclosing_type (struct value *val, struct type *new_encl_type)
{
- if (TYPE_LENGTH (new_encl_type) > TYPE_LENGTH (value_enclosing_type (val)))
- val->contents =
- (gdb_byte *) xrealloc (val->contents, TYPE_LENGTH (new_encl_type));
+ if (TYPE_LENGTH (new_encl_type) > TYPE_LENGTH (value_enclosing_type (val)))
+ {
+ check_type_length_before_alloc (new_encl_type);
+ val->contents =
+ (gdb_byte *) xrealloc (val->contents, TYPE_LENGTH (new_encl_type));
+ }

val->enclosing_type = new_encl_type;
}
@@ -4013,4 +4063,17 @@ Check whether an expression is void.\n\
Usage: $_isvoid (expression)\n\
Return 1 if the expression is void, zero otherwise."),
isvoid_internal_fn, NULL);
+
+ add_setshow_zuinteger_unlimited_cmd ("max-value-size",
+ class_support, &max_value_size, _("\
+Set maximum sized value gdb will load from the inferior."), _("\
+Show maximum sized value gdb will load from the inferior."), _("\
+Use this to control the maximum size, in bytes, of a value that gdb\n\
+will load from the inferior. Setting this value to 'unlimited'\n\
+disables checking.\n\
+Setting this does not invalidate already allocated values, it only\n\
+prevents future values, larger than this size, from being allocated."),
+ set_max_value_size,
+ show_max_value_size,
+ &setlist, &showlist);
}
--
2.5.1