I am currently, writing a code for string manipulation.
As part of this, I am using vsnprintf()
.
However, compiler flashes below error message:
dont_call: vsnprintf(). Invokation of a potentially dangerous function
that could introduce a vulnerability. remediation:
Recommendation: Use vsprintf_s() instead!
The results with vsprintf_s()
is as not expected.
What is the difference between vsnprintf()
and vsprintf_s()
?
vsnprintf()
is a perfectly fine standard function that has no security issues if used properly, especially if the format string is a string literal compatible with the arguments passed.
Microsoft deprecated this function because an attacker could take advantage of sloppy code that uses user supplied text as the format specification: carefully crafted user supplied strings could use the %n
format to try and corrupt the program's data and make the program execute arbitrary code.
Using a variable format string is error prone as it is difficult to verify that the types of the arguments are compatible with this dynamically generated format string. Sensible coding conventions do not permit such code. Passing a user supplied string as a format string is completely inappropriate as it is a trivial source of undefined behavior. Disabling useful and standard functions because they could be misused and create security flaws is laudable, but in this particular case, the proposed alternatives have shortcomings.
There are 2 alternatives for vsnprintf()
, standardized in Annex K, but with optional support and thus non portable across environments:
int vsprintf_s(char * restrict s, rsize_t n,
const char * restrict format,
va_list arg);
and
int vsnprintf_s(char * restrict s, rsize_t n,
const char * restrict format,
va_list arg);
These functions behave differently from vsnprintf
in subtile ways:
%n
specifier.n
to have a 0
value.s
target array to be a null pointer, which snprintf()
allows if the n
size is 0
.vsprintf_s
differs from vsnprintf_s
when the output string is longer than n-1
: it sets the destination to the empty string, invokes the error handler and returns 0
or a negative number instead of the length of the formatted string, as both vsnprintf
and vsnprintf_s
do.As commented by A T, to make matters worse, Microsoft implements different semantics in their own version of this standard functions: even the prototype for vsnprintf_s
is different (as documented in the Visual Studio documentation):
int vsnprintf_s(
char *buffer,
size_t sizeOfBuffer, // extra argument
size_t count, // special semantics for the value _TRUNCATE
const char *format,
va_list argptr
);
As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable.
You did not post the code where you want a replacement for snprintf()
, but a classic use case is this:
/* allocate a string formated according to `format` */
int vasprintf(char **strp, const char *format, va_list ap) {
va_list arg;
int ret;
if (!strp) {
errno = EINVAL;
return -1;
}
va_copy(arg, ap);
ret = vsnprintf(NULL, 0, format, arg);
va_end(arg);
*strp = NULL;
if (ret < 0 || (*strp = malloc(ret + 1)) == NULL) {
return -1;
}
return vsnprintf(*strp, ret + 1, format, ap);
}
In the above code, vnsprintf
cannot be replaced with vsprintf_s
because vsprintf_s
cannot be used to compute the required size: it always considers a short size as an error. vsnprintf_s
cannot be used as a direct replacement because it considers a NULL
pointer as the target to be an error too.
The solution is simple: you can prevent Visual Studio from emitting this warning by defining a macro before including <stdio.h>
in the source file:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS // let me use standard functions
#endif
This prevents the warning for all deprecated library functions.
Note also that it is very useful to increase the warning level and let the compiler warn about potential bugs, such as inconsistent arguments for a given format string. With gcc
and clang
, you can pass -Wall
and other command line options to enable such behavior. For Visual Studio, you can add more warnings with /W4
.
The solution is to always add
#define _CRT_SECURE_NO_WARNINGS
as the first line of code when compiling with Visual Studio. Or maybe the second line if you're writing cross-platform code and the first line is something like #ifdef _WIN32
, such as
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
This will disable warnings that Microsoft has "deprecated" functions required by the C Standard.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With