I'm puzzled by the following difference in behaviour:
// suppose myfile.txt contains a single line with the single character 's'
errno_t res;
FILE* fp;
char cmd[81];
res = fopen_s(&fp, "D:\\myfile.txt", "rb" );
fscanf(fp,"%80s",cmd); // cmd now contains 's/0'
fclose(fp);
res = fopen_s(&fp, "D:\\myfile.txt", "rb" );
fscanf_s(fp,"%80s",cmd); // cmd now contains '/0' !
fclose(fp);
The results do not depend in the order of call (i.e., call fscanf_s first, you'd get the empty string first). Compiled on VC++ - VS2005. Can anyone reproduce? Can anyone explain?
Thanks!
From the docs on fscanf_s(), http://msdn.microsoft.com/en-us/library/6ybhk9kc.aspx:
The main difference between the secure functions (with the _s suffix) and the older functions is that the secure functions require the size of each c, C, s, S and [ type field to be passed as an argument immediately following the variable. For more information, see scanf_s, _scanf_s_l, wscanf_s, _wscanf_s_l and scanf Width Specification.
And http://msdn.microsoft.com/en-us/library/w40768et.aspx:
Unlike scanf and wscanf, scanf_s and wscanf_s require the buffer size to be specified for all input parameters of type c, C, s, S, or [. The buffer size is passed as an additional parameter immediately following the pointer to the buffer or variable. For example, if reading a string, the buffer size for that string is passed as follows:
char s[10];
scanf("%9s", s, 10);
So you should call it like so:
fscanf_s(fp,"%80s",cmd, sizeof(cmd));
fscanf_s (and the whole scanf_s family) requires that you pass the size of any %c, %C, %s, %S, or %[ after the buffer itself; you're omitting that argument:
fscanf_s(fp, "%80s", cmd, 81);
Your question is tagged C++ and you're compiling in VC++, but using fscanf? Get a std::ifstream.
std::string buffer;
std::ifstream fp("my filepath");
fp >> buffer;
I think there is a chance of misleading result in:
fscanf_s(fp,"%80s",cmd, sizeof(cmd));
Instead it should be like:
fscanf_s(fp,"%80s",cmd, countof(cmd));
where countof(cmd) is total size of memory block allocated to cmd which must be greater than 80, sizeof(cmd) would be either 4 or 8 which will give error in the case.
Related
I'm trying to open a file whose name is composed by constant and variable parts.
My actual code is
char filename[100];
char extension1[] = ".pdb";
vector<string> id;
//code to find the ids(it works)
sprintf(filename, "/home/giovanni/Scrivania/enzimi/ligan/%s", id[1].c_str());
sprintf(filename, "%s%s", filename,extension1);
The problem is that filenames becomes
.pdbe/giovanni/Scrivania/enzimi/ligan/102M
instead of
/home/giovanni/Scrivania/enzimi/ligan/102M.pdb
Simply use std::string:
string filename = "/home/giovanni/Scrivania/enzimi/ligan/" + id[1] + ".pdb";
...
std::ifstream file(filename.c_str());
Simply using this code would do (sprintf takes variable number of arguments):
sprintf(filename,"/home/giovanni/Scrivania/enzimi/ligan/%s%s", id[1].c_str(), extension1);
But, as you are using C++, Doing it in C style is not preferable. sprintf() can cuase buffer overflows and you can you safer version snprintf(). Best option would be to use std::string
C99 and POSIX.1-2001 specify that the results are undefined if a call to sprintf()/snprintf would cause copying to take place between objects that overlap (e.g., if the target string array and one of the supplied input arguments refer to the same buffer).
So line sprintf(filename, "%s%s", filename,extension1) is illegal.
You can try other options like std::string.
Watch Out For Buffer Overflow!
You need to append to the existing string; you need to know the number of characters in it, then offset the pointer you pass in to the second call to sprintf:
int len = sprintf(filename, "/home/giovanni/Scrivania/enzimi/ligan/%s", id[1].c_str());
sprintf(filename + len, "%s", extension1);
(sprintf returns the number of characters written into the buffer, but does not include the NUL terminator.)
Note that the second call to sprintf only has one "%s".
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.
I'm using fputs in C++ to write a string in a file.
fputs (const char*, FILE*);
If I use a simple statement like,
fputs ("information", pFile);
everything is ok and "information" will be written in the file. But if I write a variable of type,
std::vector<std::string>
into the file, some non-ascii characters are stored in the file. Do I have to use a method to convert type std::vector<std::string> into a format which fputs can recognize ?
That correct, fputs does not understand how a std::vector is laid out in memory.
You should actually have got a compile error when you tried to pass a std::vector to fputs(). Did you try to work around the error by adding a cast or something?
Use
fprintf ( pFile, "%s", iterVar.at(currentRun).c_str() )
instead of
fprintf ( pFile, "%s", iterVar.at(currentRun).data() )
You don't want to use data() ever as it is missing the trailing \0.
(This code is based on the questioner's comment on #GregHewgill's answer, where iterVar is of type vector<string>.)
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
This question already has answers here:
C++: how to get fprintf results as a std::string w/o sprintf
(8 answers)
Closed 5 years ago.
Does anyone know a good safe way to redirect the output of a printf-style function to a string? The obvious ways result in buffer overflows.
Something like:
string s;
output.beginRedirect( s ); // redirect output to s
... output.print( "%s%d", foo, bar );
output.endRedirect();
I think the problem is the same as asking, "how many characters will print produce?"
Ideas?
You can use:
std::snprintf if you are working with a char*
std::stringstream if you want to use strings (not same as printf but will allow you to easily manipulate the string using the normal stream functions).
boost::format if you want a function similar to printf that will work with streams. (as per jalf in comments)
fmt::format which is has been standardized since c++20 std::format
The snprintf() function prints to a string, but only as much as the length given to it.
Might be what you're looking for...
The fmt library provides fmt::sprintf function that performs printf-compatible formatting (including positional arguments according to POSIX specification) and returns the result as an std::string:
std::string s = fmt::sprintf( "%s%d", foo, bar );
Disclaimer: I'm the author of this library.
Since you've tagged this as C++ (rather than just C), I'll point out that the typical way to do this sort of thing in C++ is to use stringstream, not the printf family. No need to worry about buffer overflows with stringstreams.
The Boost Format library is also available if you like printf-style format strings but want something safer.
snprintf() returns the number of bytes needed to write the whole string.
So, as a tiny example:
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
char* buf = 0;
size_t bufsize = 0;
size_t sz;
const char* format="%s and %s.";
const char* str_1 ="string 1";
const char* str_2 ="string 2";
sz = snprintf(buf, bufsize, format, str_1, str_2);
printf("The buffer needs to be %d bytes.\n", sz);
buf=malloc(sz+1);
if(!buf) {
printf("Can't allocate buffer!\n");
return 1;
}
bufsize = sz+1;
buf[bufsize-1] = '\0';
sz = snprintf(buf, bufsize, format, str_1, str_2);
printf("Filled buffer with %d bytes.\n", sz);
printf("The buffer contains:\n'%s'\n", buf);
return 0;
}
output:
The buffer needs to be 22 bytes.
Filled buffer with 22 bytes.
The buffer contains:
'string 1 and string 2.'
This StackOverflow question has a similar discussion. Also in that question I present my favorite solution, a "format" function that takes identical arguments to printf and returns a std::string.
Old school:
snprintf()
allows you to put a limit on the number written, and return the actual written size, and
asprintf()
allocate (with malloc()) a sufficient buffer which then becomes your problem to free(). `asprintf is a GNU libc function now reimplemented in the BSD libc.
With C99 you have the snprintf-function which takes the size of the buffer as a parameter. The GNU C-library has asprintf which allocates a buffer for you. For c++ though, you might be better of using iostream.
Wikipedia has more info.
I find the printf formatting to be very helpful and easier to use than streams. On the other hand, I do like std::string a lot too. The solution is to use sprintf, but that cannot handle arbitrary buffer size.
I've found that I need to handle common case (say, buffer limited to 256 chars) w/o
overhead, and yet handle the large buffer safely. To do that, I have a buffer of 256 chars alocated in my class as a member, and I use snprinf, passing that buffer and its size. If snprintf succeeds, I can immediately retunr the formatted string. If it fails, I allocate the buffer and call snprinf again. The buffer is deallocated in the class' destructor.
On Windows:
StringCchPrintf
StringCbPrintf
from strsafe.h/lib.
Microsoft introduce the 'safe' crt functions for this.
You could use printf_s()