C segmentation fault using malloc'ed strings - c

Essentially, I have a structure for a linked list in my code,
struct node{
char* val;
struct node *next;
};
Then later I try to do some things to it...
...
addr = malloc(sizeof(struct node));
addr->val = malloc(72);
addr->val = "";
snprintf(addr->val, 1000, "%s", ident);
...
...
This gives me a segmentation fault at the snprintf. Valgrind says the following
Process terminating with default action of signal 11 (SIGSEGV)
==10724== Bad permissions for mapped region at address 0x40566B
==10724== at 0x4EB0A32: vsnprintf (vsnprintf.c:112)
==10724== by 0x4E8F931: snprintf (snprintf.c:33)
==10724== by 0x4016CC: id (Analyzer.c:267)
...
I am fairly new to C as opposed to C++, but I thought that calling malloc on the char* should make it valid, especially since I can initialize and print it, so I don't understand why the snprintf wouldn't work.
I also had my program print out the addresses of both variables, and the address valgrind complains about is indeed from addr->val.
I also tried using strcpy instead of snprintf but had the same result.
Thanks.

addr->val = malloc(72);
This line dynamically allocates 72 bytes and assigns the address of that memory region to addr->val.
addr->val = "";
This then sets addr->val to point to the address of a string constant, discarding the address of the malloced area of memory that it previously contained, causing a memory leak.
When you then try to use snprintf, you're attempting to write to a string literal. Since these are typically stored in a read-only section of memory, attempting to do so results in a core dump.
There's no need for addr->val = "";. It throws away allocated memory; it doesn't set that allocated memory to an empty string, which is probably what you thought it would do. Even if it did, it's useless anyway because snprintf will overwrite anything that might be there.

The code
addr->val = malloc(72);
addr->val = ""; <====
overwrites val pointer with "", address of a static area, made of 1 character (of value 0). Remove this line.
And
snprintf(addr->val, 1000, "%s", ident);
Accept a length of 1000 while you only allocated 72 characters.
snprintf(addr->val, 72, "%s", ident);
is better.

addr->val = malloc(72);
addr->val = "";
The second line re-assignes addr->val to an address in read-only section (where string literals lie), and discards the address of allocation from malloc, which can lead to a potential memory leak.
I know you want to clear it. To assign strings, you should use strcpy()
strcpy(addr->val, "");
But since you want to empty it, it's easiest to set the first character to zero:
addr->val[0] = '\0';
Besides, you're trying to do a potentially harmful job:
snprintf(addr->val, 1000, "%s", ident);
You intended to allocate 72 bytes, but why us the second parameter going up to 1k? Change it to a safer number:
snprintf(addr->val, 72, "%s", ident);
Everything should be fine by then.

Related

Is putting string direct to the pointer (without declare static or dynamic space in RAM) good practice?

