Home > Software design >  How do I annotate a C or C function using SAL to specify that it returns true/false if a parameter
How do I annotate a C or C function using SAL to specify that it returns true/false if a parameter

Time:11-17

When compiling with Microsoft's /analyze static analysis command line option for cl.exe, I get the warning

warning C6011: Dereferencing NULL pointer 'foo'

on a code path that calls a trivial function that guarantees that foo is not NULL where the analyzer thinks it can be.

The trivial function:

bool check_ptr(void* ptr)
{
    if (!ptr)
    {
        // The original does more things here, but
        // the repro is valid even without that.
        return false;
    }

    return true;
}

The calling site:

foo_t* foo = lookup_foo(id);
if (!check_ptr(foo))
    return;
foo->bar = 4711; // warning C6011: Dereferencing NULL pointer 'foo'

The analyzer is really bad at seeing through function calls, even trivial ones. If check_ptr is reduced to

bool check_ptr(void* ptr)
{
    return !!ptr;
}

then the analyzer can deduce that foo cannot be NULL when dereferenced, but that's not an option. The checker function is there for a reason.

So, I assume that there is an ungodly SAL annotation combination that can be applied to check_ptr to convince the analyzer that if it returns true, then the foo argument is not NULL.

Is there such a SAL annotation?

EDIT: I found a SAL solution and added it as a separate answer https://stackoverflow.com/a/74459650/6345

CodePudding user response:

I found a way to SAL annotate the check_ptr function so that the analyzer knows what it does, by using _Post_equal_to_. By annotating the function return declaration with _Post_equal_to_(!!p), the analyzer knows that the return value is non-zero if p is non-NULL.

Added a complete minimal repro example on godbolt / Compiler Explorer that looks more like the actual usage scenario in my code. Note the UNCOMMENT NEXT LINE TO REMOVE comment.

// Compile with these cl.exe command line options: /TC /WX -std:c17 /analyze
// https://godbolt.org/z/5sj9j7dY3
#include <sal.h>

typedef struct source_location {
    const char* file;
    int line;
} source_location;

// UNCOMMENT NEXT LINE TO REMOVE "warning C6011: Dereferencing NULL pointer"
//_Post_equal_to_(!!p)
int check_ptr__(void* p, source_location location);

#define check_ptr(p) \
    check_ptr__((p), (source_location) { __FILE__, __LINE__ }) 

// '_In_opt_' tells the analyzer that 'i' can validly be NULL
void minimal_analyzer_repro(_In_opt_ int* i)
{
    if (check_ptr(i))
        *i = 4711; // "warning C6011: Dereferencing NULL pointer"
}

CodePudding user response:

It's not exactly using SAL, but a generic answer that should work all the time: instead of returning bool, you might take two lambdas: one for then, one for else:

template<typename Then, typename Else>
void if_check_ptr(Foo* ptr, Then then, Else els)
{
    if (!ptr)
    {
        // The original does more things here, but
        // the repro is valid even without that.
        then(*ptr);
    } else {
        els();
    }
}

// usage:
if_check_ptr(
    lookup_foo(id),
    /*then*/ [&](Foo& foo) { foo.bar = 4711; },
    /*else*/ []() {}
);

You could even have lookup_foo() and if_check_ptr() in one call.

  • Related