https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116984

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|---                         |INVALID
             Status|UNCONFIRMED                 |RESOLVED

--- Comment #9 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
(In reply to Kees Cook from comment #8)
> (In reply to Jakub Jelinek from comment #6)
> > counted_by is just another way how to get the initial whole
> > object dynamic size (similarly to fixed size automatic/static vars, malloc
> > etc., alloca, VLA definitions, whatever else provides the size of the whole
> > object).
> 
> I don't understand why the word "initial" is used there. It provides the
> _ongoing_ runtime bounds of the given array. Both the bounds sanitizer and
> __bdos were extended to make use of that information.

It is initial in the workflow of the object size pass, which has some
__builtin_object_size/__builtin_dynamic_object_size calls (explicit in the IL
or implicit e.g. from sanitization) and tracks object sizes through pointer
arithmetics and PHIs back to something which has a known object (or subobject)
size.
In your testcase, p->array has that known size because of counted_by attribute,
in other cases it could be a pointer initialized from malloc or similar calls,
in other cases it could be an object with non-dynamic size.

> > The rest is __builtin_dynamic_object_size dynamic tracking from that size
> > through pointer arithmetics etc.  And that doesn't change depending on what
> > the whole size has been computed with.
> 
> Part of the bounds sanitizer+__bdos work was to make sure that getting the
> size of invalidly indexed array element is 0 (and _not_ "don't know", since
> we *do* know: there is no element at an invalid location, therefore the size
> available at such an "address" is 0 bytes). This is so that the various

You can't get anything safely after invoking UB, there is nothing safe after
that, anything can happen.

The side-effects imply don't know special case was done for
__builtin_object_size (and later for __builtin_dynamic_object_size just
inherited it too) so that one could use the builtin e.g. in macros and don't
risk side-effects being evaluated multiple times.
So it is for cases like
__builtin_object_size (&a[i++], 0)
where we just say the function will return "don't know" and will not evaluate
the expression.  If there aren't side-effects in the C/C++ meaning (that
includes e.g. reading volatile vars, calling impure functions etc.), the
expression in the __bos/__bdos argument is evaluated at runtime and if there is
UB in it, the program is still invalid, and the compiler really can't guarantee
anything about that.
Consider if you have
int *pindex;
__builtin_dynamic_object_size (&a[*pindex], 0);
If the pindex is NULL or otherwise invalid pointer, the program will invoke UB
and certainly doesn't guarantee returning 0.  It can be diagnosed by UBSan
(e.g. the NULL case), it might be diagnosed by ASan (invalid pointer, some
cases of it), it might not be diagnosed at all and just crash.
Similarly, if you have
int idx1, idx2;
__builtin_dynamic_object_size (&a[idx1 + idx2], 0);
and idx1 + idx2 evaluation invokes signed integer overflow, again it will be UB
and anything can happen.  And the negative index on array ref is yet another
UB.

If __bos/__bdos doesn't have the side-effects which cause immediate folding of
the builtin with its arguments to the "don't know" value, then the expressions
are simply lowered to normal IL like anything else and can be instrumented for
UB like anything else, I don't think LLVM has any other representation for it
where you could safely invoke any kind of UB you'd like and all it would do is
change the return value of the __bos/__bdos call to 0.

Reply via email to