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

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);

Related

Can std::string::c_str() be used whenever a string literal is expected?

I would guess that the last two lines in this code should compile.
#include "rapidjson/document.h"
int main(){
using namespace rapidjson ;
using namespace std ;
Document doc ;
Value obj(kObjectType) ;
obj.AddMember("key", "value", doc.GetAllocator()) ; //this compiles fine
obj.AddMember("key", string("value").c_str(), doc.GetAllocator()) ; //this does not compile!
}
My guess would be wrong, though. One line compiles and the other does not.
The AddMember method has several variants as documented here, but beyond that... why is the return of .c_str() not equivalent to a string literal?
My understanding was that where ever a string literal was accepted, you could pass string::c_str() and it should work.
PS: I'm compiling with VC++ 2010.
EDIT:
The lack of #include <string> is not the problem. It's already included by document.h
This is the error:
error C2664: 'rapidjson::GenericValue<Encoding> &rapidjson::GenericValue<Encoding>::AddMember(rapidjson::GenericValue<Encoding> &,rapidjson::GenericValue<Encoding> &,Allocator &)'
: cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue<Encoding> &'
with
[
Encoding=rapidjson::UTF8<>,
Allocator=rapidjson::MemoryPoolAllocator<>
]
and
[
Encoding=rapidjson::UTF8<>
]
EDIT2:
Please ignore the fact that .c_str() is called on a temporal value. This example is just meant to show the compile error. The actual code uses a string variable.
EDIT3:
Alternate version of the code:
string str("value") ;
obj.AddMember("key", "value", doc.GetAllocator()) ; //compiles
obj.AddMember("key", str, doc.GetAllocator()) ; // does not compile
obj.AddMember("key", str.c_str(), doc.GetAllocator()) ; // does not compile
The std::string::c_str() method returns a char const*. The type of a string literal is char const[N] where N is the number of characters in the string (including the null terminator). Correspondingly, the result of c_str() can not be used in all places where a string literal can be used!
I'd be surprised if the interface you are trying to call requires a char array, though. That is, in your use it should work. It is more likely that you need to include <string>.
even if this code compiled:
obj.AddMember("key2", string("value").c_str(), doc.GetAllocator());
You cannot guarantee that it is safe.
The const char* returned by std::string::c_str() will be valid until the end of this statement.
If the AddMember method stores a copy of the string itself, all well and good. If it stores a pointer then you're doomed. You need knowledge of the inner workings of AddMember before you can reason about the correctness of your code.
I suspect the authors have already thought of this and have constructed overloads that demand that you either send in a std::string object (or equivalent) or a string literal reference (template<std::size_t N> void AddMember(const char (&str)[N]))
Even if this is not what they had in mind, they might be looking to protect you from yourself, in case you inadvertently send in an invalid pointer.
While seemingly an inconvenience, this compile time error indicates a possibly-faulty program. It's a tribute to the library's authors. Because compile time errors are a gazillion times more useful than runtime errors.
Looking at the documentation you linked to, it seems like you are trying to call the overload of AddMember taking two StringRefTypes (and an Allocator). StringRefType is a typedef for GenericStringRef<Ch>, which has two overloaded constructors taking a single argument:
template<SizeType N>
GenericStringRef(const CharType(&str)[N]) RAPIDJSON_NOEXCEPT;
explicit GenericStringRef(const CharType *str);
When you pass a string literal, the type is const char[N], where N is the length of the string + 1 (for the null terminator). This can be implicitly converted to a GenericStringRef<Ch> using the first constructor overload. However, std::string::c_str() returns a const char*, which cannot be converted implicitly to a GenericStringRef<Ch>, because the second constructor overload is declared explicit.
The error message you get from the compiler is caused by it choosing another overload of AddMember which is a closer match.
Re
” why is the return of .c_str() not equivalent to a string literal
A string literal is a zero-terminated string in an array with size known at compile time.
c_str() produces a pointer to (the first item in) a zero-terminated string in an array with size known only at run-time.
Usually a string literal expression will be used in a context where the expression decays to pointer to first item, but in some special cases it does not decays. These cases include
binding to a reference to array,
using the sizeof operator, and
forming a larger literal by compile time concatenation of string literals (simply writing them in order).
I think that's an exhaustive list.
The error message you cite,
” cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue &
… does not match your presented code
#include "rapidjson/document.h"
int main(){
using namespace rapidjson ;
using namespace std ;
Document doc ;
Value obj(kObjectType) ;
obj.AddMember("key1", "value", doc.GetAllocator()) ; //this compiles fine
obj.AddMember("key2", string("value").c_str(), doc.GetAllocator()) ; //this does not compile!
}
Nowhere in this code is there a three character long string literal.
Hence the claims that “this compiles” and “this does not compile”, are not very trustworthy.
You
should have quoted the actual error message and actual code (at least one of them is not what you had when you compiled), and
should have quoted the documentation of the function you're calling.
Also, note that the actual argument that compiler reacts to in the quoted diagnostic, is a literal or an array declared as such, not a c_str() call.

