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".
Related
Hi i'm using visual studio and trying to make a program that replicate itself to a disk, when i run it does just that, but then i get the message:
"*Run-Time Check Failure #2 - Stack around the variable 'folderPath' was corrupted*."
the code is as follows:
void copyToDrive(char driveLetter) {
char folderPath[10] = { driveLetter };
strcat(folderPath, ":\\");
strcat(folderPath, FILE_NAME);
char filename[MAX_PATH];
DWORD size = GetModuleFileNameA(NULL, filename, MAX_PATH);
std::ifstream src(filename, std::ios::binary);
std::ofstream dest(folderPath, std::ios::binary);
dest << src.rdbuf();
return;
}
what is causing it? and how can i fix that?
The string "app.exe" is seven characters long. That means the total length of the string you construct will be ten characters long.
Unfortunately you seem to forget that char strings in C++ are really called null-terminated byte strings, and that the null-terminator also needs space.
Since there is no space for the null-terminator (character '\0') the last strcat call will write out of bounds of your folderPath array, leading to undefined behavior (and the error you get).
The simple solution is to add one element to the array to make space for the terminator as well:
char folderPath[11];
A more correct solution is to use std::string instead, and not have to worry about the length.
And since you are working with paths I would suggest you use std::filesystem::path (or Boost filesystem path if you don't have C++17 available).
could you say me what is the mistake in my following code?
char* line="";
printf("Write the line.\n");
scanf("%s",line);
printf(line,"\n");
I'm trying to get a line as an input from the console.But everytime while using "scanf" the program crashes. I don't want to use any std, I totally want to avoid using cin or cout. I'm just trying to learn how to tak a full line as an input using scanf().
Thank you.
You need to allocate the space for the input string as sscanf() cannot do that itself:
char line[1024];
printf("Write the line.\n");
scanf("%s",line);
printf(line,"\n");
However this is dangerous as it's possible to overflow the buffer and is therefore a security concern. Use std::string instead:
std::string line;
std::cout << "Write the line." << std::endl;
std::cin >> line;
std::cout << line << std::endl;
or:
std::getline (std::cin, line);
Space not allocated for line You need to do something like
char *line = malloc();
or
Char line[SOME_VALUE];
Currently line is a poor pointer pointing at a string literal. And overwriting a string literal can result in undefined behaviour.
scanf() doesn't match lines.
%s matches a single word.
#include <stdio.h>
int main() {
char word[101];
scanf("%100s", word);
printf("word <%s>\n", word);
return 0;
}
input:
this is a test
output:
word <this>
to match the line use %100[^\n"] which means 100 char's that aren't newline.
#include <stdio.h>
int main() {
char word[101];
scanf("%100[^\n]", word);
printf("word <%s>\n", word);
return 0;
}
You are trying to change a string literal, which in C results in Undefined behavior, and in C++ is trying to write into a const memory.
To overcome it, you might want to allocate a char[] and assign it to line - or if it is C++ - use std::string and avoid a lot of pain.
You should allocate enough memory for line:
char line[100];
for example.
The %s conversion specifier in a scanf call expects its corresponding argument to point to a writable buffer of type char [N] where N is large enough to hold the input.
You've initialized line to point to the string literal "". There are two problems with this. First is that attempting to modify the contents of a string literal results in undefined behavior. The language definition doesn't specify how string literals are stored; it only specifies their lifetime and visibility, and some platforms stick them in a read-only memory segment while others put them in a writable data segment. Therefore, attempting to modify the contents of a string literal on one platform may crash outright due to an access violation, while the same thing on another platform may work fine. The language definition doesn't mandate what should happen when you try to modify a string literal; in fact, it explicitly leaves that behavior undefined, so that the compiler is free to handle the situation any way it wants to. In general, it's best to always assume that string literals are unwritable.
The other problem is that the array containing the string literal is only sized to hold 1 character, the 0 terminator. Remember that C-style strings are stored as simple arrays of char, and arrays don't automatically grow when you add more characters.
You will need to either declared line as an array of char or allocate the memory dynamically:
char line[MAX_INPUT_LEN];
or
char *line = malloc(INITIAL_INPUT_LEN);
The virtue of allocating the memory dynamically is that you can resize the buffer as necessary.
For safety's sake, you should specify the maximum number of characters to read; if your buffer is sized to hold 21 characters, then write your scanf call as
scanf("%20s", line);
If there are more characters in the input stream than what line can hold, scanf will write those extra characters to the memory following line, potentially clobbering something important. Buffer overflows are a common malware exploit and should be avoided.
Also, %s won't get you the full line; it'll read up to the next whitespace character, even with the field width specifier. You'll either need to use a different conversion specifier like %[^\n] or use fgets() instead.
The pointer line which is supposed to point to the start of the character array that will hold the string read is actually pointing to a string literal (empty string) whose contents are not modifiable. This leads to an undefined behaviour manifested as a crash in your case.
To fix this change the definition to:
char line[MAX]; // set suitable value for MAX
and read atmost MAX-1 number of characters into line.
Change:
char* line="";
to
char line[max_length_of_line_you_expect];
scanf is trying to write more characters than the reserved by line. Try reserving more characters than the line you expect, as been pointed out by the answers above.
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
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.
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()