modulo size_t yields "The result of expression ' ' is undefined"

Viewed 123

On a 64-bit system, when using the modulo operator along with size_t types, i get the following warning from the clang analyzer:

htable.c:38:62: warning: The result of the ' ' expression is undefined [clang-analyzer-core.UndefinedBinaryOperatorResult]
return ht->num_buckets > 0 ? (ht->hash_fn(key, ht->seed) % ht->num_buckets)
                                                         ^

Complete trace

The result of the hash function has size_t, and the number of buckets is also of size_t. Ultimately this translates into unsigned long. AFAIK the result cannot be negative, I checked if the number of buckets is zero.

The hash function might overflow, but as it's used for a chaining hash table this does not lead to problems (if I am not mistaken).

What's wrong here? Is this a false positive?

The function where the warning occurs:

static size_t
htable_bucket_idx(htable_t* ht, void* key)
{
    if (!ht || !key) {
        printf("htable - bucket_idx: Invalid Argument!\n");
        exit(-1);
    }

    return ht->num_buckets > 0 ? (ht->hash_fn(key, ht->seed) % ht->num_buckets)
                               : ht->hash_fn(key, ht->seed);
}

The hash function is a crudely simplified version of the FNV hash function:

size_t
fnv_hash_ul(const void* in, unsigned int seed)
{
    size_t             h     = seed;
    const unsigned int prime = 0xFDCFB7;
unsigned long ul = *((unsigned long*) in);

    return (h ^ ul) * prime;
}

The definition of htable_t (with other types omitted for succinctness, appending on request)

typedef size_t (*htable_hash)(const void* in, unsigned int seed);

typedef struct htable
{
    htable_hash      hash_fn;
    htable_keq       keq;
    htable_cbs_t     cbs;
    htable_bucket_t* buckets;
    size_t           num_buckets;
    size_t           num_used;
    unsigned int     seed;
} htable_t;

Call of the function:

static int
htable_add_to_bucket(htable_t* ht, void* key, void* value, bool rehash)
{
    if (!ht || !key) {
        printf("htable - add_to_bucket: Invalid Argument!\n");
        exit(-1);
    }
    size_t idx = htable_bucket_idx(ht, key);

    [...]

Compiler Information:

clang version 11.1.0
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0
Candidate multilib: .;@m64

htable.c on GitHub
htable.h on GitHub

1 Answers

In deed, this seems to be a false positive. I was able to wipe out all warnings by changing htable_rehash() from:

static int htable_rehash(htable_t* ht)
{
...
    ht->num_buckets <<= 1UL;
...
}

to

static int htable_rehash(htable_t* ht)
{
...
    ht->num_buckets *= 2;
...
}

Weird.

Related