Am Mittwoch, dem 24.09.2025 um 10:08 +0200 schrieb Jakub Jelinek:
> On Wed, Sep 24, 2025 at 08:41:05AM +0200, Martin Uecker wrote:
> > Often I am interested in what I need to fix in the source code to avoid
> > the trap (because this may not be acceptable, e.g. when controlling some
> > hardware or because I want to remove performance impact of having the
> > conditional check). So, I like a diagnostic because it ties it back to
> > the source code and existing tools can handle this nicely. For example,
> > consider this example:
> >
> > maybe(int) safe_divide(int a, int b)
> > {
> > if (b == 0)
> > return maybe_nothing(int);
> >
> > return maybe_just(int, a / b);
> > }
> >
> > I would expect that compiling with
> >
> > -fsanitize=undefined -fsanitize-trap=all -O2
> >
> > comes up clean because I tested for zero and I expect the later trap to
> > be removed by the optimizer. But with the warning it tells me:
> >
> >
> > div.c: In function ‘safe_divide’:
> > div.c:13:34: warning: trap generated: "builtin" [-Wtrap]
> > 13 | return maybe_just(int, a / b);
> > | ^
>
> Note, UBSan intentionally attempts in many cases not to rely on certain
> kind of optimizations because they rely on lack of UB in the program and
> UBSan is meant to detect programs with UB in it.
> So e.g. using VRP info is problematic, in some cases it relies on checks
> which are there at runtime and so will happen even in programs with UB,
> but just replace return maybe_nothing(int); in your above testcase
> with __builtin_unreachable (); and program with UB will happily run into
> the division even with zero divisor.
Yes, this is clear to me. In the end you want to transform unreachable
into a trap as well.
> Sometimes we do just leave it to optimizers though, so best checking is
> certainly at -O0 -fsanitize=undefined, but with higher levels we should
> still maintain some middle ground and not try to optimize UBSan
> instrumentation too heavily.
I understand this if you run sanitizers in diagnostic mode. Then,
it seems clear that you do not want not optimize based on the UB.
But if you instrument code early, e.g. using the signed overflow
sanitizer, and it traps, then the instrumented code has no UB
anymore. So all optimization passes which come later would need
to care, or?
I know people use clang with sanitizers in production and then they
also care a lot about optimization. I my experience this also
works very well with GCC. Is there something I am missing?
> In the division particular case, we just turn it into
> if (b == 0) report_or_trap (); x = a / b;
> or for signed into
> if (b == 0 || (b == -1 && a == INT_MIN)) report_or_trap (); x = a / b;
> and so VRP will happily optimize it away.
Yes, you would need to turn it into "trap()" so that the
code path with UB ends in the trap.
Martin