char* load_text(char* text, int how_many)
{
char* result = 0;
char* here = 0;
result = fgets(text, how_many, stdin);
if (result)
{
here = strchr(text, '\n');
if (here)
*here = '\0';
else
while (getchar() != '\n')
continue;
}
return result;
}
This function is simple, send string from the keyboard and put to the pointer variable and then search new line '\n' in this string and remove in this place with null character '\0'
Then remove new line from function when you pressed ENTER
My question is:
Is it good to put string direct to the pointer from function fgets() to pointer *result?
Or is it a bad practice?
Because i searched a lot information about this problem
And many peaople told me:
Never put string direct to the pointer, which points to the random address in RAM without making static space or dynamic allocation in RAM !!!
I learn C programming language from Stephen Prat book "C Primer Plus, 6th Edition" and i founded this example in this book
From my point of view, I think putting strings directly to the random address RAM without static declare space for string or dynamic allocation is very bad idea
Correct me if i am wrong?
I think this example make really mess in RAM
I want know if my point of view is correct.
There is a notion that memory is associated with pointers. For example, in int *p = malloc(3 * sizeof *p);, some people say that memory is “allocated to p”. This is not correct. Memory is allocated, the address of that memory is returned, and that address is assigned to p, meaning that the value of p is set to that address. No ownership of the memory is associated with p, nor is any enduring relationships between p and the memory formed other than that p currently holds the address of the memory.
You could follow this with int *q = p; and int *r = p;, and then p, q, and r would have the same address, and none of them would have any privileged status over the others. They are simply variables that hold values.
In result = fgets(text, how_many, stdin);, the value returned by fgets is assigned to result. There is no need to allocate any memory for result to point to before this. In fact, doing so would be counterproductive, because the address assigned to result would be overwritten by result = fgets(text, how_many, stdin);.
Is it good to put string direct to the pointer from function fgets() to pointer *result?
It is fine.
And many peaople told me: Never put string direct to the pointer, which points to the random address in RAM without making static space or dynamic allocation in RAM !!!
You should never use a pointer before it has been assigned an appropriate address. For example, you should never use *result, as in *result = x; or x = *result;, before result has been set to pointer to an appropriate address.
result = fgets(text, how_many, stdin); sets result to an appropriate address, so it is fine.
there is no simple answer - it all depends, but your function depends on the caller supplying a valid pointer and a valid length. But c coding is like that , you are at the mercy of your caller. Look at the fgets function, it has to depend on its caller providing valid arguments too
This function has no responsibility to allocate memory, that has been pushed to the caller. If the caller does not allocate correctly when calling, this function will fail, but through no fault of its own.
Since you do check the result of fgets if there is an error you do return an error, so it all works out.
When you say this:
Never put string direct to the pointer, which points to the random address in RAM without making static space or dynamic allocation in RAM !!!
I think what you mean is "never write to an uninitialized pointer", which is true. It does not mean "never put a string direct to the pointer" as that doesn't mean anything.
For example, if you had this:
char* x;
load_text(x, 100);
That is undefined behaviour, no allocation was made, but it's not the fault of the load_text function itself. This is just buggy code that needs to be fixed.

Understanding results with valgrind

For the following code I have the following defintion:
typedef struct string {char* data; int length;} string;
If I run the following code with valgrind, I got conditional jump or move depends on unitinialized value and seg. fault:
string* s = (string*) malloc(sizeof(string));
strcpy("Hello", s->data);
free(s);
First of all, I can't understand why I got above errors.
I thought if I add to that code free(s->data) it will freed memory but program will run ok.
How I think:
I know sizeof(string) equal to 4(pointer to char) + 4(int) = 8.
then we allocate 8 bits for s.
strcpy will copy the string into data but I got a problem here. why?
There are multiple problems:
string* s = (string*) malloc(sizeof(string));
which should better be
string* s = malloc(sizeof(*s));
allocates memory for s->data, but does not make s->data point to any valid memory location. If you want to make use of the memory location, you need to make sure that it points to a valid memory location. For example: you'd need to malloc() for s->data seperately.
That said, the syntax for strcpy() says, it's strcpy(dest, source), so in your case
strcpy("Hello", s->data);
attempts to
read from an unitilized memory location
write into a string literal
either of which invokes undefined behaviour.
You should write
strcpy(s->data, "Hello");
after ensuring s->data is a valid destination.

C pointer being freed was not allocated (even though I malloc'd it, and that's all this program does)

