Bug 111629 - [14 Regression] ggc_common_finalize() corrupts global memory outsuide GTY(()) objects
Summary: [14 Regression] ggc_common_finalize() corrupts global memory outsuide GTY(())...
Status: RESOLVED DUPLICATE of bug 111505
Alias: None
Product: gcc
Classification: Unclassified
Component: other (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2023-09-28 13:26 UTC by Sergei Trofimovich
Modified: 2023-09-28 14:17 UTC (History)
4 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed:


Note You need to log in before you can comment on or make changes to this bug.
Description Sergei Trofimovich 2023-09-28 13:26:02 UTC
Initially I observed the bug as a gcc profiled bootstrap failure on r14-4300-g1fab05a885a308:

$ ~/dev/git/gcc/configure --disable-multilib --enable-languages=c,c++ 'CC=gcc -O2 -ggdb3' 'CXX=g++ -O2 -ggdb3'
$ make profiledbootstrap
configure: error: uint64_t or int64_t not found
$ touch a.c &&  /tmp/gb/./prev-gcc/xg++ -B/tmp/gb/./prev-gcc/ -c a.c
xg++: internal compiler error: Segmentation fault signal terminated program cc1plus

While it look slike a profile-related problem I think it's just a coincidence: it's a side-effect of gcov globals corruption exposed by `ggc_common_finalize()`.

`ggc_common_finalize()` is supposed to memset() GTY(()) globals, but its slightly out of bounds and corrupts gcov.


$ gdb --args /tmp/gb/./prev-gcc/cc1plus -quiet -v -iprefix /tmp/gb/prev-gcc/../lib/gcc/x86_64-pc-linux-gnu/14.0.0/ -isystem /tmp/gb/./prev-gcc/include -isystem /tmp/gb/./prev-gcc/include-fixed -D_GNU_SOURCE a.c -quiet -dumpbase a.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -o /run/user/1000/ccA6Gln1.s
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x000000000442ab26 in gcov_do_dump (list=0x5cab0e0, run_counted=0, mode=0) at /home/slyfox/dev/git/gcc/libgcc/libgcov-driver.c:690
690             for (unsigned i = 0; i < cinfo->num; i++)

(gdb) list
685         for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
686           {
687             const struct gcov_ctr_info *cinfo
688               = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];

(gdb) p gi_ptr->functions[f_ix]
$1 = (const gcov_fn_info * const) 0x0

 === This is a corruption: `gi_ptr->functions[f_ix]` are gcov.* global counters.  
 === They are never expected to be zero. Looking up place of corruption:

(gdb) p &gi_ptr->functions[f_ix]
$2 = (const gcov_fn_info * const *) 0x50c1540
(gdb) watch -l *(void**)0x50c1540
Hardware watchpoint 1: -location *(void**)0x50c1540
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Hardware watchpoint 1: -location *(void**)0x50c1540

Old value = (void *) 0x50c4080 <__gcov_._ZN10hash_tableI18subsumption_hasherLb0E11xcallocatorE26find_empty_slot_for_expandEj>
New value = (void *) 0x50c4000
__memset_avx2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S:328
328     ../sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S: No such file or directory.
(gdb) bt
#0  __memset_avx2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S:328
#1  0x0000000001933651 in ggc_common_finalize () at /home/slyfox/dev/git/gcc/gcc/ggc-common.cc:1312
#2  0x00000000020d7cf1 in toplev::finalize (this=this@entry=0x7fffffffaece) at /home/slyfox/dev/git/gcc/gcc/toplev.cc:2354
#3  0x0000000000c640b6 in main (argc=<optimized out>, argv=0x7fffffffaff8) at /home/slyfox/dev/git/gcc/gcc/main.cc:42
(gdb) fr 1
#1  0x0000000001933651 in ggc_common_finalize () at /home/slyfox/dev/git/gcc/gcc/ggc-common.cc:1312
1312          memset (rti->base, 0, rti->stride * rti->nelt);
(gdb) list
1307        for (rti = *rt; rti->base != NULL; rti++)
1308          memset (rti->base, 0, rti->stride * rti->nelt);
1310      for (rt = gt_ggc_rtab; *rt; rt++)
1311        for (rti = *rt; rti->base != NULL; rti++)
1312          memset (rti->base, 0, rti->stride * rti->nelt);
1314      for (rt = gt_pch_scalar_rtab; *rt; rt++)
1315        for (rti = *rt; rti->base != NULL; rti++)
1316          memset (rti->base, 0, rti->stride * rti->nelt);

 === What ggc are we destroying?

(gdb) p *rt
$3 = (const ggc_root_tab * const) 0x448e320 <gt_ggc_r_gt_cp_tree_h>
(gdb) p rti - *rt
$4 = 5

Fifth element of gt_ggc_r_gt_cp_tree_h:

From prev-gcc/gt-cp-tree.h:

/* Structures for the easy way to mark roots.
   In an array, terminated by having base == NULL.  */
struct ggc_root_tab {
  void *base;
  size_t nelt;
  size_t stride;
  gt_pointer_walker cb;
  gt_pointer_walker pchw;

EXPORTED_CONST struct ggc_root_tab gt_ggc_r_gt_cp_tree_h[] = {
  { // 4
    1 * (2) * (OVL_OP_MAX),
    sizeof (ovl_op_info[0][0]),
  { // 5
    1 * (2) * (OVL_OP_MAX),
    sizeof (ovl_op_info[0][0]),
    (gt_pointer_walker) &gt_ggc_m_S,
    (gt_pointer_walker) &gt_pch_n_S
  { //6
    1 * (2) * (OVL_OP_MAX),
    sizeof (ovl_op_info[0][0]),
    (gt_pointer_walker) &gt_ggc_m_S,
    (gt_pointer_walker) &gt_pch_n_S

  === Note how 'base' points not at the beginning of the whole 'ovl_op_info[0][0])' but at the GTY()) field itself within the struct:

struct GTY(()) ovl_op_info_t {
  /* The IDENTIFIER_NODE for the operator.  */
  tree identifier;
  /* The name of the operator.  */
  const char *name;
  /* The mangled name of the operator.  */
  const char *mangled_name;
  /* The (regular) tree code.  */
  enum tree_code tree_code : 16;
  /* The (compressed) operator code.  */
  enum ovl_op_code ovl_op_code : 8;
  /* The ovl_op_flags of the operator */
  unsigned flags : 8;

/* Overloaded operator info indexed by ass_op_p & ovl_op_code.  */
extern GTY(()) ovl_op_info_t ovl_op_info[2][OVL_OP_MAX];

 === Done.

I think `ggc_common_finalize()`'s intent is to memset() the whole struct, but it has no pointer to that struct. Sounds about right? What would be the best fix here? Just zero out pointers? Sounds a bit risky, but might be fine?

Compiler version:

$ /tmp/gb/./prev-gcc/xg++ -B/tmp/gb/./prev-gcc/ -v
Reading specs from /tmp/gb/./prev-gcc/specs
Target: x86_64-pc-linux-gnu
Configured with: /home/slyfox/dev/git/gcc/configure --disable-multilib --enable-languages=c,c++ CC='gcc -O2 -ggdb3' CXX='g++ -O2 -ggdb3'
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 14.0.0 20230926 (experimental) (GCC)
Comment 1 Andrew Pinski 2023-09-28 14:14:28 UTC
There is a dup of this where valgrind complains.
Comment 2 Andrew Pinski 2023-09-28 14:17:16 UTC
Dup of bug 111505.

*** This bug has been marked as a duplicate of bug 111505 ***