Crash C++ application using sprintf_s without specifying string length - c++

I have a c++ application, in which customer reported a crash.But the crash is not easily reproducible.
After analysing some logs and all i found that the crash may occure in between the following code portions. Please tell me there is any chance of getting crashed the application if i have these code statements in it?
//Tesrt
std::string strAppName = "App1\0";
int nSize = 10;
sprintf_s(szBuff, "The appname %s have %d dependancies ", strAppName.c_str(), nSize);
//Then use the szBuff to log to a text file
//Test end

The problem is that you've not provided the correct arguments to sprintf_s:
int sprintf_s(
char *buffer,
size_t sizeOfBuffer,
const char *format [,
argument] ...
);
sprintf_s takes a size_t as it's second argument (the size of szBuff), but you've not provided that. Instead, you've given it a const char * where that parameter should be. The only way to have compiled this is for you to have ignored compiler warnings.
So what sprintf_s is seeing is:
buffer to print into
large number of characters allowed to go into buffer
strAppName.c_str() as the format string
In other words, this isn't doing anything like what you want. Provide the size of szBuff as the second parameter, and I'll bet your problems go away.
And yes, given what you've done I'd expect crashes all over the place.

Related

Coverity issue: Copy of overlapping memory (OVERLAPPING_COPY) on sprintf statement

char arr[512];
...
sprintf(arr, "%s %30s", arr, "Some Text");
I'm getting the following message for the sprintf statement:
In the call to function sprintf, the arguments arr and arr may point to the same object.
Is there another way to implement this kind of formatting?
This code code contains two unrelated errors. As for the PVS-Studio analyzer, it issues two following warnings:
V576 Incorrect format. A different number of actual arguments is expected while calling 'sprintf' function. Expected: 4. Present: 3. test.cpp 54
V541 It is dangerous to print the 'arr' string into itself. test.cpp 54
The first one implies that the function is passed insufficient number of actual arguments. Indeed, the format string indicates that a string and an integer number are expected as arguments. But only a string is passed. There is no numeric argument, resulting in the usage of an amount of memory with a random value and consequently undefined behavior.
The second warning tells us that there is no guarantee that the sprintf function works correctly if one buffer is used as an input and output buffer. Such code might work correctly or it might not. It all depends on the implementation of the sprintf function. In any case, there is no reason to write code in such a way.
Therefore, Coverity is absolutely right when issuing the warning for this code. The code is definitely incorrect.
P.S. It reminded me of another funny case related to the usage of a "fake sprintf" :).
The overlapping warning has to do with the fact that you are copying arr to itself, thus overwriting it's content.
Furthermore in the code:
sprintf(arr, "%s %0x", arr);
^^^
%s takes the arr string, but %0x doesn't take an unsigned hexadecimal integer variable as it should, so you are probably missing a parameter.
Something along the lines of:
char arr[512];
char arr2[1024];
unsigned int x = 15;
sprintf(arr2, "%s %0x", arr, x);

vsnprintf crash program in case %s is set with an integer

I'm writing a logger in C++ and to simplify entering lines I use vsnprintf function to build log line
void CLogger::RegManLog(const LogLevel & logLevelMask, char * Format, ...)
...
...
va_start(marker_, Format);
vsnprintf(buffer_ ,MaxLogBuffSize , Format, marker_)
va_end(marker_);
printer_ += buffer_;
...
...
every thing works great up until I accidently enter a number into a string
integer test = 10;
eg.: "now I'll show a string %s" , test
tried to add "try and catch", but I think vsnprintf does have throw, so it crashes any way.
tried to get return value from vsnprintf, it return value, while string are fine, when reaching the same issue, it crashes
any thoughts of I can i solve this issue?
thanks
%s expects to get a char*. When you pass in 10, it treats it like an address, goes there and kills your program.
If you wish to print integers, use %d. For more information look at http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html
In C++ it is best to use other methods to accomplish what you need, like std::stringstream
You could avoid printf and friends and instead use std::stringstream or boost::format
Well, when passed to printf() and family, the %s formatter is here as a placeholder for a -eventually const- char * pointer.
What is happening, is that your integer is read as a pointer, and it's likely that the memory address (10 in your example) is invalid.

sprintf_s crashes