I'm writing a C program on my mac and I made something basic, but I got this error. I have a struct with a char pointer in it to hold a short description. In my main class I made a pointer to this struct and then another for the char pointer within the struct. When I free the char pointer in the struct, my terminal throws out some error message about a pointer being freed that was not allocated. The error is not present if I comment that line out, so I'm pretty sure it's that line. I'll include the code causing this odd issue.
Chair *chair = (Chair*)malloc(sizeof(Chair));
chair->color = (char*)malloc(sizeof(char)*4);
chair->color = "blue";
printf("This chair is %s", chair->color);
free(chair->color);
free(chair);
return 0;
When I searched for this, it always came to user error of not mallocing or "double free errors". I'm pretty sure I used malloc and that there isn't a glaringly obvious problem in those 6 lines.
chair->color = (char*)malloc(sizeof(char)*4);
chair->color = "blue";
To store a 4-char string, you need 5 bytes. 1 extra for the ending \0.
Also, to assign a string, you should use strcpy().
strcpy(chair->color, "blue");
You have couple of errors.
In the line:
chair->color = "blue";
you are assigning chair->color to point to some memory that you didn't get from malloc. Then you are trying to free it later.
You'll need strcpy to accomplish what you are trying.
strcpy(chair->color, "blue");
In order to copy "blue", you have to have a string that has at least 5 characters - 4 for the letters and 1 for the terminating null character. You need to use:
chair->color = malloc(N); // Where N > 4.
Don't cast the return value of malloc. It is known to be problematic if you do. See Do I cast the result of malloc?.
You also don't need to use sizeof(char). It is guaranteed to be 1.

memory leak during char* manipulation

I do something like that in the loop :
char* test = "\0";
test = strcat(test, somestr);
...
char* tmp = strstr(test, 0, len);
free(test);
test = tmp;
And get memory leak. What I do wrong?
You don't actually have a memory leak (in the code you posted anyway), but you do several things wrong.
char* test = "\0";
This declares pointer named test and initializes it to point to some literal array of two bytes { 0, 0 }
test = strcat(test, somestr);
This tries to append something to the end of that string literal (and since as a C string it is empty it would be like a string copy). Literal values often are stored in memory that is not writable, so copying something into this memory would likely cause an error (segmentation fault or SIGSEGV on many operating systems). Additionally you only have two bytes of storage pointed to by test, which means that unless somestr refers to a string whose strlen is less than or equal to 1 you would end up attempting to write over some other memory (whatever happens to be after the "\0" that test points to).
char* tmp = strstr(test, 0, len);
I don't know what is going on here since strstr only takes 2 arguments (both of them const char *).
free(test);
Here you are attempting to free non-heap allocated memory. The heap is where malloc, realloc, and calloc get the memory they allocate. Calling free with a memory location that was not returned by one of these functions (and a few other functions on some systems) is an error because free does not know what to do with them.
You should probably keep in mind that often memory is huge array of bytes, and that the pointers you use are like array indexes. The system you are using may be able to distinguish between some areas of this array and determine how you can access them (readable, writable, and/or executable). But it is still just an array of bytes. When you have a string (such as "foo") that means that somewhere in RAM there are four bytes ( 3 letters + the \0 terminator byte) and you can access this area by knowing its index within the array of bytes that is RAM. There are likely other things that are stored adjacent to your string (such as { ..., 4, 2, 'f', 'o', 'o', 0, 99, 3, 2, ...}) so you have to try to make sure you stay within the space of that memory without wandering into the adjacent data.
There are a couple of problems:
strcat will append a string to the destination buffer. You need the first parameter to be a buffer not a string literal pointer. Here is an example of a char buffer or also called an array of chars: char test[1024];
The return value of strcat is a pointer to the destination buffer, it is not a newly allocated string on the heap. So you shouldn't call free on the return value.
You can't strcat to test because you are initially pointing it to a constant char *. You need to assign memory for it. strcat won't do it.
Change your code to something like:
char* test = (char*)malloc(20*sizeof(char));
test[0] = '\0'; // nothing on this string to begin with
strcat(test, "something");
free(test);
Also, this won't work:
char* tmp = strcat(test, 0, len);
Since there is no strcat function with three parameters.
Remember. 99.9% of the time there will be a free call for each malloc allocation.

Why do I get a segmentation fault when using strncpy?

