How do I enforce SAL annotations on a typedef'd function?

248 views Asked by At

Let's say I import a DLL function like so:

typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

HINSTANCE hDLL = LoadLibraryW(L"foo.dll");
pRandomNumber RandomNumber = (pRandomNumber)GetProcAddress(hDLL, "RandomNumber");

Now, this function RandomNumber in the code of the DLL is annotated with the SAL annotation _Check_return_. I also want to have this annotation enforced in code that calls it. How do I get that done?

If I put the annotation on the typedef:

_Check_return_
typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

Analysis fails to report a warning later when I initialize a pRandomNumber and call it without checking the return value.

If I put the annotation on an instance:

typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

_Check_return_
pRandomNumber RandomNumber;

Analysis still fails to report when I call it without checking the return value.

Is there any way I can have that enforced? One way is writing a stub inline function with the annotation on it, but that feels ugly to me.

1

There are 1 answers

0
RbMm On BEST ANSWER

instead of declare variable

int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

you can declare function with __declspec(dllimport) attribute and use [[nodiscard]] and/or _Must_inspect_result_ on this declaration

EXTERN_C
_NODISCARD
DECLSPEC_IMPORT
_Must_inspect_result_
int 
WINAPI 
RandomNumber(
    _In_ size_t cb, 
    _Out_writes_bytes_(cb) unsigned char *pb
    );

internally, when you declare api with __declspec(dllimport) attribute, compiler declare variable with extern keyword;

extern "C" { 
    extern PVOID __imp_<function_name>;
}

in place <function_name> __FUNCDNAME__ used - the decorated name of the function

so possible say and

extern PVOID __imp_##__FUNCDNAME__;

of course, because extern, this is only declaration. and if you not add definition - you got

error LNK2001: unresolved external symbol __imp_RandomNumber

if you try call RandomNumber. so you need add definition. for _AMD64_ this is very simply, because extern "C" symbols not decorated.

so simply write:

EXTERN_C_START
    PVOID __imp_RandomNumber;
EXTERN_C_END

instead

EXTERN_C_START
    int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
EXTERN_C_END

you need in both case declare pointer size variable, which will be hold function pointer - different only in name - you select pRandomNumber as name (and can select any name) - in my case - you must select __imp_RandomNumber ( __imp_<function_name> ) - add __imp_ prefix to decorated function name.

but in case _X86_ exist problem - for __stdcall - the @ symbol will be in decorated function name. you need write

EXTERN_C_START
    PVOID __imp__RandomNumber@8;
EXTERN_C_END

because decorated name will be _RandomNumber@8. but __imp__RandomNumber@8 not valid c/c++ name. you can define such name in asm code but not in c/c++. but you can use /alternatename linker option.

__pragma(comment(linker, "/alternatename:__imp__RandomNumber@8=___imp_RandomNumber"))

EXTERN_C_START
    PVOID __imp_RandomNumber;
EXTERN_C_END

if linker not found __imp__RandomNumber@8 it try use ___imp_RandomNumber if it exist. and __imp_RandomNumber will be decorated to ___imp_RandomNumber in _X86_

possible write next macro

#ifdef _X86_
#define ALT_NAME(name, n) __pragma(comment(linker, _CRT_STRINGIZE(/alternatename:__imp__##name##@##n####=___imp_##name)))
#else
#define ALT_NAME(name, n)
#endif

#define IMP_FUNC(name, n) EXTERN_C_START PVOID __imp_##name; EXTERN_C_END ALT_NAME(name, n)

with this you code will be

// global declaration

EXTERN_C
_NODISCARD
DECLSPEC_IMPORT
_Must_inspect_result_
int 
WINAPI 
RandomNumber(
    _In_ size_t cb, 
    _Out_writes_bytes_(cb) unsigned char *pb
    );

IMP_FUNC(RandomNumber, 8);

// initialization
__imp_RandomNumber = GetProcAddress(GetModuleHandle(L"foo"), "RandomNumber");

//usage
if (__imp_RandomNumber )
{
    UCHAR test[16];
    RandomNumber(sizeof(test), test);
}

and you got

warning C4834: discarding return value of function with 'nodiscard' attribute

if not use return value of RandomNumber