Bug 96381 - gfc_find_vtab can use a character type typespec as a derived type (causing invalid access)
Summary: gfc_find_vtab can use a character type typespec as a derived type (causing in...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 11.0
: P3 normal
Target Milestone: ---
Assignee: anlauf
URL:
Keywords:
: 98263 (view as bug list)
Depends on:
Blocks:
 
Reported: 2020-07-29 16:56 UTC by Matthew Malcomson
Modified: 2021-01-04 21:09 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-07-30 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Matthew Malcomson 2020-07-29 16:56:26 UTC
When running the testsuite with a compiler sanitized with -fsanitize=hwaddress (HWASAN sanitizer which is not yet committed upstream) I saw the error below.

The error comes from the testsuite running `pr93337.f90`.

It is complaining that `gfc_find_derived_vtab` is attempting to access an 8 byte chunk of data 88 bytes after a region that is only 48 bytes long.
That seems to be coming from the access of `derived->attr.pdt_template` (which is a one-bit field in the byte 92 bytes into a `gfc_symbol` structure).

According to the dump the 48 byte long structure is allocated in `gfc_new_charlen`.
This function only ever sets the `cl` alternative of the union in a `gfc_typespec`.
I've inlined a GDB session demonstrating the mis-use under the HWASAN dump.





==25394==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdfffff79d8 at pc 0x0000006a8560
READ of size 8 at 0xefdfffff79d8 tags: 58/ff (ptr/mem) in thread T0
    #0 0x6a855c in SigTrap<3> ../../../../gcc-source/libsanitizer/hwasan/hwasan_checks.h:27
    #1 0x6a855c in CheckAddress<(__hwasan::ErrorAction)0, (__hwasan::AccessType)0, 3> ../../../../gcc-source/libsanitizer/hwasan/hwasan_checks.h:88
    #2 0x6a855c in __hwasan_load8 ../../../../gcc-source/libsanitizer/hwasan/hwasan.cpp:454
    #3 0x6ff654 in gfc_find_derived_vtab(gfc_symbol*) ../../gcc-source/gcc/fortran/class.c:2269
    #4 0x707498 in gfc_find_vtab(gfc_typespec*) ../../gcc-source/gcc/fortran/class.c:2908
    #5 0x707498 in gfc_find_vtab(gfc_typespec*) ../../gcc-source/gcc/fortran/class.c:2898
    #6 0x7a7578 in gfc_match_assignment() ../../gcc-source/gcc/fortran/match.c:1393
    #7 0x80d53c in match_word ../../gcc-source/gcc/fortran/parse.c:65
    #8 0x80d53c in decode_statement ../../gcc-source/gcc/fortran/parse.c:361
    #9 0x812f28 in next_free ../../gcc-source/gcc/fortran/parse.c:1280
    #10 0x812f28 in next_statement ../../gcc-source/gcc/fortran/parse.c:1512
    #11 0x816190 in parse_spec ../../gcc-source/gcc/fortran/parse.c:3923
    #12 0x819948 in parse_progunit ../../gcc-source/gcc/fortran/parse.c:5853
    #13 0x81c02c in gfc_parse_file() ../../gcc-source/gcc/fortran/parse.c:6394
    #14 0x898d98 in gfc_be_parse_file ../../gcc-source/gcc/fortran/f95-lang.c:212
    #15 0x152ac7c in compile_file ../../gcc-source/gcc/toplev.c:458
    #16 0x69d114 in do_compile ../../gcc-source/gcc/toplev.c:2320
    #17 0x69d114 in toplev::main(int, char**) ../../gcc-source/gcc/toplev.c:2459
    #18 0x6a0218 in main ../../gcc-source/gcc/main.c:39
    #19 0xffffa03c38dc in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x1f8dc)

[0xefdfffff79c0,0xefdfffff7a00) is a small allocated heap chunk; size: 64 offset: 24
0xefdfffff79d8 is located 40 bytes to the right of 48-byte region [0xefdfffff7980,0xefdfffff79b0)
allocated here:
    #0 0x6a9d40 in __sanitizer_calloc ../../../../gcc-source/libsanitizer/hwasan/hwasan_interceptors.cpp:138
    #1 0x2d1ebbc in xcalloc ../../gcc-source/libiberty/xmalloc.c:162
    #2 0x8831a0 in gfc_new_charlen(gfc_namespace*, gfc_charlen*) ../../gcc-source/gcc/fortran/symbol.c:3964
    #3 0x7146ec in gfc_match_char_spec(gfc_typespec*) ../../gcc-source/gcc/fortran/decl.c:3478
    #4 0x71e324 in gfc_match_decl_type_spec(gfc_typespec*, int) ../../gcc-source/gcc/fortran/decl.c:4169
    #5 0x7220d8 in gfc_match_data_decl() ../../gcc-source/gcc/fortran/decl.c:6129
    #6 0x80d6b0 in match_word ../../gcc-source/gcc/fortran/parse.c:65
    #7 0x80d6b0 in decode_statement ../../gcc-source/gcc/fortran/parse.c:376
    #8 0x812f28 in next_free ../../gcc-source/gcc/fortran/parse.c:1280
    #9 0x812f28 in next_statement ../../gcc-source/gcc/fortran/parse.c:1512
    #10 0x816600 in parse_derived ../../gcc-source/gcc/fortran/parse.c:3343
    #11 0x816600 in parse_spec ../../gcc-source/gcc/fortran/parse.c:3884
    #12 0x819948 in parse_progunit ../../gcc-source/gcc/fortran/parse.c:5853
    #13 0x81c02c in gfc_parse_file() ../../gcc-source/gcc/fortran/parse.c:6394
    #14 0x898d98 in gfc_be_parse_file ../../gcc-source/gcc/fortran/f95-lang.c:212
    #15 0x152ac7c in compile_file ../../gcc-source/gcc/toplev.c:458
    #16 0x69d114 in do_compile ../../gcc-source/gcc/toplev.c:2320
    #17 0x69d114 in toplev::main(int, char**) ../../gcc-source/gcc/toplev.c:2459
    #18 0x6a0218 in main ../../gcc-source/gcc/main.c:39
    #19 0xffffa03c38dc in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x1f8dc)
    #20 0x6a3e5c  (/home/ubuntu/working-directory/gcc-hwasan-install/libexec/gcc/aarch64-unknown-linux-gnu/11.0.0/f951+0x6a3e5c)

Thread: T0 0xeffe00002000 stack: [0xfffffabad000,0xfffffebad000) sz: 67108864 tls: [0xffffa060b000,0xffffa060b850)
Memory tags around the buggy address (one tag corresponds to 16 bytes):
  0xfefcfffff710: b5  b5  b5  08  1a  1a  1a  1a  47  47  47  08  2b  2b  2b  08
  0xfefcfffff720: 79  79  79  08  ad  ad  ad  ad  77  77  77  08  42  42  42  08
  0xfefcfffff730: 70  70  70  70  93  93  93  08  36  36  36  36  19  19  19  08
  0xfefcfffff740: 6e  6e  6e  6e  c2  c2  c2  08  29  29  29  08  9f  9f  9f  9f
  0xfefcfffff750: ce  ce  ce  08  3c  3c  3c  ed  d1  d1  d1  08  2a  2a  2a  08
  0xfefcfffff760: bb  bb  bb  bb  e9  e9  e9  08  2c  2c  2c  08  b4  b4  b4  b4
  0xfefcfffff770: 33  33  33  08  0d  0d  0d  08  ec  ec  ec  ec  29  29  29  08
  0xfefcfffff780: bf  bf  bf  08  f9  f9  f9  f9  f0  f0  f0  08  01  01  01  08
=>0xfefcfffff790: fc  fc  fc  fc  f0  f0  f0  08  58  58  58  81  ff [ff] ff  08
  0xfefcfffff7a0: a8  a8  a8  08  83  83  83  83  f3  f3  f3  08  9e  9e  9e  08
  0xfefcfffff7b0: bf  bf  bf  08  7f  7f  7f  7f  cc  cc  04  9e  43  43  43  43
  0xfefcfffff7c0: c4  c4  c4  c4  12  12  12  b5  36  36  36  36  2d  2d  2d  2d
  0xfefcfffff7d0: 65  65  65  65  cc  cc  cc  cc  4c  4c  4c  4c  78  78  78  0e
  0xfefcfffff7e0: d1  d1  d1  d1  f5  f5  f5  f5  57  57  57  08  b0  b0  b0  08
  0xfefcfffff7f0: 5e  5e  5e  08  49  49  49  08  7c  7c  7c  08  85  85  85  08
  0xfefcfffff800: 63  63  63  08  7d  7d  7d  08  0d  0d  0d  08  71  71  71  08
  0xfefcfffff810: 06  06  08  97  74  74  74  08  bb  bb  bb  08  aa  aa  aa  08
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
  0xfefcfffff780: ..  ..  ..  bf  ..  ..  ..  ..  ..  ..  ..  f0  f0  00  00  01
=>0xfefcfffff790: ..  ..  ..  ..  ..  ..  ..  f0  ..  ..  ..  ..  .. [..] ..  ff
  0xfefcfffff7a0: ..  ..  ..  a8  ..  ..  ..  ..  ..  ..  ..  f3  ..  ..  ..  9e
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
SUMMARY: HWAddressSanitizer: tag-mismatch ../../../../gcc-source/libsanitizer/hwasan/hwasan_checks.h:27 in SigTrap<3>







GDB session demonstrating this happening (to demonstrate that this does seem to be misusing a `gfc_charlen*` as a `gfc_symbol *`).


ubuntu@ubuntu:~/working-directory$ ./gcc-hwasan-install/bin/gfortran gcc-source/gcc/testsuite/gfortran.dg/pr93337.f90 -O -pedantic-errors -S -o - -wrapper gdb,-q,--args
Reading symbols from /home/ubuntu/working-directory/gcc-hwasan-install/libexec/gcc/aarch64-unknown-linux-gnu/11.0.0/f951...done.
(gdb) break decl.c:3516 if $_any_caller_is("gfc_match_decl_type_spec")
Breakpoint 1 at 0x714814: file ../../gcc-source/gcc/fortran/decl.c, line 3516.
(gdb) run
Starting program: /home/ubuntu/working-directory/gcc-hwasan-install/libexec/gcc/aarch64-unknown-linux-gnu/11.0.0/f951 gcc-source/gcc/testsuite/gfortran.dg/pr93337.f90 -quiet -dumpbase pr93337.f90 -dumpbase-ext .f90 -mlittle-endian -mabi=lp64 -O -pedantic-errors -o - -fintrinsic-modules-path /home/ubuntu/working-directory/gcc-hwasan-install/lib/gcc/aarch64-unknown-linux-gnu/11.0.0/finclude
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
        .arch armv8-a
        .file   "pr93337.f90"

Breakpoint 1, gfc_match_char_spec (ts=ts@entry=0x4028c58 <current_ts>) at ../../gcc-source/gcc/fortran/decl.c:3516
3516      ts->u.cl = cl;
(gdb) print cl
$1 = (gfc_charlen *) 0x3100efdfffff3440
(gdb) break gfc_find_derived_vtab if (void*)derived == (void*)$1
Breakpoint 2 at 0x6ff370: file ../../gcc-source/gcc/fortran/class.c, line 2262.
(gdb) cont
Continuing.

Breakpoint 2, gfc_find_derived_vtab (derived=derived@entry=0x3100efdfffff3440) at ../../gcc-source/gcc/fortran/class.c:2262
2262    {
(gdb)
Comment 1 Martin Liška 2020-07-30 07:43:47 UTC
Confirmed, one can see it in valgrind as well:

==1018== Invalid read of size 1
==1018==    at 0x7F10BB: gfc_find_derived_vtab(gfc_symbol*) (class.c:2269)
==1018==    by 0x8484F3: gfc_match_assignment() (match.c:1393)
==1018==    by 0x86F742: match_word (parse.c:65)
==1018==    by 0x86F742: decode_statement() (parse.c:361)
==1018==    by 0x874C14: next_free (parse.c:1315)
==1018==    by 0x874C14: next_statement() (parse.c:1547)
==1018==    by 0x87675C: parse_spec(gfc_statement) (parse.c:3962)
==1018==    by 0x87949C: parse_progunit(gfc_statement) (parse.c:5892)
==1018==    by 0x87AB80: gfc_parse_file() (parse.c:6433)
==1018==    by 0x8CC54F: gfc_be_parse_file() (f95-lang.c:212)
==1018==    by 0xE4C123: compile_file() (toplev.c:458)
==1018==    by 0x7D2D91: do_compile (toplev.c:2327)
==1018==    by 0x7D2D91: toplev::main(int, char**) (toplev.c:2466)
==1018==    by 0x7D6B5E: main (main.c:39)
==1018==  Address 0x51264ae is 18 bytes before a block of size 264 free'd
==1018==    at 0x48399AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==1018==    by 0x7DE7EF: gfc_match_array_spec(gfc_array_spec**, bool, bool) (array.c:802)
==1018==    by 0x803396: variable_decl (decl.c:2520)
==1018==    by 0x803396: gfc_match_data_decl() (decl.c:6200)
==1018==    by 0x86F88D: match_word (parse.c:65)
==1018==    by 0x86F88D: decode_statement() (parse.c:376)
==1018==    by 0x874C14: next_free (parse.c:1315)
==1018==    by 0x874C14: next_statement() (parse.c:1547)
==1018==    by 0x876CC4: parse_derived (parse.c:3382)
==1018==    by 0x876CC4: parse_spec(gfc_statement) (parse.c:3923)
==1018==    by 0x87949C: parse_progunit(gfc_statement) (parse.c:5892)
==1018==    by 0x87AB80: gfc_parse_file() (parse.c:6433)
==1018==    by 0x8CC54F: gfc_be_parse_file() (f95-lang.c:212)
==1018==    by 0xE4C123: compile_file() (toplev.c:458)
==1018==    by 0x7D2D91: do_compile (toplev.c:2327)
==1018==    by 0x7D2D91: toplev::main(int, char**) (toplev.c:2466)
==1018==    by 0x7D6B5E: main (main.c:39)
Comment 2 anlauf 2020-12-31 21:09:43 UTC
The following patch fixes the invalid read:

diff --git a/gcc/fortran/class.c b/gcc/fortran/class.c
index 5677d920239..783e4c7354b 100644
--- a/gcc/fortran/class.c
+++ b/gcc/fortran/class.c
@@ -2906,7 +2906,9 @@ gfc_find_vtab (gfc_typespec *ts)
     case BT_DERIVED:
       return gfc_find_derived_vtab (ts->u.derived);
     case BT_CLASS:
-      if (ts->u.derived->components && ts->u.derived->components->ts.u.derived)
+      if (ts->u.derived->attr.is_class
+         && ts->u.derived->components
+         && ts->u.derived->components->ts.u.derived)
        return gfc_find_derived_vtab (ts->u.derived->components->ts.u.derived);
       else
        return NULL;

Not regtested yet.
Comment 4 GCC Commits 2021-01-01 17:56:13 UTC
The master branch has been updated by Harald Anlauf <anlauf@gcc.gnu.org>:

https://gcc.gnu.org/g:d816b0c144d15e6570eb5b124b9f3ccbe3d40082

commit r11-6405-gd816b0c144d15e6570eb5b124b9f3ccbe3d40082
Author: Harald Anlauf <anlauf@gmx.de>
Date:   Fri Jan 1 18:55:41 2021 +0100

    PR fortran/96381 - invalid read in gfc_find_derived_vtab
    
    An invalid declaration of a CLASS instance can lead to an internal state
    with inconsistent attributes during parsing that needs to be handled with
    sufficient care when processing subsequent statements.  Avoid a lookup of
    the vtab entry for such cases.
    
    gcc/fortran/ChangeLog:
    
            * class.c (gfc_find_vtab): Add check on attribute is_class.
Comment 5 anlauf 2021-01-01 20:10:57 UTC
*** Bug 98263 has been marked as a duplicate of this bug. ***
Comment 6 GCC Commits 2021-01-04 21:03:53 UTC
The releases/gcc-10 branch has been updated by Harald Anlauf <anlauf@gcc.gnu.org>:

https://gcc.gnu.org/g:78ff090d0a0bb5a77560203b3b49bb7da7fcb88c

commit r10-9200-g78ff090d0a0bb5a77560203b3b49bb7da7fcb88c
Author: Harald Anlauf <anlauf@gmx.de>
Date:   Fri Jan 1 18:55:41 2021 +0100

    PR fortran/96381 - invalid read in gfc_find_derived_vtab
    
    An invalid declaration of a CLASS instance can lead to an internal state
    with inconsistent attributes during parsing that needs to be handled with
    sufficient care when processing subsequent statements.  Avoid a lookup of
    the vtab entry for such cases.
    
    gcc/fortran/ChangeLog:
    
            * class.c (gfc_find_vtab): Add check on attribute is_class.
    
    (cherry picked from commit d816b0c144d15e6570eb5b124b9f3ccbe3d40082)
Comment 7 GCC Commits 2021-01-04 21:06:10 UTC
The releases/gcc-9 branch has been updated by Harald Anlauf <anlauf@gcc.gnu.org>:

https://gcc.gnu.org/g:2bfcf6011a6cdce0439e3d1b94bcb5fcf078f4c2

commit r9-9151-g2bfcf6011a6cdce0439e3d1b94bcb5fcf078f4c2
Author: Harald Anlauf <anlauf@gmx.de>
Date:   Fri Jan 1 18:55:41 2021 +0100

    PR fortran/96381 - invalid read in gfc_find_derived_vtab
    
    An invalid declaration of a CLASS instance can lead to an internal state
    with inconsistent attributes during parsing that needs to be handled with
    sufficient care when processing subsequent statements.  Avoid a lookup of
    the vtab entry for such cases.
    
    gcc/fortran/ChangeLog:
    
            * class.c (gfc_find_vtab): Add check on attribute is_class.
    
    (cherry picked from commit d816b0c144d15e6570eb5b124b9f3ccbe3d40082)
Comment 8 anlauf 2021-01-04 21:09:15 UTC
Fixed on all branches where testcase gfortran.dg/pr93337.f90 was committed.

Thanks for the report!