invalid read memory - valgrind - c

Here is my code: I am trying to get the info of an struct and deep copy the info. But, valgrind shows that "invalid read". I know that is I read the memory that is released. I don't know why; is anyone able to figure it out for me?
Code
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct student
{
int id;
char *name;
int age;
};
void get_info(struct student *dest, struct student *src)
{
memcpy(dest,src,sizeof(struct student));
dest->name = strdup(src->name);
}
int main()
{
struct student foo;
foo.id = 1001;
foo.name = strdup("kevin");
foo.age = 18;
struct student bar;
get_info(&bar, &foo);
puts(bar.name);
free(foo.name);
free(bar.name);
return 0;
}
Valgrind report
valgrind --tool=memcheck --leak-check=full ./test
==2130== Memcheck, a memory error detector
==2130== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2130== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==2130== Command: ./test
==2130==
==2130== Invalid read of size 4
==2130== at 0x40B083B: ??? (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x40B04A4: strdup (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x80484B1: get_info (test.c:15)
==2130== by 0x80484F8: main (test.c:26)
==2130== Address 0x419902c is 4 bytes inside a block of size 6 alloc'd
==2130== at 0x4026775: malloc (vg_replace_malloc.c:291)
==2130== by 0x40B04AF: strdup (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x80484D8: main (test.c:22)
==2130==
==2130== Invalid read of size 4
==2130== at 0x40B083B: ??? (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x409ACE4: puts (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x8048504: main (test.c:28)
==2130== Address 0x4199064 is 4 bytes inside a block of size 6 alloc'd
==2130== at 0x4026775: malloc (vg_replace_malloc.c:291)
==2130== by 0x40B04AF: strdup (in /lib/tls/i686/cmov/libc-2.11.1.so)
==2130== by 0x80484B1: get_info (test.c:15)
==2130== by 0x80484F8: main (test.c:26)
==2130==
kevin
==2130==
==2130== HEAP SUMMARY:
==2130== in use at exit: 0 bytes in 0 blocks
==2130== total heap usage: 2 allocs, 2 frees, 12 bytes allocated
==2130==
==2130== All heap blocks were freed -- no leaks are possible
==2130==
==2130== For counts of detected and suppressed errors, rerun with: -v
==2130== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 11 from 6)

I think this is actually an error report from valgrind that you should suppress. It is ultimately a bug in the C library on your system.
The complaint is that code called from strdup() is reading 4 bytes at an offset of 4 bytes into a block of 6 bytes that were allocated by malloc(). Given that "kevin" occupies 6 bytes, then I believe a memcpy() variant has been employed by strdup() and has been caught in the act of reading 4 bytes at a time through the data. While it is probably actually safe, technically, valgrind is correct to complain. However, you can't do anything about that — your code is innocent, and the system library is guilty. That's the sort of thing suppressions are for!
+---+---+---+---+---+---+...+...+
| k | e | v | i | n | \0| ? | ? |
+---+---+---+---+---+---+...+...+
The fast copy is using the fact that the malloc()'d data is 4-byte (more likely, 8-byte) aligned. It copies the 4 bytes 'k', 'e', 'v', and 'i' in one operation; it then copies the other two bytes of the string ('n', '\0') plus the two bytes that are not technically part of the allocated space in a second operation. The minimum actual allocation is probably 8 bytes on a 32-bit system; it tends to be 16-bytes on 64-bit machines. That means that the extra two bytes are part of the space reserved for the memory allocation, but valgrind is correct to report that the code is copying outside the allocated space.

This looks like an optimization in strdup() and puts(), they read their input in chunks of four bytes instead of one byte at a time, however they are careful not to write beyond the end. This is safe as long as the four byte addresses are aligned: Properly aligned reads can never trigger a hardware exception, and since these two functions do not write beyond the end of the string, their operation is safe, even though it is illegal from a language point of view. And you can be certain that these four byte addresses will be properly aligned because it's an optimization, unaligned reading would bring the code to crawl.
Valgrind checks on the language level of correctness, not on the physical level, hence the error report. So I agree with Jonathan Leffler that you should just suppress this "error", which, looking at the comments, seems to be automatically done by more recent versions of valgrind.

Related

`Syscall param read(buf) points to unaddressable bytes` and `address is 0 bytes after a block of n bytes alloc'd`

Valgrind is giving me bizarre output that goes away if more memory is allocated. In my program, the number I have to add to make it go away is 2064. This number appears nowhere in my program, and I have been up and down the thing for a few hours now trying to find where I could have gone wrong. No luck. Everything seems airtight, and I see no reason why an extra 2064 bytes of memory should be needed.
Thankfully, I managed to reproduce the bug in a minimal program.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
/* 8500 is a number I chose that is just above the generated executable size (8472)
* I double-checked after compiling and the executable is still under 8472 bytes
* You may have to choose a different size.
*/
unsigned char *p = malloc(8500);
unsigned char *pp;
FILE *fp = fopen(argv[0], "rb");
ssize_t nread;
if(p == NULL)
return 1;
pp = p;
while((nread = fread(pp, 1, 4096, fp)) > 0)
pp += nread;
free(p);
fclose(fp);
return 0;
}
The output of valgrind (and wc):
$ gcc wtf.c -Wall
$ wc -c a.out
8472
$ valgrind ./a.out
==31142== Memcheck, a memory error detector
==31142== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31142== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31142== Command: ./a.out
==31142==
==31142== Syscall param read(buf) points to unaddressable byte(s)
==31142== at 0x4F4E151: read (read.c:27)
==31142== by 0x4EC9741: _IO_file_xsgetn (fileops.c:1364)
==31142== by 0x4EBD4A0: fread (iofread.c:38)
==31142== by 0x1087CF: main (in /home/braden/code/git/bfvm2/src/a.out)
==31142== Address 0x5231174 is 0 bytes after a block of size 8,500 alloc'd
==31142== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==31142== by 0x108782: main (in /home/braden/code/git/bfvm2/src/a.out)
==31142==
==31142==
==31142== HEAP SUMMARY:
==31142== in use at exit: 0 bytes in 0 blocks
==31142== total heap usage: 3 allocs, 3 frees, 13,148 bytes allocated
==31142==
==31142== All heap blocks were freed -- no leaks are possible
==31142==
==31142== For counts of detected and suppressed errors, rerun with: -v
==31142== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
Any ideas? I don't believe there's any undefined behavior here. This isn't preventing my program from running, but it's driving me crazy, and I don't want it to turn into something serious in the future.
The valgrind message indicates an attempt to write past the end of the allocated space.
This could be coming from the call to fread requesting 4096 bytes when there is not that much space remaining in the buffer -- even though there may only be a few characters left in the input stream.
The C Standard isn't entirely clear on this matter but regardless of that it seems either:
your compiler is taking the liberty of writing to some other locations within the 4096 requested that are past the end of the allocated space, or
Valgrind is reporting an error for passing the incomplete buffer to system call read, even if the read call wouldn't have written past the end.
To avoid this, make sure the maximum extent passed to fread does not exceed the remaining buffer size, e.g. fread(pp, 1, MIN(4096, (p + 8500 - pp)), fp) where MIN is the usual minimum macro.

What does Valgrind mean when it says memory is "definitely lost"?

The following program:
#include <stdlib.h>
int main(void)
{
char *my_str = malloc(42 * sizeof(char));
return 0;
}
Compiled as such:
gcc -g -o prog prog.c
And executed with Valgrind as such:
valgrind --leak-check=full ./prog
Produces the following (truncated) output:
...
==18424== HEAP SUMMARY:
==18424== in use at exit: 42 bytes in 1 blocks
==18424== total heap usage: 1 allocs, 0 frees, 42 bytes allocated
==18424==
==18424== 42 bytes in 1 blocks are definitely lost in loss record 1 of 1
==18424== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18424== by 0x10865B: main (main.c:5)
==18424==
==18424== LEAK SUMMARY:
==18424== definitely lost: 42 bytes in 1 blocks
==18424== indirectly lost: 0 bytes in 0 blocks
==18424== possibly lost: 0 bytes in 0 blocks
==18424== still reachable: 0 bytes in 0 blocks
==18424== suppressed: 0 bytes in 0 blocks
...
Why does Valgrind say that the unfreed memory is "definitely lost"?
From the Valgrind Memcheck docs:
This means that no pointer to the block can be found. The block is classified as "lost", because the programmer could not possibly have freed it at program exit, since no pointer to it exists. This is likely a symptom of having lost the pointer at some earlier point in the program.
However, it's pretty clear that I could have easily freed the block before program exit. What am I missing here? Shouldn't the memory be "still reachable"?
Here's an example of "definitely lost" vs. "still reachable":
#include <stdio.h>
#include <stdlib.h>
void *p;
int main()
{
p = malloc(10);
p = malloc(100);
void *m = malloc(50);
return 0;
}
What happens in this code is the following:
10 bytes are allocated and the pointer to this memory is assigned to the global p.
100 bytes are allocated and the pointer to this memory is assigned to the global p. This overwrites the pointer value that pointed to 10 allocated bytes, and there is no other reference to it. So that memory is "definitely lost".
50 bytes are allocated and the pointer to this memory is assigned to the local m.
main returns causing the lifetime of m to end. As a result there is no reference stored to the memory pointer to 50 bytes so that memory is "definitely lost".
The pointer value pointing to 100 bytes still lives in the global p, so cleanup routines such as those called by atexit or other compiler specific methods could still clean up that memory. So it is "still reachable".
When running valgrind on this code, it outputs the following:
==60822== Memcheck, a memory error detector
==60822== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==60822== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==60822== Command: ./x1
==60822==
==60822==
==60822== HEAP SUMMARY:
==60822== in use at exit: 160 bytes in 3 blocks
==60822== total heap usage: 3 allocs, 0 frees, 160 bytes allocated
==60822==
==60822== LEAK SUMMARY:
==60822== definitely lost: 60 bytes in 2 blocks
==60822== indirectly lost: 0 bytes in 0 blocks
==60822== possibly lost: 0 bytes in 0 blocks
==60822== still reachable: 100 bytes in 1 blocks
==60822== suppressed: 0 bytes in 0 blocks
==60822== Rerun with --leak-check=full to see details of leaked memory
==60822==
==60822== For counts of detected and suppressed errors, rerun with: -v
==60822== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Which is consistent with the description above.
So to summarize, memory is "still reachable" if a pointer to it is stored either in a file scope variable or is pointed to by any "still reachable" memory. Otherwise it is lost.
Why does Valgrind say that the unfreed memory is "definitely lost"?
Because - once main returns the variable my_str doesn't exist anymore. Consequently, there is no way to free the allocated memory. And there is no way to reach the memory. No one know where the memory is. It's lost.
Note: All modern OS systems will however take care of the memory clean up so it's not a real problem.
Valgrind is checking for memory leaks, and you are leaking memory as you didn't free the pointer before exit. You should free(my_str) .
int main(void)
{
char *my_str = malloc(42 * sizeof(char));
free (my_str);
return 0;
}
Definitely Lost mean that nobody has a reference to that memory location anymore, so nobody could free that memory. In a context of a running program that will be a segment of memory that nobody could reuse (so it is lost). Of course at the end of the program nobody will ever use it again, but some of the memory could be referenced by another leaking object (and will be an indirect lost), so with those hints you can debug your program. The link bellow shows a discussion on the topic.
This is a complete response on why you should free memory before exit

What if memory is not allocated to '\0' in char array and char pointer

The following two programs are almost similar. In the two programs, memory is not allocated for null('\0') character.
Ex A:
void main()
{
char *ptr;
ptr = (char *)malloc(2);
strcpy(ptr, "ls");
printf("%s\n",ptr);
system(ptr);
free(ptr);
}
Ex B:
void main()
{
char ptr[2] = "ls";
system(ptr);
}
1.The first program(Ex. A) is working But i have seen error only with valgrind tool.
output
[root#localhost tmp]# valgrind --leak-check=full ./a.out
==8619== Memcheck, a memory error detector
==8619== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8619== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==8619== Command: ./a.out
==8619==
==8619== Invalid write of size 1
==8619== at 0x400635: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8619== Address 0x51f2042 is 0 bytes after a block of size 2 alloc'd
==8619== at 0x4C29C4F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8619== by 0x400627: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8619==
==8619== Invalid read of size 1
==8619== at 0x4C2CC14: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8619== by 0x4EA4D3B: puts (in /usr/lib64/libc-2.20.so)
==8619== by 0x400644: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8619== Address 0x51f2042 is 0 bytes after a block of size 2 alloc'd
==8619== at 0x4C29C4F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8619== by 0x400627: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8619==
ls
==8620== Syscall param execve(argv[i]) points to unaddressable byte(s)
==8620== at 0x4EF9537: execve (in /usr/lib64/libc-2.20.so)
==8620== by 0x4E77D18: do_system (in /usr/lib64/libc-2.20.so)
==8620== by 0x400650: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8620== Address 0x51f2042 is 0 bytes after a block of size 2 alloc'd
==8620== at 0x4C29C4F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==8620== by 0x400627: main (in /home/gen4linux/Projects/Cprgm/tmp/a.out)
==8620==
a.out test37.c
==8619==
==8619== HEAP SUMMARY:
==8619== in use at exit: 0 bytes in 0 blocks
==8619== total heap usage: 1 allocs, 1 frees, 2 bytes allocated
==8619==
==8619== All heap blocks were freed -- no leaks are possible
==8619==
==8619== For counts of detected and suppressed errors, rerun with: -v
==8619== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
2.But the second one is not working
Ex B
[root#localhost Cprgm]# ./a.out
sh: $'ls%\211\376\177': command not found
Why this happens?
TL;DR both the codes cause undefined behavior.
In first case
ptr = (char *)malloc(2);
strcpy(ptr, "ls");
you're off-by-one, as mentioned in C11, chapter §7.24.2.3,
The strcpy function copies the string pointed to by s2 (including the terminating null
character) into the array pointed to by s1.
So, the size of s1 should at least be strlen(s2)+ 1.
In second case
char ptr[2] = "ls";
ptr does not have a null-terminator which in essence, causes out of bound access which again causes UB.
Related, quoting the POSIX manual,
[...] If command is
not a null pointer, the system() function shall pass the string
pointed to by command to that command processor to be executed in an
implementation-defined manner; [...]
A char array, without a null-terminator in place, is not considered a string.
Having said that, there are a few sggestions,
For a hosted environment, void main() should at least be int main(void) to conform to the standard.
See this discussion on why not to cast the return value of malloc() and family in C..
The array that you used in Ex B is only of length 2. whenever you store a string in a array "ls" automatically a null character is added with is '\0' so "ls" will become l s \0 when it is stored in memory. Most of the algorithm are based on this NULL character to find the length of string of value of string.
So just declare your array of length 3 and you will be good to go.
Thanks.
In the first case:
ptr = (char *)malloc(2);
memory is allocated for two bytes but strcpy stored 3 bytes at this location. So it is basically a memory corruption case but still "ls" command with \0 at the end is stored at the location which makes system command to work.
In the second case:
char ptr[2] = "ls";
This assignment itself is wrong and is not supposed to work. Behaviour is undefined.

Valgrind Reports Invalid Realloc

I'm trying to backfill my knowledge of C memory management. I've come from a mostly scripting and managed background, and I want to learn more about C and C++. To that end I've been reading a few books, including one which included this example of using realloc to trim a string of whitespace:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* trim(char* phrase)
{
char* old = phrase;
char* new = phrase;
while(*old == ' ') {
old++;
}
while(*old) {
*(new++) = *(old++);
}
*new = 0;
return (char*)realloc(phrase, strlen(phrase)+1);
}
int main ()
{
char* buffer = (char*)malloc(strlen(" cat")+1);
strcpy(buffer, " cat");
printf("%s\n", trim(buffer));
free(buffer);
buffer=NULL;
return 0;
}
I dutifully copied the example, and compiled with c99 -Wall -Wpointer-arith -O3 -pedantic -march=native. I don't get any compile errors, and the app runs and does what's promised in the book, but when I run it against valgrind I get an error about invalid realloc.
==21601== Memcheck, a memory error detector
==21601== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21601== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==21601== Command: ./trim
==21601==
==21601== Invalid free() / delete / delete[] / realloc()
==21601== at 0x402B3D8: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==21601== by 0x804844E: main (in /home/mo/programming/learning_pointers/trim)
==21601== Address 0x4202028 is 0 bytes inside a block of size 6 free'd
==21601== at 0x402C324: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==21601== by 0x80485A9: trim (in /home/mo/programming/learning_pointers/trim)
==21601== by 0x804842E: main (in /home/mo/programming/learning_pointers/trim)
==21601==
==21601==
==21601== HEAP SUMMARY:
==21601== in use at exit: 4 bytes in 1 blocks
==21601== total heap usage: 2 allocs, 2 frees, 10 bytes allocated
==21601==
==21601== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==21601== at 0x402C324: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==21601== by 0x80485A9: trim (in /home/mo/programming/learning_pointers/trim)
==21601== by 0x804842E: main (in /home/mo/programming/learning_pointers/trim)
==21601==
==21601== LEAK SUMMARY:
==21601== definitely lost: 4 bytes in 1 blocks
==21601== indirectly lost: 0 bytes in 0 blocks
==21601== possibly lost: 0 bytes in 0 blocks
==21601== still reachable: 0 bytes in 0 blocks
==21601== suppressed: 0 bytes in 0 blocks
==21601==
==21601== For counts of detected and suppressed errors, rerun with: -v
==21601== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
So please help me understand why it's consider an invalid realloc. Is the example crap? Is there something I'm missing? I know that according to the specs, realloc expects the pointer to have been created previously by malloc, so is it because the realloc is in another function? Or is valgrind confused because they're in separate functions? I'm not a complete idiot (most days), but right now I kind of feel like one for not seeing the issue.
Thanks in advance!
You're trying to free the original pointer, not the reallocd one. You can fix it by:
buffer = trim(buffer)

Assigning strings to a struct member (pointer) in C

I need to be able to fill out my my_s1 structure with data. I am passing it to a get_data() function that should do all the dirty work. The problem I have is the b member of my structure which is a pointer. I have no idea how to properly assign value pointed by (char *) buff to b without segmentation faults or valgrind errors.
For example:
Why does initial p1->b="abc"; works fine, but if i try to strcpy() or assign through "=" operator an array to p1->b i get errors?
Does s1 my_s1 allocate memory for b or should I somehow malloc() p1->b myself? But then again i need to free() it and assign a NULL pointer before returning from the function, which defeats the purpose (of having the function assigning data to structure), right?
With current code listed below I have "proper execution result" but I also get the following valgrind output errors (from what I understand, please correct me if I'm wrong, it seems as if printf() accesses not properly allocated memory - so it works in this case but it's rubbish) :
valgrind:
==1067== Memcheck, a memory error detector
==1067== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==1067== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==1067== Command: ./if
==1067== Parent PID: 1059
==1067==
==1067== Invalid read of size 1
==1067== at 0x4E7ADF9: vfprintf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4005EF: main (iface.c:10)
==1067== Address 0x51f3040 is 0 bytes inside a block of size 5 free'd
==1067== at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067== by 0x40064D: get_data (ifacelib.c:17)
==1067== by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 1
==1067== at 0x4EA9459: _IO_file_xsputn##GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067== by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4005EF: main (iface.c:10)
==1067== Address 0x51f3043 is 3 bytes inside a block of size 5 free'd
==1067== at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067== by 0x40064D: get_data (ifacelib.c:17)
==1067== by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 1
==1067== at 0x4EA946C: _IO_file_xsputn##GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067== by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4005EF: main (iface.c:10)
==1067== Address 0x51f3042 is 2 bytes inside a block of size 5 free'd
==1067== at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067== by 0x40064D: get_data (ifacelib.c:17)
==1067== by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 4
==1067== at 0x4EBBDDE: __GI_mempcpy (in /usr/lib64/libc-2.17.so)
==1067== by 0x4EA939C: _IO_file_xsputn##GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067== by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067== by 0x4005EF: main (iface.c:10)
==1067== Address 0x51f3040 is 0 bytes inside a block of size 5 free'd
==1067== at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067== by 0x40064D: get_data (ifacelib.c:17)
==1067== by 0x4005D3: main (iface.c:8)
==1067==
==1067==
==1067== HEAP SUMMARY:
==1067== in use at exit: 0 bytes in 0 blocks
==1067== total heap usage: 1 allocs, 1 frees, 5 bytes allocated
==1067==
==1067== All heap blocks were freed -- no leaks are possible
==1067==
==1067== For counts of detected and suppressed errors, rerun with: -v
==1067== ERROR SUMMARY: 10 errors from 4 contexts (suppressed: 2 from 2)
Code in 3 files.
ifacelib.h:
#ifndef IFACELIB_H
#define IFACELIB_H
typedef struct
{
int a;
char * b;
} s1;
int get_data(s1 *);
#endif
ifacelib.c:
#include "ifacelib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int get_data(s1 *p1)
{
char *buff;
p1->a=1;
p1->b="abc";
buff = (char *) malloc(strlen("test")*sizeof(char)+1);
strcpy(buff, "test");
p1->b = buff;
free(buff);
buff = NULL;
return 0;
}
iface.c:
#include "ifacelib.h"
#include <stdio.h>
int main()
{
s1 my_s1;
if ((get_data(&my_s1))==0)
{
printf("a= %d\tb= %s\n", my_s1.a, my_s1.b);
}
return 0;
}
Any help or just pointing in a right direction would be appreciated.
From best practices point of view, when working with structures should I be writing a function that fills out data in structure (works on a passed structure) and returns int to control successes/fails or should I be writing a function that returns a modified structure instead ?
This is my first post here, so please bear with me, my formatting mistakes, walls of text and my ignorance.
Thanks in advance,
Tom
You are doing wrong, and just lucky to get correct result, in fact you are accessing memory that just being freed.
It's right that you have to malloc for the char* in struct(by the way, you can use strdup), but you need another destructor to free the struct when their job are done.
In your case, you need a function like free_s1 after the printf, rather than free in constructor function(get_data).
This works in C, not so good in C++:
If you are going to malloc(), it may be better to malloc both your structure and the data area in one blow. Rather than having a pointer arrange a minimal data area at the end of the struct. When allocating the struct add additional bytes to allow for the data. You are then good to go. One free will release both struct and data.
Reworking fragments of your code to show the key idea I get the following. I leave compiling, debugging, even syntax checking, for the student.
typedef struct
{
int a;
char * b;
char data[1]; // data goes here.
// structure MUST be malloced at run time WITH
// extra storage for data.
} s1;
s1 *p1;
data = "test";
data_len = strlen(data); // additional bytes of storage
p1 = malloc( data_len + sizeof( *p1 ) ); // allocate structure + data
strcpy(s1->data, data ); // copy data to buffer
...
free(p1); // free storage

Resources