This code sample makes use of banned.h
, a Microsoft-supplied header file that deprecates dangerous CRT functions. Microsoft also poisons these functions on UNIX if you include banned.h
there. This is a Good Thing, but what about the fact that they banned strlen
? The banned API page states:
For critical functions, such as those accepting anonymous Internet connections, strlen
must also be replaced.
That’s good advice for cases where you want to operate on untrusted data. In those cases they tell you that you should use strnlen_s
. The problem is, banned.h
straight out bans strlen
. There is no way to tell it that hey, this particular invocation is safe because I control the buffer in all aspects. Nope, sorry. You can’t use strlen
. Or can you?
Here is a code sample that uses banned.h
to deprecate unsafe APIs, yet still manages to invoke strlen
when necessary. The sample works in both Visual Studio on Windows and GCC on UNIX.
// // banned_test.c // 20091208 ramsey@casabasecurity.com // // A sample program that illustrates how to "grandfather in" banned APIs // for use when they are marked deprecated (Windows) or poisoned (UNIX) // by the compiler. // // to compile on Windows: // cl /GS /W4 /WX banned_test.c // // to compile on UNIX: // gcc -Wall -Werror banned_test.c #if defined _WIN32 #include <windows.h> #endif // _WIN32 #include <stdio.h> #include <string.h> #if defined _WIN32 size_t my_strlen(const char *string) { size_t len; #pragma warning(push) #pragma warning(disable:4995) len = strlen(string); #pragma warning(pop) return len; } #else #define my_strlen strlen #endif // _WIN32 #include "banned.h" int main(int ac, char **av) { char *str = "foo"; #if defined _WIN32 UNREFERENCED_PARAMETER(ac); UNREFERENCED_PARAMETER(av); #endif // _WIN32 #if defined _WIN32 (void)printf("len is %Id\n", strlen(str)); #else (void)printf("len is %zd\n", strlen(str)); #endif // _WIN32 return 0; } |
Note that this code requires the use of Microsoft’s banned.h
header file, which can be downloaded here. Stick it in the same directory as the above source file.
To compile the sample in Windows from a Visual Studio Command Prompt:
cl banned_test.c /GS /W4 /WX
As expected, this program will generate an error when run:
banned_test.c banned_test.c(50) : error C2220: warning treated as error - no 'object' file generated banned_test.c(50) : warning C4995: 'strlen': name was marked as #pragma deprecated
Now edit banned_test.c
and change the strlen
on line 50 to my_strlen
and recompile:
cl banned_test.c /GS /W4 /WX
It should compile without error. Now run it and you should see:
len is 3
Nifty.
The same code works without change on UNIX (tested on NetBSD):
gcc -Wall -Werror banned_test.c
As with the Windows example, running the program will generate an error, as expected:
banned_test.c:52:31: error: attempt to use poisoned "strlen"
Again, change the occurrence of strlen
(this time on line 52) to my_strlen
and recompile. It will work and when run, it will say:
len is 3
What’s going on here is simple. While we are banning use of the strlen
function, we are still allowing its use selectively through a wrapper that we have “grandfathered in.” This is easy to accomplish in UNIX: we simply
#define my_strlen strlen
prior to including banned.h
and use that function call entry point instead. Problem solved. This is not as easy to accomplish with Windows, however, as cl.exe
has no notion of “grandfathering in” deprecated APIs. So what we do is wrap strlen
in another function. We ignore the deprecation warning that occurs where we make the call to strlen
through the judicious application of some Visual Studio-specific pragma
instructions. Now all need to do is call in to our new function entry point. We’re good to go. The Windows solution requires a little more work up front, but turns out to be not so hard to accomplish after all.