如何使用SAL对C或C++函数进行注解,以指定在参数为非NULL/NULL时返回true/false

cigdeys3  于 2022-11-19  发布在  其他
关注(0)|答案(2)|浏览(172)

当使用Microsoft的/analyze静态分析命令行选项对cl.exe进行编译时,我收到警告

warning C6011: Dereferencing NULL pointer 'foo'

在代码路径上调用一个平凡的函数,该函数保证foo在分析器认为可以为NULL的地方不为NULL。
平凡函数:

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;
}

调用站点:

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

分析器在看穿函数调用方面确实很糟糕,即使是微不足道的调用。

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

那么分析器可以推断出foo在解引用时不能为NULL,但这不是一个选项。
因此,我假设有一个不可靠的SAL注解组合可以应用于check_ptr,以使分析器确信如果它返回true,则foo参数不是NULL。
是否有此类SAL注解?

**编辑:**我找到了SAL解决方案,并将其作为单独的答案添加https://stackoverflow.com/a/74459650/6345

mkshixfv

mkshixfv1#

我找到了一种方法,可以使用_Post_equal_to_check_ptr函数进行SAL注解,以便分析器知道它的作用。通过使用_Post_equal_to_(!!p)对函数返回声明进行注解,分析器知道如果p为非NULL,则返回值为非零。
godbolt / Compiler Explorer上添加了一个完整的最小复制示例,看起来更像我代码中的实际使用场景。注意UNCOMMENT NEXT LINE TO REMOVE注解。

// 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"
}
ijnw1ujt

ijnw1ujt2#

这并不完全是使用SAL,而是一个应该始终有效的通用答案:您可以使用两个lambda来代替返回bool:一个用于then,一个用于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*/ []() {}
);

您甚至可以在一个呼叫中同时使用lookup_foo()if_check_ptr()

相关问题