Bug 92589 - heuristic to avoid flexible array members too liberal
Summary: heuristic to avoid flexible array members too liberal
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: documentation
Depends on:
Blocks: flexmembers
  Show dependency treegraph
 
Reported: 2019-11-20 00:32 UTC by Kees Cook
Modified: 2020-03-31 20:51 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
PoC for -fsanitize=bounds (351 bytes, text/x-csrc)
2019-11-20 00:32 UTC, Kees Cook
Details
updated PoC (604 bytes, text/x-csrc)
2020-03-31 20:51 UTC, Kees Cook
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Kees Cook 2019-11-20 00:32:19 UTC
Created attachment 47305 [details]
PoC for -fsanitize=bounds

When choosing which arrays to instrument, -fsanitize=bounds tries to avoid flexible array members. Traditionally, this has been either [], [0], or [1]-sized arrays (though the latter two are realistically considered deprecated in C).

However, the sanitizer appears to be ignoring _all_ trailing arrays in a structure, no matter what their size. Comparing the behavior between GCC and Clang, this is more visible:

$ gcc -Wall -g3 -fsanitize=bounds -fsanitize-undefined-trap-on-error -o bounds-gcc bounds.c
$ ./bounds-gcc abc
flex
non_flex
non_trailing
Illegal instruction (core dumped)

$ clang -Wall -g3 -fsanitize=bounds -fsanitize-undefined-trap-on-error -o bounds-clang bounds.c
$ ./bounds-clang abc
flex
non_flex
Illegal instruction (core dumped)

I would expect the trap during the non_flex structure over-index, as seen in the Clang-built binary.
Comment 1 Andrew Pinski 2019-11-20 00:48:32 UTC
I thought GCC documented this differently.  So this is just a documentation issue.
GCC allows even non-1 sized fields to be considered flexible arrays if they are at the end of the struct.
Comment 2 Kees Cook 2019-11-20 00:58:42 UTC
Is there anything to enforce a strict "only consider empty array size as flexible array member" mode? This is an unfortunate weakening of the array bounds checker as there are plenty of structures that have a fixed-size array as the final member.
Comment 3 Andrew Pinski 2019-11-20 01:28:03 UTC
From what I remember is there are actually arrays in POSIX and other older code bases that are not just one element which needs to be treated this way.  


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68270#c1

There are others too.  I can't find them right now though.

>Is there anything to enforce a strict "only consider empty array size as flexible array member" mode?

NO.  See above of why.
Comment 4 Jakub Jelinek 2019-11-20 08:20:54 UTC
(In reply to Kees Cook from comment #2)
> Is there anything to enforce a strict "only consider empty array size as
> flexible array member" mode? This is an unfortunate weakening of the array
> bounds checker as there are plenty of structures that have a fixed-size
> array as the final member.

There is -fsanitize=bounds-strict.
Comment 5 Martin Sebor 2019-11-20 16:51:06 UTC
(In reply to Andrew Pinski from comment #1)
> I thought GCC documented this differently.  So this is just a documentation
> issue.
> GCC allows even non-1 sized fields to be considered flexible arrays if they
> are at the end of the struct.

GCC might handle it gracefully in many contexts but it's considered a bug (in user code) and warnings like -Wstringop-overflow try to detect and diagnose it, so it's something to avoid.  At -O3, current trunk issues:

pr92589.c:41:27: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=]
   41 |         non_flex->data[i] = argv[1][i];
      |         ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
pr92589.c:14:10: note: destination object declared here
   14 |     char data[2];
      |          ^~~~

GCC manual (Zero Length Arrays) documents the [] and [0] forms and discourages the [1] form of trailing arrays.  There is no mention of arrays with more elements being treated that way.
Comment 6 Kees Cook 2020-03-31 20:43:47 UTC
(In reply to Jakub Jelinek from comment #4)
> (In reply to Kees Cook from comment #2)
> > Is there anything to enforce a strict "only consider empty array size as
> > flexible array member" mode? This is an unfortunate weakening of the array
> > bounds checker as there are plenty of structures that have a fixed-size
> > array as the final member.
> 
> There is -fsanitize=bounds-strict.

This is too strict: it doesn't allow flexible arrays ([]) either. I'd like something that ignores _only_ flexible arrays and fails on all other trailing arrays beyond their size.
Comment 7 Kees Cook 2020-03-31 20:49:23 UTC
(In reply to Kees Cook from comment #6)
> (In reply to Jakub Jelinek from comment #4)
> > (In reply to Kees Cook from comment #2)
> > > Is there anything to enforce a strict "only consider empty array size as
> > > flexible array member" mode? This is an unfortunate weakening of the array
> > > bounds checker as there are plenty of structures that have a fixed-size
> > > array as the final member.
> > 
> > There is -fsanitize=bounds-strict.
> 
> This is too strict: it doesn't allow flexible arrays ([]) either. I'd like
> something that ignores _only_ flexible arrays and fails on all other
> trailing arrays beyond their size.

Oops, sorry, my PoC was testing the corner cases, not the correct cases. -fsanitize=bounds-strict _does_ work (though I'd rather it disallowed [0], but I'll live):

$ gcc -Wall -g3 -fsanitize=bounds-strict -fsanitize-undefined-trap-on-error -o bounds-gcc bounds.c
$ ./bounds-gcc abc
flex (should always be okay): ok (no trap!)
zero (should be okay, treated as flex): ok (no trap!)
one (should fail): Illegal instruction (core dumped)
Comment 8 Kees Cook 2020-03-31 20:51:03 UTC
Created attachment 48153 [details]
updated PoC