I am getting a crash while executing the following code ocassionally at sprintf_s. This code was working many years without any problems. When I gave the size in strcat_s and sprintf_s as in the statements below, the crash is not appearing. What could be the reason for this?
strcat_s(sztmpCurrDate,100,sztmpCurrTime);
sprintf_s(sztmpCurrDate,100,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds););
char sztmpCurrDate[100] = "";
char sztmpCurrTime[100] = "";
SYSTEMTIME curTime;
GetLocalTime(&curTime);
GetLocalTime(&curTime);
GetDateFormat(LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
&curTime,
NULL,
sztmpCurrDate,
100);
GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_FORCE24HOURFORMAT,
&curTime,
"HH':'mm':'ss",
sztmpCurrTime,
100);
strcat_s(sztmpCurrDate," ");
strcat_s(sztmpCurrDate,sztmpCurrTime);
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
From the documentation for sprintf_s:
If copying occurs between strings that overlap, the behavior is undefined.
Your code:
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
copies from the source to the destination sztmpCurrDate. Also, you haven't specified the size of the destination string, which is required by sprintf_s (I don't know how your code even compiled like that). Try:
sprintf_s(sztmpCurrDate + strlen(sztmpCurrDate), 100-strlen(sztmpCurrDate),
":%0.3d",curTime.wMilliseconds);
A better approach, since you're using C++, is to use std::string and then you won't have to worry about this sort of C string manipulation error.
Wrong syntax!!!
Second argument of sprintf_s is length of your buffer and in your case when program crashes you provided pointer to C string (char *). This is absolutely syntax error.
The compiler probably issued a warning, but sadly has let this pass. This is because sprintf_s takes three arguments + variable number of arguments. In your wrong case you provided three arguments so the compiler was satisfied, but he treated your "format string" as "number of arguments" and sztmpCurrDate as "format string".
If you used any other function with fixed number of arguments and you provided less than needed this would be a compile error.
You are probably using the incorrect version:
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Instead it should be:
int slen = strlen(sztmpCurrDate) + 1;
sprintf_s(sztmpCurrDate, slen, "%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Refer to this sprintf_s for more information.
The code worked correctly, the code is not correct.
sprintf(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Rewrite as
sprintf(&sztmpCurrDate[strlen(sztmpCurrDate)],"%0.3d",curTime.wMilliseconds);
And for home work tell us why .....

understanding the dangers of sprintf(...)

OWASP says:
"C library functions such as strcpy
(), strcat (), sprintf () and vsprintf
() operate on null terminated strings
and perform no bounds checking."
sprintf writes formatted data to string
int sprintf ( char * str, const char * format, ... );
Example:
sprintf(str, "%s", message); // assume declaration and
// initialization of variables
If I understand OWASP's comment, then the dangers of using sprintf are that
1) if message's length > str's length, there's a buffer overflow
and
2) if message does not null-terminate with \0, then message could get copied into str beyond the memory address of message, causing a buffer overflow
Please confirm/deny. Thanks
You're correct on both problems, though they're really both the same problem (which is accessing data beyond the boundaries of an array).
A solution to your first problem is to instead use std::snprintf, which accepts a buffer size as an argument.
A solution to your second problem is to give a maximum length argument to snprintf. For example:
char buffer[128];
std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");
// std::strcmp(buffer, "This is a test\n") == 0
If you want to store the entire string (e.g. in the case sizeof(buffer) is too small), run snprintf twice:
int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");
++length; // +1 for null terminator
char *buffer = new char[length];
std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");
(You can probably fit this into a function using va or variadic templates.)
Both of your assertions are correct.
There's an additional problem not mentioned. There is no type checking on the parameters. If you mismatch the format string and the parameters, undefined and undesirable behavior could result. For example:
char buf[1024] = {0};
float f = 42.0f;
sprintf(buf, "%s", f); // `f` isn't a string. the sun may explode here
This can be particularly nasty to debug.
All of the above lead many C++ developers to the conclusion that you should never use sprintf and its brethren. Indeed, there are facilities you can use to avoid all of the above problems. One, streams, is built right in to the language:
#include <sstream>
#include <string>
// ...
float f = 42.0f;
stringstream ss;
ss << f;
string s = ss.str();
...and another popular choice for those who, like me, still prefer to use sprintf comes from the boost Format libraries:
#include <string>
#include <boost\format.hpp>
// ...
float f = 42.0f;
string s = (boost::format("%1%") %f).str();
Should you adopt the "never use sprintf" mantra? Decide for yourself. There's usually a best tool for the job and depending on what you're doing, sprintf just might be it.
Yes, it is mostly a matter of buffer overflows. However, those are quite serious business nowdays, since buffer overflows are the prime attack vector used by system crackers to circumvent software or system security. If you expose something like this to user input, there's a very good chance you are handing the keys to your program (or even your computer itself) to the crackers.
From OWASP's perspective, let's pretend we are writing a web server, and we use sprintf to parse the input that a browser passes us.
Now let's suppose someone malicious out there passes our web browser a string far larger than will fit in the buffer we chose. His extra data will instead overwrite nearby data. If he makes it large enough, some of his data will get copied over the webserver's instructions rather than its data. Now he can get our webserver to execute his code.
Your 2 numbered conclusions are correct, but incomplete.
There is an additional risk:
char* format = 0;
char buf[128];
sprintf(buf, format, "hello");
Here, format is not NULL-terminated. sprintf() doesn't check that either.
Your interpretation seems to be correct. However, your case #2 isn't really a buffer overflow. It's more of a memory access violation. That's just terminology though, it's still a major problem.
The sprintf function, when used with certain format specifiers, poses two types of security risk: (1) writing memory it shouldn't; (2) reading memory it shouldn't. If snprintf is used with a size parameter that matches the buffer, it won't write anything it shouldn't. Depending upon the parameters, it may still read stuff it shouldn't. Depending upon the operating environment and what else a program is doing, the danger from improper reads may or may not be less severe than that from improper writes.
It is very important to remember that sprintf() adds the ASCII 0 character as string terminator at the end of each string. Therefore, the destination buffer must have at least n+1 bytes (To print the word "HELLO", a 6-byte buffer is required, NOT 5)
In the example below, it may not be obvious, but in the 2-byte destination buffer, the second byte will be overwritten by ASCII 0 character. If only 1 byte was allocated for the buffer, this would cause buffer overrun.
char buf[3] = {'1', '2'};
int n = sprintf(buf, "A");
Also note that the return value of sprintf() does NOT include the null-terminating character. In the example above, 2 bytes were written, but the function returns '1'.
In the example below, the first byte of class member variable 'i' would be partially overwritten by sprintf() (on a 32-bit system).
struct S
{
char buf[4];
int i;
};
int main()
{
struct S s = { };
s.i = 12345;
int num = sprintf(s.buf, "ABCD");
// The value of s.i is NOT 12345 anymore !
return 0;
}
I pretty much have stated a small example how you could get rid of the buffer size declaration for the sprintf (if you intended to, of course!) and no snprintf envolved ....
Note: This is an APPEND/CONCATENATION example, take a look at here

