I need help with debugging this piece of code. I know the problem is in malloc and free but can't find exactly where, why and how to fix it. Please don't answer: "Use gdb" and that's it. I would use gdb to debug it, but I still don't know much about it and am still learning it, and would like to have, in the meanwhile, another solution.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#define MAX_COMMAND_LENGTH 256
#define MAX_ARGS_NUMBER 128
#define MAX_HISTORY_NUMBER 100
#define PROMPT ">>> "
int num_elems;
typedef enum {false, true} bool;
typedef struct {
char **arg;
char *infile;
char *outfile;
int background;
} Command_Info;
int parse_cmd(char *cmd_line, Command_Info *cmd_info)
{
char *arg;
char *args[MAX_ARGS_NUMBER];
int i = 0;
arg = strtok(cmd_line, " ");
while (arg != NULL) {
args[i] = arg;
arg = strtok(NULL, " ");
i++;
}
num_elems = i;precisa em free_mem
if (num_elems == 0)
return 0;
cmd_info->arg = (char **) ( malloc(num_elems * sizeof(char *)) );
cmd_info->infile = NULL;
cmd_info->outfile = NULL;
cmd_info->background = 0;
bool b_infile = false;
bool b_outfile = false;
int iarg = 0;
for (i = 0; i < num_elems; i++)
{
if ( !strcmp(args[i], "<") )
{
if ( b_infile || i == num_elems-1 || !strcmp(args[i+1], "<") || !strcmp(args[i+1], ">") || !strcmp(args[i+1], "&") )
return -1;
i++;
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->infile, args[i]);
b_infile = true;
}
else if (!strcmp(args[i], ">"))
{
if ( b_outfile || i == num_elems-1 || !strcmp(args[i+1], ">") || !strcmp(args[i+1], "<") || !strcmp(args[i+1], "&") )
return -1;
i++;
cmd_info->outfile = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->outfile, args[i]);
b_outfile = true;
}
else if (!strcmp(args[i], "&"))
{
if ( i == 0 || i != num_elems-1 || cmd_info->background )
return -1;
cmd_info->background = true;
}
else
{
cmd_info->arg[iarg] = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->arg[iarg], args[i]);
iarg++;
}
}
cmd_info->arg[iarg] = NULL;
return 0;
}
void print_cmd(Command_Info *cmd_info)
{
int i;
for (i = 0; cmd_info->arg[i] != NULL; i++)
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("infile=\"%s\"\n", cmd_info->infile);
printf("outfile=\"%s\"\n", cmd_info->outfile);
printf("background=\"%d\"\n", cmd_info->background);
}
void get_cmd(char* str)
{
fgets(str, MAX_COMMAND_LENGTH, stdin);
str[strlen(str)-1] = '\0';
}
pid_t exec_simple(Command_Info *cmd_info)
{
pid_t pid = fork();
if (pid < 0)
{
perror("Fork Error");
return -1;
}
if (pid == 0)
{
if ( (execvp(cmd_info->arg[0], cmd_info->arg)) == -1)
{
perror(cmd_info->arg[0]);
exit(1);
}
}
return pid;
}
void type_prompt(void)
{
printf("%s", PROMPT);
}
void syntax_error(void)
{
printf("msh syntax error\n");
}
void free_mem(Command_Info *cmd_info)
{
int i;
for (i = 0; cmd_info->arg[i] != NULL; i++)
free(cmd_info->arg[i]);
free(cmd_info->arg);
free(cmd_info->infile);
free(cmd_info->outfile);
}
int main(int argc, char* argv[])
{
char cmd_line[MAX_COMMAND_LENGTH];
Command_Info cmd_info;
//char* history[MAX_HISTORY_NUMBER];
while (true)
{
type_prompt();
get_cmd(cmd_line);
if ( parse_cmd(cmd_line, &cmd_info) == -1)
{
syntax_error();
continue;
}
if (!strcmp(cmd_line, ""))
continue;
if (!strcmp(cmd_info.arg[0], "exit"))
exit(0);
pid_t pid = exec_simple(&cmd_info);
waitpid(pid, NULL, 0);
free_mem(&cmd_info);
}
return 0;
}
Since strings in C are null-terminated, their actuall size in memory is length+1, so instead of
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
You should have
cmd_info->infile = malloc((strlen(args[i])+1) * sizeof(char));
EDIT: As Aeth said, you need to change every single occurence of malloc to contain space for that additional null character:
cmd_info->arg = (char **) ( malloc(num_elems * sizeof(char *)) ); //this one can stay, since it determines number of strings, not string length
cmd_info->outfile = malloc((strlen(args[i])+1) * sizeof(char));
cmd_info->arg[iarg] = malloc((strlen(args[i])+1) * sizeof(char));
You need to allocate an extra char for each of your strings to handle the terminating null.
cmd_info->arg[iarg] = malloc((strlen(args[i])+1) * sizeof(char));
You need to allocate an additional char* in the cmd_info->arg array. This extra element will store the NULL that signifies the end of the array of arguments.
cmd_info->arg = (char **) ( malloc((num_elems+1) * sizeof(char *)) );
I have confirmed on my system that the program successfully frees all its memory without error after both of the changes listed were made.
When you are dynamically allocating memory for cmd_info->infile as:
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
you are not allocating space for the terminating null char.
Same is the case with allocation for cmd_info->outfile
When you allocate space for n char and copy a string of length n into it, I think that overwrites the metadata that malloc maintains at the end of the array and this bug shows up when you call free to deallocate the memory as free does not find that meta data.
EDIT:
Change:
num_elems = i;
to
num_elems = i+1;
Since you are marking the end of the arguments with NULL
cmd_info->arg[iarg] = NULL;
you need to allocate the space for this.
In general, this error is usually the result of something writing data outside a malloc()'d block (off the end or before the beginning). This can corrupt the memory allocator's internal bookkeeping structures.
Others have already pointed out the particular problem in your code. In cases where it's more deeply hidden, I have found Valgrind to be useful for debugging. At the expense of noticable code slowdown, it is able to detect illegal memory accesses (in the form of "invalid reads" and "invalid writes") at a very fine-grained level. Memory debuggers such as dmalloc can also be helpful, and don't impose nearly as much overhead, but in my experience aren't quite as good as Valgrind for finding everything.
Valgrind, in its 'memcheck' mode, will output memory access errors with a stack trace of where in the program they occurred. Usually, when I have an "invalid pointer" error in free(), it will be preceeded at some point by an invalid write which memcheck will find.
Related
I'm trying to split a sentence (char *) to an array of words (char **). The problem is that my function that does just that sometimes doesn't return a valid char **.
char **get_words(char *buffer, char delimiter)
{
char **words = malloc(sizeof(char *) * 4096);
for (int i = 0; i < 4096; i++)
words[i] = malloc(sizeof(char) * 4096);
int word_count = 0;
int l = 0;
for (int i = 0; buffer[i] != '\0' && buffer[i] != '\n'; i++, l++) {
if (buffer[i] == delimiter) {
words[word_count][l] = '\0';
word_count++;
l = -1;
}
else
words[word_count][l] = buffer[i];
}
words[word_count][l] = '\0';
return (words);
}
I first use it like this:
char *buffer = malloc(sizeof(char) * 50);
buffer = "/login test\n";
char **words = get_words(buffer, ' ');
printf("Words[0] = %s", words[0]);
And it works fine.
However when I do it the same way with this:
char **reply = get_words("502 Command doesn't exist.\n", ' ')
I can't even print reply[0][0] (see below) without having a segmentation fault.
Moreover, I tried to debug this using valgrind but when I use it the program doesn't crash and everything works so I can't find what's wrong.
printf("Reply[0][0] = %d\n", reply[0][0]);
printf("Reply[0][0] = %c\n", reply[0][0]);
EDIT:
Here is a reproductible example.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
char **get_words(char *buffer, char delimiter)
{
printf("buffer = %s\n", buffer);
char **words = malloc(sizeof(char *) * 100);
if (words == NULL) {
printf("Malloc Error\n");
exit(84);
}
for (int i = 0; i < 100; i++) {
words[i] = malloc(sizeof(char) * 100);
if (words[i] == NULL) {
printf("Malloc Error\n");
exit(84);
}
}
int word_count = 0;
int l = 0;
for (int i = 0; buffer[i] != '\0' && buffer[i] != '\n'; i++, l++) {
if (buffer[i] == delimiter) {
words[word_count][l] = '\0';
word_count++;
l = -1;
}
else
words[word_count][l] = buffer[i];
}
words[word_count][l] = '\0';
return (words);
}
int main()
{
char *buffer = malloc(sizeof(char) * 100);
buffer = "hello world !\n";
char **words = get_words(buffer, ' ');
printf("words[0]= %s\n", words[0]);
free (buffer);
char **reply = get_words("Second call\n", ' ');
printf("reply[0] = %s\n", reply[0]);
}
If you need help in learning programming, you can try a static analyzer. This is a program that performs code reviews and finds suspicious code fragments. Static analyzers can't replace code reviews performed by a teammate. However, analyzers complement code reviews and help find many errors at earliest stages.
Let's run the online version of the PVS-Studio analyzer for the code sample attached to the question. The first interesting and important warning is the following warning: V1031 The malloc function is not declared. Passing data to or from this function can be affected.
Without declaring the malloc function, the program runs in a strange way. According to the C language, if a function is not declared, it returns int. But actually, it's a pointer. You can find out why this is dangerous here. Let's fix this problem by adding #include <stdlib.h>.
Now the analyzer issues another warning — we get a more serious issue:
43:1: note: V773 The 'buffer' pointer was assigned values twice without releasing the memory. A memory leak is possible.
The issue is in the following code fragment:
char *buffer = malloc(sizeof(char) * 100);
buffer = "hello world !\n";
....
free (buffer);
The pointer value is overwritten. To copy a string to the buffer, a programmer should use special functions, for example strcpy. Let's fix this.
Here's the fixed code.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
char **get_words(char *buffer, char delimiter)
{
printf("buffer = %s\n", buffer);
char **words = malloc(sizeof(char *) * 100);
if (words == NULL) {
printf("Malloc Error\n");
exit(84);
}
for (int i = 0; i < 100; i++) {
words[i] = malloc(sizeof(char) * 100);
if (words[i] == NULL) {
printf("Malloc Error\n");
exit(84);
}
}
int word_count = 0;
int l = 0;
for (int i = 0; buffer[i] != '\0' && buffer[i] != '\n'; i++, l++) {
if (buffer[i] == delimiter) {
words[word_count][l] = '\0';
word_count++;
l = -1;
}
else
words[word_count][l] = buffer[i];
}
words[word_count][l] = '\0';
return (words);
}
int main()
{
char *buffer = malloc(sizeof(char) * 100);
if (buffer == NULL)
exit(84);
strcpy(buffer, "hello world !\n");
char **words = get_words(buffer, ' ');
printf("words[0]= %s\n", words[0]);
free (buffer);
char **reply = get_words("Second call\n", ' ');
printf("reply[0] = %s\n", reply[0]);
}
I can't say that this code is perfect and secure, but it runs. So, using static analyzers to find errors, you can improve your learning process.
I am trying to allocate memory for an array of strings using malloc. The size of each string is not known before the input from the user, so this is how I tried to allocate memory for each element in the array.
I have some errors with the code, but can't figure them out or can't understand them. I am getting an error regarding the allocation. Can anyone tell me what's wrong about this?
bool read_strings(char * strings[], int n)
{
int i = 0;
while (i<n)
{
char string[MAX_LENGTH];
if (scanf("%s", string)!=1)
return false;
char* memory= (char*)malloc(sizeof(char)*strlen(string));
if (memory == NULL)
return false;
memory = string;
strings[i] = memory;
i++;
}
return true;
}
Thanks a lot!
At least you have to replace
char* memory= (char*)malloc(sizeof(char)*strlen(string));
if (memory == NULL)
return false;
memory = string;
strings[i] = memory;
by
strings[i] = strdup(string)
Note that using scanf("%s", string) the separator between the read string is the space
The problem is right here:
char* memory = (char*)malloc(sizeof(char)*strlen(string));
memory = string; <<<
strings[i] = memory;
You will lose memory if you assign strings to pointers like this.
Either:
a) Copy the string into the newly allocated memory using strcpy() or strncpy(), also make sure you have enough space for the NULL character \0
strings[i] = (char*)malloc(sizeof(char) * (strlen(string) + 1));
strcpy(strings[i], string);
b) Use strdup(), which is like a mix between strcpy() and malloc(), it creates just enough space for your string and copies it into a new memory location
strings[i] = strdup(string);
You have many mistakes
(char*)malloc(sizeof(char)*(strlen(string) **+ 1**)) . You have to reserve memory to '\0'
Very wrong
memory = string;
To copy strings you have to usr strcpy (the correct function nowadays is strncpy is more safe)
To have a truly unlimited buffer in C, (or limited by the amount of memory and a size_t,) you could build up the memory allocation incrementally.
#include <stdlib.h> /* realloc free */
#include <stdio.h> /* stdin fgets printf */
#include <string.h> /* strcpy */
#include <assert.h> /* assert */
#include <stdint.h> /* C99 SIZE_MAX */
#include <stdbool.h> /* C99 bool */
/* Returns an entire line or a null pointer, in which case eof or errno may be
set. If not-null, it must be freed. */
static char *line(void) {
char temp[1024] = "", *str = 0, *str_new;
size_t temp_len, str_len = 0;
while(fgets(temp, sizeof temp, stdin)) {
/* Count the chars in temp. */
temp_len = strlen(temp);
assert(temp_len > 0 && temp_len < sizeof temp);
/* Allocate bigger buffer. */
if(!(str_new = realloc(str, str_len + temp_len + 1)))
{ free(str); return 0; }
str = str_new;
/* Copy the chars into str. */
strcpy(str + str_len, temp);
assert(str_len < SIZE_MAX - temp_len); /* SIZE_MAX >= 65535 */
str_len += temp_len;
/* If on end of line. */
if(temp_len < sizeof temp - 1 || str[str_len - 1] == '\n') break;
}
return str;
}
static bool read_strings(char * strings[], int n) {
char *a;
int i = 0;
while(i < n) {
if(!(a = line())) return false;
strings[i++] = a;
}
return true;
}
int main(void) {
char *strings[4] = { 0 }; /* C99 */
size_t i;
bool success = false;
do {
if(!read_strings(strings, sizeof strings / sizeof *strings)) break;
for(i = 0; i < sizeof strings / sizeof *strings; i++)
printf("%lu: <%s>\n", (unsigned long)i, strings[i]);
success = true;
} while(0); {
for(i = 0; i < sizeof strings / sizeof *strings; i++)
free(strings[i]);
}
return success ? EXIT_SUCCESS : (perror("stdin"), EXIT_FAILURE);
}
I think that's right-ish. However, this should bring pause; what if they never hit enter? If one has a MAX_LENGTH, then consider allocating statically, depending on your situation.
Edit: It also has a worst-case running time that may not be desirable; if entering really arbitrarily large lines, use a geometric progression to allocate space.
I think this is what you wanted to do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int read_strings(char * strings[], int n)
{
int i = 0;
char buffer[256] ={0}; /*a temp buffer of fixed max size for input */
if(NULL == strings)
{
return 0 ;
}
for (i= 0; i<n; ++i)
{
if (fgets(buffer, 256,stdin)== NULL) /*safer then scanf - read input into the buffer*/
return 0;
strings[i]= malloc(sizeof(char)*(strlen(buffer)+1)); /* the char poiner in he i place will now point to the newly allocated memory*/
strcpy(strings[i], buffer); /*copy the new string into the allocated memory now string[i] is pointing to a string*/
}
return 1;
}
static void printStringsArray(const char* strArr[], size_t size)
{
int i = 0;
if(NULL == strArr)
{
return;
}
for(i = 0; i< size; ++i)
{
printf("%s", strArr[i]);
}
}
int main(void)
{
char * arr[3]; /*array of (char*) each will point to a string after sending it to the function */
read_strings(arr,3);
printStringsArray(arr,3);
return 0;
}
I want to dynamically allocate only a portion of a character array.
So part of an array of size 100 is concrete. Say 10 is permanent memory, the other 90 is dynamic memory.
I made some attempt to read character by character until I decided to give up and take a shortcut idea I thought would work. However I end up getting an error that is
incorrect checksum for freed object - object was probably modified
after being freed
I use this method in a while loop in main and I pretty much free everything after the while loop processes. Because, I have the declaration outside of the while loop. I wanted to read an object in a while loop session since these objects end up being added into a list of objects. However the scope of the while loop causes segmentation problems, it cannot remember anything about the object. (I digress).
Here is my attempt.
Object* read(char* str)
{
Object* object = (Object*)malloc(sizeof(*object));
object->identity[0] = 0;
int capacity = (100 + 1) - (10);
object->name = (char*)malloc(capacity * sizeof(*object->name));
object->value = 0.0;
int length = strlen(str);
if (length > capacity)
object->name = (char*)realloc(object->name, (capacity * 2) * sizeof(*object->name));
int arguments = sscanf(str, "%" STRING_SPACE "s %lf %[^\n]s",
object->identity,
&object->value,
object->name);
if (arguments == MATCHER) {
return object;
} else {
return NULL;
}
return object;
}
In this case, an object has a variable sized name but a fixed amount of space allocated for its identity.
I tried something else with sscanf but realized it will never work because I read the string too late to assign memory to name. See;
/*
int len = 0;
for (char* itemObserve = item->name; *itemObserve; itemObserve++) {
if (len == sizeof(item->name)) {
capacity *= MULTIPLIER;
item->name = (char*)realloc(item->name, capacity * sizeof(*item->name));
}
len++;
}
*/
Here is the code in main, everything undefined is probably irrelevant to the bug:
int main()
{
FILE* stream;
Object* object;
ObjectList* list = initList();
while (true) {
char* line;
char cmd[15] = {0};
char* arg;
char* rest;
printf("> ");
line = getline(stdin);
arg = (char*)malloc(35 * sizeof(*arg));
rest = (char*)malloc(35 * sizeof(*rest));
int arguments = sscanf(line, "%s %s %[^\n]", cmd, arg, rest);
free(line);
line = NULL;
printf("\n");
if (strcmp(cmd, "add") == 0) {
arg = (char*)realloc(arg, (35 * 2) * sizeof(*arg));
sprintf(arg, "%s %s", arg, rest);
if ((object = read(arg)) == NULL) {
continue;
}
objectListAdd(list, object);
} else {
free(rest);
free(arg);
exit(EXIT_SUCCESS);
}
free(rest);
free(arg);
}
freeObject(object);
freeObjectList(list);
return EXIT_SUCCESS;
}
Separate getline function in main file
char* getline(FILE* stream)
{
int capacity = LINE_MAX + 1;
char* buffer = (char*)malloc(capacity * sizeof(*buffer));
int len = 0;
int ch;
while ((ch = fgetc(stream)) != '\n' && (ch != EOF)) {
if (len == capacity) {
capacity *= MULTIPLIER;
buffer = (char*)realloc(buffer, capacity * sizeof(*buffer));
}
buffer[len++] = ch;
}
if (ch == EOF) {
return NULL;
}
buffer[len] = '\0';
if (buffer == NULL)
return NULL;
return buffer;
}
There are other conditionals which work as a kind of command switch but they are irrelevant to the errors my program is exhibiting. This much I have narrowed the problem down to.
I'm using openssl's libcrypto library to try and create a sha256 hash of a arbitrary input buffer, however when I null terminate the hash buffer to make it a C string it crashes but only when using dynamic memory returned from malloc but not when I use statically allocated memory like in this example: generate sha256 with openssl and C++. I'm allocating 65 bytes just like the example, as shown by the length value being printed. So why does null terminating the buffer crash when using dynamic memory as opposed to static memory?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/sha.h>
static int sha256(char *in, char **out)
{
int rtrn, i;
SHA256_CTX sha256;
unsigned char hash[SHA256_DIGEST_LENGTH];
rtrn = SHA256_Init(&sha256);
if(rtrn < 0)
{
printf("Sha Init Error\n");
return -1;
}
rtrn = SHA256_Update(&sha256, in, strlen(in));
if(rtrn < 0)
{
printf("Sha Update Error\n");
return -1;
}
rtrn = SHA256_Final(hash, &sha256);
if(rtrn < 0)
{
printf("Sha Final Error\n");
return -1;
}
*out = malloc((SHA256_DIGEST_LENGTH * 2) + 1);
if(*out == NULL)
{
printf("Can't allocate output buf\n");
return -1;
}
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(*out + (i * 2), "%02x", hash[i]);
}
printf("Length: %d\n", (SHA256_DIGEST_LENGTH * 2) + 1);
*out[64] = '\0';
return 0;
}
int main(void)
{
int rtrn;
char *input = "3r98h932hr934hor";
char *output;
rtrn = sha256(input, &output);
if(rtrn < 0)
{
printf("Sha256\n");
return -1;
}
printf("%s\n", output);
return 0;
}
I suspect this is because you did not mean:
*out[64] = '\0';
but rather:
(*out)[64] = '\0';
Array subscripting ([]) has higher precedence than the indirection (*) operator in C, so what you wrote is the equivalent to:
*(out[64]) = '\0';
which will be corrupting a random area on the stack (where the char ** lives).
Trying to build a shell implementation in linux for an assignment and am having trouble. I get the following error after running 3/4 times into the loop
* Error in `./1': realloc(): invalid next size: 0x000000000132c150 *
Aborted (core dumped)
I have left parts of the code out as it's long and I know there's no issues with them. If there's other parts you need I can add them too.
Thanks in advance :)
Note: argc and argv are global variables
int argc = 0;
char **argv = NULL;
int main(void)
{
while(1)
{
catch_signal();
printDate();
readCommand();
if(strcmp(argv[0], "cd") == 0)
{
changeDirectory();
}
else
{
executeCommand();
}
//free(argv);
}
}
void readCommand()
{
char *buffer = NULL;
char *token = " ";
char *p;
size_t len = 0;
int i = 0;
getline(&buffer, &len, stdin);
p = strtok(buffer, "\n");
p = strtok(buffer, token);
while(p)
{
argv = realloc(argv, sizeof(char *) * ++i);
argv[i - 1] = p;
p = strtok(NULL, token);
}
argc = i;
argv[argc] = NULL;
free(p);
}
void executeCommand()
{
int status;
pid_t pid;
if((pid = fork()) < 0)
{
printf("Error: forking child process failed.");
}
else if(pid == 0)
{
if(execvp(argv[0], argv) < 0)
{
printf("error");
exit(1);
}
}
else
{
while(wait(&status) != pid);
}
}
You are reallocating the argv array 1 pointer too short. There is not enough space for the final NULL pointer. Consider replacing the while(p) loop with this code:
if (!p) {
/* deal with empty command line */
}
while (p) {
argv = realloc(argv, sizeof(char *) * (i + 2));
argv[i++] = p;
p = strtok(NULL, token);
}
p should start as NULL if the user entered an actual command. But what if buffer contains an empty string or just a blank one? strtok will return NULL directly in these cases. You should ignore such command lines.