Reputation: 5283
I have a large legacy program in which programmatically argv parameter is changed programmatically in program init followed by logic that parses the parameters.
In Release mode the program terminates gracefully.
In Debug mode, the program does all the required computation and gives the correct output. But upon exit gives Heap corruption error:
Error Message:
Microsoft Visual C++ Runtime Library
Debug Error!
Program: ...sers\AMD\source\repos\ArgvOverflow\x64\Debug\ArgvOverflow.exe
HEAP CORRUPTION DETECTED: after CRT block (#62) at 0x00000259566FDC90. CRT detected that the application wrote to memory after end of heap buffer.
Memory allocated at minkernel\crts\ucrt\src\appcrt\startup\argv_parsing.cpp(285).
(Press Retry to debug the application)
Abort Retry Ignore
Code:
#include <stdio.h>
int main(int argc, char **argv)
{
argc = 12;
argv[1] = "str1";
argv[2] = "str2";
argv[3] = "str3";
argv[4] = "str4";
argv[5] = "str5";
argv[6] = "str6";
argv[7] = "str7";
argv[8] = "str8";
argv[9] = "str9";
argv[10] = "str10";
argv[11] = "str11";
printf("Hello world\n");
return 0;
}
I have read several posts about modifying argv, where they claim that such modifications are legal according to C standards. I also tried the suggestion to have the line
argv[argc] = NULL;
This is not solving the problem.
Upvotes: 0
Views: 454
Reputation: 133839
It is legal to modify the characters of the argument strings. C11 5.1.2.2.1p2
The parameters
argc
andargv
and the strings pointed to by theargv
array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.
It is still not allowed to access the array out of bounds, argv
will have only argc + 1
elements (with the value of argc
as in the beginning of the main), not as many as you try to stuff in there.
Upvotes: 2
Reputation: 73
It is totally plausible to have this kind of setup when doing debug. One work around which worked for me was to set up a dummy argument list when calling the program, then repopulate argv with the desired data once you get into main.
myprog.exe dummy dummy dummy dummy dummy dummy dummy dummydummy dummydummy dummy
and inside your code
int main (int argc , char* argv[]){
argc = 12;
argv[1] = "str1";
argv[2] = "str2";
argv[3] = "str3";
argv[4] = "str4";
argv[5] = "str5";
argv[6] = "str6";
argv[7] = "str7";
argv[8] = "str8";
argv[9] = "str9";
argv[10] = "str10";
argv[11] = "str11";
printf("Hello world\n");
return 0;
}
Upvotes: 0
Reputation: 213136
You are allowed to modify argc
and argv
, but that doesn't mean that C suddenly handles (re)allocation of these variables for you. argv
will be an array of type char* argv[argc];
. It will contain as many pointers as argc
says that it contains, no more, no less. Similarly, the length of each string pointed at by argv[i]
is as long as what the caller passed.
Example:
myprog.exe foo
argc == 2
and argv
will have the length 2. This cannot be changed by your program. argv[0]
will point at a modifiable string "myprog.exe"
, size 10+1 = 11 bytes. You can change the contents but not store anything longer than 11 bytes there.argv[1]
will point at the string "foo"
, size 3+1 = 4 bytes. You can change the contents but not store anything longer than 4 bytes there.(Curiosity: it is perfectly fine and arguably the most correct way to define argv
as a VLA like this:
int main (int argc, char* argv[argc])
, because argv
decays into a char**
anyway.)
That all being said, modifying argc
and argv
, although allowed by the C standard, is horribly bad practice. Don't do this. Instead you should use a local variable and let it refer to argv
where needed. Example:
int main (int argc, char* argv[])
{
const char* argstr [12] =
{
"str0",
"str1",
"str2",
"str3",
"str4",
"str5",
"str6",
"str7",
"str8",
"str9",
"str10",
"str11",
};
for(int i=0; i<argc; i++)
{
argstr[i] = argv[i];
}
/* always use argstr here, never argv */
return 0;
}
Upvotes: 2
Reputation: 50767
You cannot mess around with argv
like this. argv
is not your's, it has been allocated and filled during program start according to the command line.
If you invoke your program like this:
program a b c
then argc
is 4, argv[0]
points to "program"
, argv[1]
points to "a"
etc.
argv[argc]
is NULL
But accessing argv[5]
and beyond is undefined behaviour because you access an array out of bounds.
This SO article may help too: How dangerous is it to access an array out of bounds?.
To solve your problem: don't access arrays out of bounds.
Upvotes: 1
Reputation: 5283
To solve this, I created a separate char** variable, and used that variable in the code to solve the problem.
Here is what the new code looked like:
#include <stdio.h>
int main(int argc, char **argv)
{
int nargc = 12;
char **nargv;
nargv = malloc(sizeof(char*)*nargc);
nargv[0] = malloc(1 + strlen(argv[0]));
strcpy(nargv[0], argv[0]);
nargv[1] = malloc(1 + strlen("srt1"));
strcpy(nargv[1], "srt1");
nargv[2] = malloc(1 + strlen("srt2"));
strcpy(nargv[2], "srt2");
nargv[3] = malloc(1 + strlen("srt3"));
strcpy(nargv[3], "srt3");
nargv[4] = malloc(1 + strlen("srt4"));
strcpy(nargv[4], "srt4");
nargv[5] = malloc(1 + strlen("srt5"));
strcpy(nargv[5], "srt5");
nargv[6] = malloc(1 + strlen("srt6"));
strcpy(nargv[6], "srt6");
nargv[7] = malloc(1 + strlen("srt7"));
strcpy(nargv[7], "srt7");
nargv[8] = malloc(1 + strlen("srt8"));
strcpy(nargv[8], "srt8");
nargv[9] = malloc(1 + strlen("srt9"));
strcpy(nargv[9], "srt9");
nargv[10] = malloc(1 + strlen("srt10"));
strcpy(nargv[10], "srt10");
nargv[11] = malloc(1 + strlen("srt11"));
strcpy(nargv[11], "srt11");
/* Useful code */
free(nargv[11]);
free(nargv[10]);
free(nargv[9]);
free(nargv[8]);
free(nargv[7]);
free(nargv[6]);
free(nargv[5]);
free(nargv[4]);
free(nargv[3]);
free(nargv[2]);
free(nargv[1]);
free(nargv[0]);
free(nargv);
printf("Hello world\n");
return 0;
}
Upvotes: 0