Is there any way to determine how many characters will be written by sprintf?

I'm working in C++.
I want to write a potentially very long formatted string using sprintf (specifically a secure counted version like _snprintf_s, but the idea is the same). The approximate length is unknown at compile time so I'll have to use some dynamically allocated memory rather than relying on a big static buffer. Is there any way to determine how many characters will be needed for a particular sprintf call so I can always be sure I've got a big enough buffer?
My fallback is I'll just take the length of the format string, double it, and try that. If it works, great, if it doesn't I'll just double the size of the buffer and try again. Repeat until it fits. Not exactly the cleverest solution.
It looks like C99 supports passing NULL to snprintf to get the length. I suppose I could create a module to wrap that functionality if nothing else, but I'm not crazy about that idea.
Maybe an fprintf to "/dev/null"/"nul" might work instead? Any other ideas?
EDIT: Alternatively, is there any way to "chunk" the sprintf so it picks up mid-write? If that's possible it could fill the buffer, process it, then start refilling from where it left off.
The man page for snprintf says:
Return value
Upon successful return, these functions return the number of
characters printed (not including the trailing '\0' used to end
output to strings). The functions snprintf and vsnprintf do not
write more than size bytes (including the trailing '\0'). If
the output was truncated due to this limit then the return value
is the number of characters (not including the trailing '\0')
which would have been written to the final string if enough
space had been available. Thus, a return value of size or more
means that the output was truncated. (See also below under
NOTES.) If an output error is encountered, a negative value is
returned.
What this means is that you can call snprintf with a size of 0. Nothing will get written, and the return value will tell you how much space you need to allocate to your string:
int how_much_space = snprintf(NULL, 0, fmt_string, param0, param1, ...);
As others have mentioned, snprintf() will return the number of characters required in a buffer to prevent the output from being truncated. You can simply call it with a 0 buffer length parameter to get the required size then use an appropriately sized buffer.
For a slight improvement in efficiency, you can call it with a buffer that's large enough for the normal case and only do a second call to snprintf() if the output is truncated. In order to make sure the buffer(s) are properly freed in that case, I'll often use an auto_buffer<> object that handles the dynamic memory for me (and has the default buffer on the stack to avoid a heap allocation in the normal case).
If you're using a Microsoft compiler, MS has a non-standard _snprintf() that has serious limitations of not always null terminating the buffer and not indicating how big the buffer should be.
To work around Microsoft's non-support, I use a nearly public domain snprintf() from Holger Weiss.
Of course if your non-MS C or C++ compiler is missing snprintf(), the code from the above link should work just as well.
I would use a two-stage approach. Generally, a large percentage of output strings will be under a certain threshold and only a few will be larger.
Stage 1, use a reasonable sized static buffer such as 4K. Since snprintf() can restrict how many characters are written, you won't get a buffer overflow. What you will get returned from snprintf() is the number of characters it would have written if your buffer had been big enough.
If your call to snprintf() returns less than 4K, then use the buffer and exit. As stated, the vast majority of calls should just do that.
Some will not and that's when you enter stage 2. If the call to snprintf() won't fit in the 4K buffer, you at least now know how big a buffer you need.
Allocate, with malloc(), a buffer big enough to hold it then snprintf() it again to that new buffer. When you're done with the buffer, free it.
We worked on a system in the days before snprintf() and we acheived the same result by having a file handle connected to /dev/null and using fprintf() with that. /dev/null was always guaranteed to take as much data as you give it so we would actually get the size from that, then allocate a buffer if necessary.
Keep in kind that not all systems have snprintf() (for example, I understand it's _snprintf() in Microsoft C) so you may have to find the function that does the same job, or revert to the fprintf /dev/null solution.
Also be careful if the data can be changed between the size-checking snprintf() and the actual snprintf() to the buffer (i.e., wathch out for threads). If the sizes increase, you'll get buffer overflow corruption.
If you follow the rule that data, once handed to a function, belongs to that function exclusively until handed back, this won't be a problem.
For what it's worth, asprintf is a GNU extension that manages this functionality. It accepts a pointer as an output argument, along with a format string and a variable number of arguments, and writes back to the pointer the address of a properly-allocated buffer containing the result.
You can use it like so:
#define _GNU_SOURCE
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *hi = "hello"; // these could be really long
char *everyone = "world";
char *message;
asprintf(&message, "%s %s", hi, everyone);
puts(message);
free(message);
return 0;
}
Hope this helps someone!
Take a look at CodeProject: CString-clone Using Standard C++. It uses solution you suggested with enlarging buffer size.
// -------------------------------------------------------------------------
// FUNCTION: FormatV
// void FormatV(PCSTR szFormat, va_list, argList);
//
// DESCRIPTION:
// This function formats the string with sprintf style format-specs.
// It makes a general guess at required buffer size and then tries
// successively larger buffers until it finds one big enough or a
// threshold (MAX_FMT_TRIES) is exceeded.
//
// PARAMETERS:
// szFormat - a PCSTR holding the format of the output
// argList - a Microsoft specific va_list for variable argument lists
//
// RETURN VALUE:
// -------------------------------------------------------------------------
void FormatV(const CT* szFormat, va_list argList)
{
#ifdef SS_ANSI
int nLen = sslen(szFormat) + STD_BUF_SIZE;
ssvsprintf(GetBuffer(nLen), nLen-1, szFormat, argList);
ReleaseBuffer();
#else
CT* pBuf = NULL;
int nChars = 1;
int nUsed = 0;
size_type nActual = 0;
int nTry = 0;
do
{
// Grow more than linearly (e.g. 512, 1536, 3072, etc)
nChars += ((nTry+1) * FMT_BLOCK_SIZE);
pBuf = reinterpret_cast<CT*>(_alloca(sizeof(CT)*nChars));
nUsed = ssnprintf(pBuf, nChars-1, szFormat, argList);
// Ensure proper NULL termination.
nActual = nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1);
pBuf[nActual+1]= '\0';
} while ( nUsed < 0 && nTry++ < MAX_FMT_TRIES );
// assign whatever we managed to format
this->assign(pBuf, nActual);
#endif
}
I've looked for the same functionality you're talking about, but as far as I know, something as simple as the C99 method is not available in C++, because C++ does not currently incorporate the features added in C99 (such as snprintf).
Your best bet is probably to use a stringstream object. It's a bit more cumbersome than a clearly written sprintf call, but it will work.
Since you're using C++, there's really no need to use any version of sprintf. The simplest thing to do is use a std::ostringstream.
std::ostringstream oss;
oss << a << " " << b << std::endl;
oss.str() returns a std::string with the contents of what you've written to oss. Use oss.str().c_str() to get a const char *. It's going to be a lot easier to deal with in the long run and eliminates memory leaks or buffer overruns. Generally, if you're worrying about memory issues like that in C++, you're not using the language to its full potential, and you should rethink your design.