I am getting segmentation fault when using strncpy and (pointer-to-struct)->(member) notation:
I have simplified my code. I initialise a struct and set all of it's tokens to an empty string. Then a declare a pointer to a struct and assign the address of the struct to it.
I pass the pointer to a function. I can print out the contents of the struct at the beginning of the function, but if I try to use the tp -> mnemonic in a strncpy function, I get seg fault. Can anyone tell me what I am doing wrong?
typedef struct tok {
char* label;
char* mnem;
char* operand;
}Tokens;
Tokens* tokenise(Tokens* tp, char* line) {
// This prints "load"
printf("Print this - %s\n", tp -> mnem);
// This function gives me segmentation fault
strncpy(tp -> mnem, line, 4);
return tp;
}
int main() {
char* line = "This is a line";
Tokens tokens;
tokens.label = "";
tokens.mnem = "load";
tokens.operand = "";
Tokens* tp = &tokens;
tp = tokenise(tp, line);
return 0;
}
I have used printf statements to confirm that the code definitely stops executing at the strncpy function.
The problem is that tp->mnem is pointing to a string literal, which is generally allocated in a read-only segment of memory. Therefore it's illegal to overwrite it. Most likely what you need to do instead is something like this:
Tokens tokens;
tokens.label = "";
tokens.mnem = strdup("load");
tokens.operand = "";
This will give you a dynamically allocated block of memory for mnem, which you can then write into as much as you like. Of course, you have a couple of other problems too: first, you'll need to remember to release that memory with free later; second, you'll have to be aware of the size of the buffer you've allocated so that you don't overwrite it.
If you know that the contents of mnem will never exceed 4 bytes, then you might instead change your structure declaration like so:
typedef struct tok {
char* label;
char mnem[5]; // note: +1 byte for a NULL terminator
char* operand;
}Tokens;
Then, you'd initialize it like this:
Tokens tokens;
tokens.label = "";
strcpy(tokens.mnem, "load");
tokens.operand = "";
This relieves you of the responsibility of managing the memory for mnem, although you still have some risk of overrunning your buffer.
Following line
tokens.mnem = "load"
assigns mnem to address of string literal, which is typically located in read-only data segment, so changing this memory with strncpy() or any other function will fail.
The problem is you've assigned string literals to the members of your Tokens structure and are trying to overwrite that memory (specifically, the mnem field) in tokenise.
Most modern OSes will allocate memory for string literals from a special read-only section of your program's address space. If you try to write to that memory, then your program will die with a segfault.
This is why the type of a string literal is const char *, not char *. Your compiler should warn you when you try to assign these to the fields of tokenise.
If you want to overwrite the memory later, you need to allocate the memory dynamically using malloc or change the members of the Tokens structure to fixed-length arrays, then copy the initial value into the allocated memory. Of course if you allocate the memory dynamically you need to free it later too.
You're calling strncpy() without having allocated the buffer spacem, just like Shadow said.
The literal string "load" you set the mnem member to in the initializer is not overwritable.
If you want to be able to change the string stored, and the size is reasonable, it might be easiest to just change the declaration of the struct field to char mnem[5];.
Also, please note that strncpy() has quite weird semantics. Check if you have strlcpy(); it's a better function.
You're getting a segmentation fault because this line:
strncpy(tp -> mnem, line, 4);
Is trying to copy four characters from 'line' into a location occupied by a string literal as assigned here:
tokens.mnem = "load";
The string literal is stored in a special text part of your program and may not be modified.
What you need to do is allocate a buffer of your own into which the string will be copied:
tokens.mnem = (char*) malloc (bufferSize);
And free the buffer when you are done using it.
This line is questionable:
strncpy(tp -> mnem, line, 4);
You are relying on a function that returns a pointer to memory that is not allocated. The return of *tokenise() is undefined. Its returning a pointer to memory that could contain all kinds of stuff, and that you don't have permission to modify.
It should return an allocated pointer.
You might malloc the tp variable. If you don't malloc there is no guarantee that the memory is actually yours. Don't forget to free the memory when you are finished.

Resources