First off, sorry about the title. I wasn’t really too sure how to phrase it.
In C, I have a 2D string array, declared and allocated as follows:
char ** args = malloc(50*(num_args+1));
for (int i = 0; i < num_args+1; i++){
args[i] = malloc(50);
I am using this in sort of a “rudimentary shell” type program, mimicking some of the features of bash, hence the num_args variable.
Compiled and run on multiple machines, the address at args[4] is always out of bounds. Here is the relevant gdb output:
(gdb) print args[0]
$2 = 0x609140 "gcc"
(gdb) print args[1]
$3 = 0x609180 ""
(gdb) print args[2]
$4 = 0x6091c0 ""
(gdb) print args[3]
$5 = 0x609200 ""
(gdb) print args[4]
$6 = 0x636367 <Address 0x636367 out of bounds>
(gdb) print args[5]
$7 = 0x609280 ""
As you can see, addresses before and after args[4] are valid. How can this one address be out of bounds?
The entire function where this code is used is here and below:
void parse(const char * command){
// first parse built-ins (ie, not a call to the OS)
if (strcmp(command, "history") == 0){
show_history();
return;
}
if (strcmp(command, "exit") == 0){
exit(0);
}
hist_add(command);
// copy 'command' into arg_string, while ignoring any possible comments
char * arg_str;
int num_args = 1;
arg_str = malloc(strlen(command));
for (int i = 0; i < strlen(command); i++){
if (command[i] == '#' || command[i] == '\n') break;
if (command[i] == ' ') num_args++;
arg_str[i] = command[i];
}
// split arg_str into a string array where each string is an argument
// to the command
char ** args = malloc(num_args+1);
for (int i = 0; i < num_args+1; i++){
args[i] = malloc(50);
}
int tokens = 0;
const char token = ' ';
char * next = strtok(arg_str, &token);
while (next != NULL){
strcpy(args[tokens++], next);
next = strtok(NULL, &token);
if (next == NULL)
args[tokens] = (char *)NULL;
}
exec_command(args);
}
The answer to your question lies in the fact that it’s not a 2D array. Instead,
argscontains a pointer to the first element of a 1D array of pointers, and each of those elements can itself point to an element of a 1D array ofchar(this is often called a “ragged array”, because those 1D arrays can be of different lengths).So the reason that the one address
args[4]can be out-of-bounds, even thoughargs[3]andargs[5]are not, is that the three pointersargs[3],args[4]andargs[5]are completely independent values.It is quite likely that
args[4]is being overwritten with an incorrect value because it actually lies outside your allocated area – you are not allocating sufficient space for the array pointed to byargs. Yourmalloc()call requestsnum_args + 1bytes, but you want sufficient space fornum_args + 1pointers, each of which takes up more than one byte. I suggest changing yourmalloc()call to:(Rather than using
calloc()you can of course multiplynum_args + 1bysizeof args[0]yourself and callmalloc(), but if you do this then you need to check to make sure that the multiplication doesn’t overflowSIZE_MAX.calloc()should handle that for you).