This is the third revision for my patch series to check bounds
consistency at run-time when assigning VM types.  Relative
to the last version, mostly the tests were simplified and some
coding style issues fixed.


It adds a new code instrumentation option that inserts
run-time checks to ensure bounds are matching. This helps 
with bounds safe programming and also finds problems in
numerical code, e.g. when bound are swapped in
multi-dimensional arrays

void foo(int x, int y, double m[x][y]);

double m[10][20];
foo(20, 10, m);

where currently do not get a warning or check.

(After updating this patch series and testing it, it
found a bug in new code added recently to my BART
project - a numerical toolbox for image reconstruction and
machine learning for magnetic resonance imaging.)



The patches in the series do:

1. Checks simple assignments.

2. In addition checks function calls under the assumption
that size expressions are declared as in the definition.
This assumption goes beyond what ISO C guarantees (and is
one reason this is not part of the UB sanitizer, the other
is that there is no library support of this use case) but
any inconsistency would usually indicate a bug anyway and
we warn already by default with -Wvla-parameter 
(this now becomes really useful)

3. Checks function calls via pointers when possible.

4. Adds a warning if a function calls can not be
instrumented e.g. because size expressions do not simply
references to other parameters or variables. Also adds
documentation for the warning and about instrumentation
of function calls.


The code is fairly simple and FE only as during recursive type
checking in the comptypes family of function we can simply collect
all pairs of size expressions that are supposed to match. This
list is then further processed to emit simple checking code.

For functions the main complication is that we need to evalute
size expressions in the caller. We therefor only add
checking if all size expressions direcly refer to other
declarations, and simply give up for anything more complex.
This already covers the most important use cases though.


The code is also useful infrastructure for future compile-time
warnings, e.g. the simply example above should clearly be
diagnosed already compile time. 

I haven't implemented this yet, but this should be simple to add
by detecting obvious cases during processing of the list of size
expressions.

Other current limitations are:

The outermost bounds for functions parameters are not checked because
they are lost when the type is adjusted to a pointer. The right semantics
of checking those are also less obvious.

As mentioned above, for functions we only check very simple size
expressions that directly refer to a parameter or argument. It would
be useful to extend this to more complex expressions without side
effects, such 'n + 1' or maybe even 'n + m'. This would then cover
most use cases in numerical code.

A bounds violation just causes a run-time trap without error message.
This is sufficient for safety and debugging with a debugger, but one 
consider adding a short error message. (This would have to go into
libgcc I guess).



The instrumentation is guarded by a new instrumentation flag -fvla-bounds,
but runtime overhead should generally be very low as most checks are
removed by the optimizer, e.g.

void foo(int x, char (*buf)[x])
{
 bar(x, buf);
}

does not have any overhead with -O1 (we also might want to filter out
some obvious cases already in the FE). So I think this flag could be
a good addition to -fhardened after some testing.  Maybe it could even
be activated by default.


Finally, I am unsure about the use if signals in the tests.  Maybe this
is not supported everywhere?  Any recommendation is much appreciated.


Each patch was bootstrapped and regression tested on x86_64.


Martin










Reply via email to