gdb: fix getting range of flexible array member in Python
authorSimon Marchi <simon.marchi@polymtl.ca>
Thu, 22 Apr 2021 19:01:28 +0000 (15:01 -0400)
committerSimon Marchi <simon.marchi@polymtl.ca>
Thu, 22 Apr 2021 19:01:28 +0000 (15:01 -0400)
As reported in bug 27757, we get an internal error when doing:

    $ cat test.c
    struct foo {
        int len;
        int items[];
    };

    struct foo *p;

    int main() {
        return 0;
    }
    $ gcc test.c -g -O0 -o test
    $ ./gdb -q -nx --data-directory=data-directory ./test -ex 'python gdb.parse_and_eval("p").type.target()["items"].type.range()'
    Reading symbols from ./test...
    /home/simark/src/binutils-gdb/gdb/gdbtypes.h:435: internal-error: LONGEST dynamic_prop::const_val() const: Assertion `m_kind == PROP_CONST' failed.
    A problem internal to GDB has been detected,
    further debugging may prove unreliable.
    Quit this debugging session? (y or n)

This is because the Python code (typy_range) blindly reads the high
bound of the type of `items` as a constant value.  Since it is a
flexible array member, it has no high bound, the property is undefined.
Since commit 8c2e4e0689 ("gdb: add accessors to struct dynamic_prop"),
the getters check that you are not getting a property value of the wrong
kind, so this causes a failed assertion.

Fix it by checking if the property is indeed a constant value before
accessing it as such.  Otherwise, use 0.  This restores the previous GDB
behavior: because the structure was zero-initialized, this is what was
returned before.  But now this behavior is explicit and not accidental.

Add a test, gdb.python/flexible-array-member.exp, that is derived from
gdb.base/flexible-array-member.exp.  It tests the same things, but
through the Python API.  It also specifically tests getting the range
from the various kinds of flexible array member types (AFAIK it wasn't
possible to do the equivalent through the CLI).

gdb/ChangeLog:

PR gdb/27757
* python/py-type.c (typy_range): Check that bounds are constant
before accessing them as such.
* guile/scm-type.c (gdbscm_type_range): Likewise.

gdb/testsuite/ChangeLog:

PR gdb/27757
* gdb.python/flexible-array-member.c: New test.
* gdb.python/flexible-array-member.exp: New test.
* gdb.guile/scm-type.exp (test_range): Add test for flexible
array member.
* gdb.guile/scm-type.c (struct flex_member): New.
(main): Use it.

Change-Id: Ibef92ee5fd871ecb7c791db2a788f203dff2b841

gdb/ChangeLog
gdb/guile/scm-type.c
gdb/python/py-type.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.guile/scm-type.c
gdb/testsuite/gdb.guile/scm-type.exp
gdb/testsuite/gdb.python/flexible-array-member.c [new file with mode: 0644]
gdb/testsuite/gdb.python/flexible-array-member.exp [new file with mode: 0644]

index d9d0f85135f9327edd2425b5a3e608a3b56dcad8..67de5ca80bb34e071a7106a527841fddec2022aa 100644 (file)
@@ -1,3 +1,10 @@
+2021-04-22  Simon Marchi  <simon.marchi@polymtl.ca>
+
+       PR gdb/27757
+       * python/py-type.c (typy_range): Check that bounds are constant
+       before accessing them as such.
+       * guile/scm-type.c (gdbscm_type_range): Likewise.
+
 2021-04-22  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
 
        * Makefile.in (COMMON_SFILES): Remove continuations.c.
index dd45d8e8b35c0cb6ad5413dc7a2c9bd7db110c71..11693844edcb85b8fa47cfd9a0e6dd6f05666264 100644 (file)
@@ -823,8 +823,15 @@ gdbscm_type_range (SCM self)
     case TYPE_CODE_ARRAY:
     case TYPE_CODE_STRING:
     case TYPE_CODE_RANGE:
-      low = type->bounds ()->low.const_val ();
-      high = type->bounds ()->high.const_val ();
+      if (type->bounds ()->low.kind () == PROP_CONST)
+       low = type->bounds ()->low.const_val ();
+      else
+       low = 0;
+
+      if (type->bounds ()->high.kind () == PROP_CONST)
+       high = type->bounds ()->high.const_val ();
+      else
+       high = 0;
       break;
     }
 
index bf707078841fedf9065cc73af25759efb3bdd05f..148e4a6aa3a0987bb225c2ca375cd0a7669b3bce 100644 (file)
@@ -592,8 +592,15 @@ typy_range (PyObject *self, PyObject *args)
     case TYPE_CODE_ARRAY:
     case TYPE_CODE_STRING:
     case TYPE_CODE_RANGE:
-      low = type->bounds ()->low.const_val ();
-      high = type->bounds ()->high.const_val ();;
+      if (type->bounds ()->low.kind () == PROP_CONST)
+       low = type->bounds ()->low.const_val ();
+      else
+       low = 0;
+
+      if (type->bounds ()->high.kind () == PROP_CONST)
+       high = type->bounds ()->high.const_val ();
+      else
+       high = 0;
       break;
     }
 
index 3099cbacac33b2053307b64669f03a519053d1f4..9730658299bf16ec15ba87e46d6bd1701a5849c4 100644 (file)
@@ -1,3 +1,13 @@
+2021-04-22  Simon Marchi  <simon.marchi@polymtl.ca>
+
+       PR gdb/27757
+       * gdb.python/flexible-array-member.c: New test.
+       * gdb.python/flexible-array-member.exp: New test.
+       * gdb.guile/scm-type.exp (test_range): Add test for flexible
+       array member.
+       * gdb.guile/scm-type.c (struct flex_member): New.
+       (main): Use it.
+
 2021-04-22  Tom Tromey  <tom@tromey.com>
 
        * gdb.rust/modules.exp: Add checks for syntax errors.
index 782b8fe70cf9eb08cc898bcae180d25918adbad0..64f5f02efedcf2818756f8834a7bbd490389da82 100644 (file)
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#include <stdlib.h>
+
 struct s
 {
   int a;
@@ -53,6 +55,12 @@ enum E
 struct s vec_data_1 = {1, 1};
 struct s vec_data_2 = {1, 2};
 
+struct flex_member
+{
+  int n;
+  int items[];
+};
+
 int
 main ()
 {
@@ -72,6 +80,10 @@ main ()
   st.b = 5;
 
   e = v2;
+
+  struct flex_member *f = (struct flex_member *) malloc (100);
+  f->items[0] = 111;
+  f->items[1] = 222;
   
   return 0;      /* break to inspect struct and array.  */
 }
index 8778cdb36cb1e24352ad5264bb9f717d7fc3a288..517c99f8369d69b6b8b7304f665f8b4489208fdc 100644 (file)
@@ -261,6 +261,18 @@ proc test_range {} {
                "ERROR: .*: Wrong type argument in position 1 \\(expecting ranged type\\): .*" \
                "check range for non ranged type"
        }
+
+       with_test_prefix "on flexible array member" {
+           gdb_scm_test_silent_cmd "print f" "print value (f)"
+           gdb_scm_test_silent_cmd "guile (define f (history-ref 0))" \
+               "get value (f) from history"
+           gdb_test "guile (print (type-range (field-type (type-field (value-type (value-dereference f)) \"items\"))))" \
+               "= \\(0 0\\)"
+           gdb_test "guile (print (value-subscript (value-field (value-dereference f) \"items\") 0))" \
+               "= 111"
+           gdb_test "guile (print (value-subscript (value-field (value-dereference f) \"items\") 1))" \
+               "= 222"
+       }
     }
 }
 
diff --git a/gdb/testsuite/gdb.python/flexible-array-member.c b/gdb/testsuite/gdb.python/flexible-array-member.c
new file mode 100644 (file)
index 0000000..79815e2
--- /dev/null
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020-2021 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/>.  */
+
+#include <stdlib.h>
+
+struct no_size
+{
+  int n;
+  int items[];
+};
+
+struct zero_size
+{
+  int n;
+  int items[0];
+};
+
+struct zero_size_only
+{
+  int items[0];
+};
+
+struct no_size *ns;
+struct zero_size *zs;
+struct zero_size_only *zso;
+
+static void
+break_here (void)
+{
+}
+
+int
+main (void)
+{
+  ns = (struct no_size *) malloc (sizeof (*ns) + 3 * sizeof (int));
+  zs = (struct zero_size *) malloc (sizeof (*zs) + 3 * sizeof (int));
+  zso = (struct zero_size_only *) malloc (sizeof (*zso) + 3 * sizeof (int));
+
+  ns->n = 3;
+  ns->items[0] = 101;
+  ns->items[1] = 102;
+  ns->items[2] = 103;
+
+  zs->n = 3;
+  zs->items[0] = 201;
+  zs->items[1] = 202;
+  zs->items[2] = 203;
+
+  zso->items[0] = 301;
+  zso->items[1] = 302;
+  zso->items[2] = 303;
+
+  break_here ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/flexible-array-member.exp b/gdb/testsuite/gdb.python/flexible-array-member.exp
new file mode 100644 (file)
index 0000000..3739c9a
--- /dev/null
@@ -0,0 +1,81 @@
+# Copyright 2020-2021 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/>.
+
+# Test getting the range of flexible array members in Python.
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" \
+       ${testfile} ${srcfile}] } {
+    return
+}
+
+if { ![runto break_here] } {
+    untested "could not run to break_here"
+    return
+}
+
+# The various cases are:
+#
+#  - ns: flexible array member with no size
+#  - zs: flexible array member with size 0 (GNU C extension that predates the
+#    standardization of the feature, but widely supported)
+#  - zso: zero-size only, a corner case where the array is the sole member of
+#    the structure
+
+gdb_test "python ns = gdb.parse_and_eval('ns').dereference()"
+gdb_test "python zs = gdb.parse_and_eval('zs').dereference()"
+gdb_test "python zso = gdb.parse_and_eval('zso').dereference()"
+
+# Print the whole structure.
+
+gdb_test "python print(ns)" "{n = 3, items = $hex}"
+gdb_test "python print(zs)" "{n = 3, items = $hex}"
+gdb_test "python print(zso)" "{items = $hex}"
+
+# Print all items.
+
+gdb_test "python print(ns\['items'\])" "$hex"
+gdb_test "python print(ns\['items'\]\[0\])" "101"
+gdb_test "python print(ns\['items'\]\[1\])" "102"
+gdb_test "python print(ns\['items'\]\[2\])" "103"
+
+gdb_test "python print(zs\['items'\])" "$hex"
+gdb_test "python print(zs\['items'\]\[0\])" "201"
+gdb_test "python print(zs\['items'\]\[1\])" "202"
+gdb_test "python print(zs\['items'\]\[2\])" "203"
+
+gdb_test "python print(zso\['items'\])" "$hex"
+gdb_test "python print(zso\['items'\]\[0\])" "301"
+gdb_test "python print(zso\['items'\]\[1\])" "302"
+gdb_test "python print(zso\['items'\]\[2\])" "303"
+
+# Check taking the address of array elements (how PR 28675 was originally
+# reported).
+
+gdb_test "python print(ns\['items'\] == ns\['items'\]\[0\].address)" "True"
+gdb_test "python print(ns\['items'\]\[0\].address + 1 == ns\['items'\]\[1\].address)" "True"
+gdb_test "python print(zs\['items'\] == zs\['items'\]\[0\].address)" "True"
+gdb_test "python print(zs\['items'\]\[0\].address + 1 == zs\['items'\]\[1\].address)" "True"
+gdb_test "python print(zso\['items'\] == zso\['items'\]\[0\].address)" "True"
+gdb_test "python print(zso\['items'\]\[0\].address + 1 == zso\['items'\]\[1\].address)" "True"
+
+# Verify the range attribute.  It looks a bit inconsistent that the high bound
+# is sometimes 0, sometimes -1, but that's what GDB produces today, so that's
+# what we test.
+
+gdb_test "python print(ns\['items'\].type.range())" "\\(0, 0\\)"
+gdb_test "python print(zs\['items'\].type.range())" "\\(0, -1\\)"
+gdb_test "python print(zso\['items'\].type.range())" "\\(0, -1\\)"