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

            Bug ID: 78653
           Summary: badly optimized kernel code with
                    -fsanitize=object-size -fsanitize=null
           Product: gcc
           Version: 7.0
               URL: https://bugs.linaro.org/show_bug.cgi?id=2354
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: other
          Assignee: unassigned at gcc dot gnu.org
          Reporter: clyon at gcc dot gnu.org
  Target Milestone: ---
            Target: arm

As described in the original bug report
(https://bugs.linaro.org/show_bug.cgi?id=2354)
parts of the Linux kernel fail to build after enabling UBSAN.

With the sample attached testcase, a simple function gets expanded to a very
complex expression. The original source for the function is

static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
                                      u32 bandwidth,
                                      u16 *nominal_rate)
{
        struct zl10353_state *state = fe->demodulator_priv;
        u32 adc_clock = 450560; /* 45.056 MHz */
        u64 value;
        u8 bw = bandwidth / 1000000;

        if (state->config.adc_clock)
                adc_clock = state->config.adc_clock;

        value = (u64)10 * (1 << 23) / 7 * 125;
        value = (bw * value) + adc_clock / 2;
        do_div(value, adc_clock);
        *nominal_rate = value;

        dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
                __func__, bw, adc_clock, *nominal_rate);
}

The key here appears to be the setting of adc_clock, which the compiler knows
is not zero, but it doesn't know the actual value of.

The do_div() macro in turn is a complex way of turning a constant 64-bit
division into a multiplication, for reference the non-preprocessed version is
at
http://lxr.free-electrons.com/source/include/asm-generic/div64.h?v=4.6
The __builtin_constant_p() here should guard code that only makes sense for a
constant input:

208         if (__builtin_constant_p(__base) &&             \
209             is_power_of_2(__base)) {                    \
210                 __rem = (n) & (__base - 1);             \
211                 (n) >>= ilog2(__base);                  \

however, the ____ilog2_NaN symbol is referenced here:

#define ilog2(n)                                \
(                                               \
        __builtin_constant_p(n) ? (             \
                (n) < 1 ? ____ilog2_NaN() :     \
                (n) & (1ULL << 63) ? 63 :       \
                (n) & (1ULL << 62) ? 62 :       \
                (n) & (1ULL << 61) ? 61 :       \
                (n) & (1ULL << 60) ? 60 :       \
...
                (n) & (1ULL <<  1) ?  1 :       \
                (n) & (1ULL <<  0) ?  0 :       \
                ____ilog2_NaN()                 \
                                   ) :          \
        (sizeof(n) <= 4) ?                      \
        __ilog2_u32(n) :                        \
        __ilog2_u64(n)                          \
 )

after 'n' has been determined to be constant yet not known whether
it is less than '1', which mathematically makes no sense.

Reply via email to