stack around the variable...was corrupted

I have a simple function that writes some data to a new file. It works, and the file is written, but I get the above mentioned error while debugging in MSVS Express 2013.
void writeSpecToFile(const char *fname); //in header file.
char myChar [20];
sprintf(myChar, "aa%03daa%daa", i1, i2);
const char* new_char = myChar;
writeSpecToFile(myChar);
As seen, I simply insert some variables into a string using sprintf (works fine). Now whether I pass myChar or new_char, it still gives me the corruption error.
What went wrong?
Why did you declare you character buffer a size of 20? More than likely the sprintf placed more characters than that can fit in myChar.
Instead, use
safer constructs such as std::ostringstream or
at the very least, declare you char arrays much bigger than you would expect (not the best way, but would at least not have had the error occur).
If you're going along the "guess the biggest size for my array"
route, the last thing you want to do is attempt to count, right down
to the last character, how big to make the buffer. If you're off by a single byte, that can cause a crash.
Assuming 32-bit int, printing one with %d will yield a maximum of 8 visible characters.
Your format-string also contains 6 literal a-characters, and we should not forget the 0-terminator.
All in all: 2*8+6+1 = 23 > 20 !!
Your buffer must be at least 23 byte big, unless there are other undisclosed input-restrictions.
Personally, I would give it a round 32.
Also, better use snprintf and optionally verify the full string did actually fit (if it does not fit you get a shortened string, so no catastrophe).
char myChar [32];
snprintf(myChar, sizeof myChar, "aa%03daa%daa", i1, i2);
Beware that the Microsoft implementation is non-conforming and does not guarantee 0-termination.

Scanf function with strings

I need to read input from user. The input value may be string type or int type.
If the value is int then the program insert the value into my object.
Else if the value is string then it should check the value of that string, if it's "end" then the program ends.
Halda h; //my object
string t;
int tint;
bool end=false;
while(end!=true)
{
if(scanf("%d",&tint)==1)
{
h.insert(tint);
}
else if(scanf("%s",t)==1)
{
if(t=="end")
end=true;
else if(t=="next")
if(h.empty()==false)
printf("%d\n",h.pop());
else
printf("-1\n");
}
}
The problem is that scanning string doesn't seem to work properly.
I've tried to change it to: if(cin>>t) and it worked well.
I need to get it work with scanf.
The specifier %s in the scanf() format expects a char*, not a std::string.
From C11 Standard (C++ Standard refers to it about the C standard library):
Except in the case of a % specifier, the input item (or, in the case of a %n directive, the
count of input characters) is converted to a type appropriate to the conversion specifier. If
the input item is not a matching sequence, the execution of the directive fails: this
condition is a matching failure. Unless assignment suppression was indicated by a *, the
result of the conversion is placed in the object pointed to by the first argument following
the format argument that has not already received a conversion result. If this object
does not have an appropriate type, or if the result of the conversion cannot be represented
in the object, the behavior is undefined.
Anyway, here there's is no real reason to prefer the C way, use C++ facilities. And when you use the C library, use safe functions that only reads characters up to a given limit (just like fgets, or scanf with a width specifier), otherwise you could have overflow, that leads again to undefined behavior, and some errors if you're luck.
That's a really bad way to check for end-of-input. Either use an integer or use a string.
If you choose string, make provisions to convert from string to int.
My logic would be to first check if it can be converted to integer. if it can be, then continue with the logic. If it can't be(such as if it's a float or double or some other string) then ignore and move on. If it can be, then insert it into Halda's object.
Sidenote: Do not use scanf() and printf() when you're working with C++.
Assuming string refers to std::sring this program doesn't have defined behavior. You can't really use std::string with sscanf() You could set up a buffer inside the std::string and read into that but the string wouldn't change its size. You are probably better off using streams with std::string (well, in my opinion you are always better off using streams).

