Using Wrapper Functions

The propose of this document is to provide an example of how to use wrapper functions to create programs that are more robust and that Splint can check more effectively.  The C standard library function realloc has complex semantics and is easy to use incorrectly.  Still, it is a popular function, and we are frequently asked how to use Splint to check code that uses realloc.  We hope to answer these questions in this section.  Additionally, we hope this example will provide insight into other ways to work around some of the limitations of Splint’s checking, and serve as a template for users wishing to create their own wrapper functions.

Splint cannot currently describe general functions where some of the attributes are dependent on one or more of the possible return values.  Often, functions are defined to modify their outputs, and perhaps allocate storage, but can also return an error indication, in which case none of the modifications or allocations will take place.

One very common example of this is the C standard library function realloc( void *pointer, size_t size).  When realloc succeeds, the pointer passed to it is no longer valid.  The returned pointer points to available storage with the specified size, and the values are copied from the leading portion of the original storage indicated by the pointer passed to the function.  When realloc returns a NULL pointer, and more than zero bytes were supposed to be allocated, no new storage has been allocated.  The original pointer passed in is not deallocated and its contents are still accessible.  (Under ANSI C '89 and later, malloc and realloc may return NULL if asked for zero bytes.  In this case, realloc would release the old storage.)

If you use realloc, -usereleased can be used to suppress warnings.  However, this may result in legitimate warnings for other errors being suppressed as well.  If you don't mind crafting your code to work around Splint's quirks, you can isolate this difficult-to-describe behavior to a single function that's easy to verify by inspection, and use that function elsewhere.  For example:

    /*@-usereleased@*/
    static /*@null@*/ void *
    grow_or_free (/*@only@*/ void *ptr, size_t newsize)
         /*@*/
    {
        void *newptr;
        newptr = realloc (ptr, newsize);
        if (newptr == NULL && newsize != 0) {
            /* Splint would complain,
               but this is correct.  */
            free (ptr);
            return NULL;
        }
        return newptr;
    }
    /*@=usereleased@*/

It would also be possible to use a function that always returns a valid pointer, and separately indicates whether it managed to change the size to that desired by the caller.