About some bugs

bearophile bearophileHUGS at lycos.com
Tue Jan 4 04:34:15 PST 2011


I have studied more Linux bugs.

----------------

An example of bug (more than 14 like this fixed in few years):

-       memset(pp, 0, sizeof(pp));
+       memset(pp, 0, sizeof(*pp));

-       memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex,sizeof(TstSchedTbl));
+       memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex, sizeof(*TstSchedTbl));

Here the type system knows that pp is a pointer. sizeof(pp) is typically a word, while the correct sizeof(*pp) is often larger. A simple way to avoid this bug in D is to use a zerioing template function, something like (untested) (in GNU C there is a way to write a similar macro, I don't know why they don't use it, even if it's a bit less safe and much less nice looking):

void zeroit(T)(T* ptr) if (!IsPointer!T) {
    memset(ptr, 0, (*ptr).sizeof);
}

Standard safer wrappers for some C functions may help low-level D coding.

If you don't want to use a zeroit() then a type system able to catch such bugs needs some nice annotations...

----------------

A common (> 12 cases in few years) example of redundant code:

        u32 da = new->da_start;
-       if (!obj || !new || !sgt) {...
+       if (!obj || !sgt) {...

If the first line is correct, then "new" can't be NULL, so there's no need to test "|| !new".

In 13 similar cases the code is inside a branch where the NULL test of the pointer is already done:

if (ptr != NULL) {
    if (!ptr) { ...
}

----------------

In 7 cases the result of malloc-like function was not tested for NULL:

        agp = kmalloc(sizeof(*agp), GFP_KERNEL);
+       if (!agp)
+               return NULL;

----------------

A very common case (20 cases in few years) are like this, where a pointer is deferenced before the NULL test:

        block = bdev->bd_disk->private_data;
-       base = block->base;
        if (!block)
                return -ENODEV;
+       base = block->base;

----------------

2 cases like this (the pattern (!E && !E->fld) is nonsensical):

-               if (!slot->hotplug_slot && !slot->hotplug_slot->info) 
+               if (!slot->hotplug_slot || !slot->hotplug_slot->info)
                        continue;

----------------

There are 11 cases like this (alloc_bootmem never returns NULL and it always sets the momory to zero):

    zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0), PAGE_SIZE);
-   memset(zero_page, 0, PAGE_SIZE);

----------------

There are a lot of cases (34) of missing free:

+       kfree(iter);

----------------

Two cases need a wider NULL test:

-               if (fep != NULL) {
+               if (fep && fep->ops) {
                        (*fep->ops->free_bd)(ndev);
                        (*fep->ops->cleanup_data)(ndev);
                }

----------------

In this post I don't see any little rule worth adding to the D compiler. So to bugzilla I will add only the !x&y thing.

But from what I have seen many bugs are NULL-related, and many of them are avoidable with two features present at the same time in the language:

1) Nonnull pointer/reference tag (with it you don't miss some null tests, and you avoid some redundant tests);

2) Plus a bit of inter-function (not intra, for that nonnull signatures are enough) flow analysis to avoid bugs like:

base = block->base;
if (!block)
    return -ENODEV;

if (fep != NULL) {
    (*fep->ops->free_bd)(ndev);

(P.S.: integer overflow bugs happen in Linux kernel too.)

Bye,
bearophile


More information about the Digitalmars-d mailing list