Why can't we use direct addressing in c or c++ code?

When I compile and execute this code consecutively for a couple of times, it reports the address of cc as 0x0012FF5C. But when I try to print out the string at that address using the second call to printf in foo, it prints garbage instead of printing out "Hello"?? Why so?? What's wrong if I directly paas the address as an argument when I know that the address lies within the address space of the application (atleast until I don't reboot my PC, or start some other application which requires a lot of space and which causes my application to be paged out)??
void foo(char *cc[])
{
printf("%x\n",cc);
printf("%s\n",(char *)(0x0012FF5C));
}
int main()
{
char *c[] = {"Hello","World"};
foo(c);
}
Because there is nothing in the C or C++ standard to guarantee that. Those addresses may be predictable depending your compiler/OS, but don't count on it.
#include <stdio.h>
int main(void) {
char s[] = "okay";
printf("%p", (void*)s);
return 0;
}
I get a different address every time (gcc on linux). Don't use "address literals" ;)
Process address space on modern OS's is randomized for security on each execution:
http://en.wikipedia.org/wiki/Address_space_layout_randomization
Because the first printf gives you the address of an array of char* when the second printf try to print this array of char* as a string.
OK, first thing is, never assume that multiple executions will return the same addresses
Now. lets say you are lucky and get same addresses. Notice that cc is an array of pointers. And you are sending the base address of this array. You need to send the value of the first element of the array
Try this, and if your lucky it works,
printf("%s\n",*((char**)(0x0012FF5C)));
If you run your program on a machine without virtual memory and memory protection, you very probably would succeed. These technologies/features are why it doesn't work.
Each process has its own virtual address space, whose addresses are translated to hardware addresses by the processor's memory-management unit.
I don't think your (0x0012FF5C) represents poiter. Try (char*)(0x0012FF5C).
Besides that, like other tell you, this has no practical value, as there is no guarantee string will be located at that address every run. This is not some embedded assembly where you say put this string at this location and later use the address directly.
The memory location of your process may have changed between the both executions. You can't do that, you are trying to read an adress who isn't alloced by your process. Moreover I have many warnings :
test.c: In function ‘foo’: test.c:6: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘char **’
test.c:7: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
test.c:9: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘char **’
when I know that the address
Well, you don't really know anything about the address when you're hard-coding it, you're guessing at it.
There are lots of reasons why the address of a thing could change between executions of the same program. There are even more reasons why the address could change if you're changing the code (as you appear to be doing here).
As someone previously said, your code location in memory, may vary between executions, and the same happens to variable's locations. Try running this code. It prints the actual address of the variable you point at in memory. You'll see that multiple executions yield to completely different addresses. Remember always that!
#include <stdio.h>
void foo(char *cc[])
{
printf("%x\n",cc);
printf("%s\n",(0x0012FF5C)); //this line will fail, there is no guarantee to that
cc++;
printf("%x\n",cc);
}
int main()
{
char *c[] = {"Hello","World"};
printf("c array location: %p",c); //print location
foo(c);
}
First thing, printf("%x\n",cc); doesn't print the address of cc. At best it prints the value of cc, but behavior is undefined because you've passed the wrong type argument for the format %x. It expects unsigned int, you've supplied a pointer.
The most likely behavior in practice is that it will print the least significant part of the value of cc (so on a 32 bit machine it will appear to work, whereas on a 64 bit machine you won't see the whole address). But it could go wrong in different ways.
Second thing, cc has type char**, and so the value of cc isn't a pointer to the first character of a string, it's a pointer to the first element of the array c from main. The %s format expects a pointer to the first character of a string. So even if the value of cc really is 0x0012FF5C, passing that value to printf with the %s format is wrong.
The "garbage" you're seeing is an attempt to print the pointer data from that array of pointers, as if it were character data belonging to a string. It isn't.
I simplified your example a little bit. If you know the address of the string, you can read from that address.
#include <stdio.h>
void foo(char cc[])
{
int i;
printf("%p\n",cc);
printf("Type the address you would like to read:");
scanf ("%x",&i);
printf("Character value at your address: %c\n",*((char *)(i)));
}
int main()
{
char c[] = "Hello";
foo(c);
}
This will allow you to read from an address you specify from the command line. It will first print out the base address of the character array, so you know where to look for the string.
Example:
$ ./a.out
0xbfcaaf3a
Type the address you would like to read:bfcaaf3d
Character value at your address: l
As expected this prints the fourth letter of Hello.

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 .....