I just started using Valgrind, and I'm not sure the error messages I get are what they should be. For instance, Valgrind just gave me a very long chain of warnings about printf() making jumps based on uninitialized memory. It was obviously that the issue wasn't with printf(). Rather, my program was giving printf() tainted memory.
I managed to produce the following MCVE:
#include <stdio.h>
int main(void)
{
int d;
d++;
printf("%d\n", d);
return 0;
}
It's really obvious that the problem here lies in d++. However, Valgrind only detects and warns me of uninitialized memory usage in the next line, with 6 messages in the form
==12178== Conditional jump or move depends on uninitialised value(s)
==12178== at 0x4E7F79D: vfprintf (vfprintf.c:1636)
==12178== by 0x4E871F8: printf (printf.c:33)
==12178== by 0x1086D1: main (mcve.c:7)
I compiled this with
gcc mcve.c -g -O0
And I ran Valgrind with
valgrind --leak-check=yes ./a.out
Then I discovered there's --track-origins=yes. It attempts to help, but it gets lost easily with pointers. For instance, it doesn't work for the next MCVE:
#include <stdio.h>
int f2(int *p)
{
(*p)++;
return *p;
}
int f1(void)
{
int d;
return f2(&d);
}
int main(void)
{
printf("%d\n", f1());
return 0;
}
Here it says that the error is in the stack frame of f1(). It's kind of helpful, but considering the performance penalty involved, maybe it's not worth it.
What can I do to make best use of Valgrind?
A deliberate decision in Valgrind is that it does not complain
just because some uninitialised memory is copied or similar.
It complaints only when the use of uninitialised memory changes the
behaviour of your program. This deliberate choice avoids a lot of false-positive errors.
See http://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.uninitvals
for more information.
Related
I have a very interesting problem.
I'd like to use PCRE2, and its JIT function. The task is simple: read lines from a file, and find patterns.
Here is the sample code:
#include <stdio.h>
#include <string.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
int search(pcre2_code *re, unsigned char * subject) {
pcre2_match_data *match_data_real = pcre2_match_data_create_from_pattern(re, NULL);
size_t len_subject = strlen((const char *)subject);
int rc = pcre2_match(
re,
(PCRE2_SPTR)subject,
len_subject,
0,
0,
match_data_real,
NULL
);
pcre2_match_data_free(match_data_real);
return rc;
}
int main(int argc, char ** argv) {
unsigned char subject[][100] = {
"this is a foobar",
"this is a barfoo",
"this is a barbar",
"this is a foofoo"
};
pcre2_code *re;
PCRE2_SPTR pattern = (unsigned char *)"foo";
int errornumber;
PCRE2_SIZE erroroffset;
re = pcre2_compile(
pattern,
PCRE2_ZERO_TERMINATED,
0,
&errornumber,
&erroroffset,
NULL
);
pcre2_jit_compile(re, PCRE2_JIT_COMPLETE);
FILE *fp;
int s = 0;
while(s < 2) {
search(re, subject[s++]);
}
if (argc >= 2) {
fp = fopen(argv[1], "r");
if (fp != NULL) {
char tline[2048];
while(fgets(tline, 2048, fp) != NULL) {
search(re, (unsigned char *)tline);
}
fclose(fp);
}
}
pcre2_code_free(re);
return 0;
}
Compile the code:
gcc -Wall -O2 -g pcretest.c -o pcretest -lpcre2-8
As you can see, in line 58 I check if there is an argument given, the code tries to open it as a file.
Also as you can see in line 49, I'd like to use PCRE2's JIT.
The code works as well, but I checked it with Valgrind, and found an interesting behavior:
if I add a file as argument, then Valgrind reports Conditional jump or move depends on uninitialised value(s) and Uninitialised value was created by a stack allocation, but it points to the main(). The command:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes -s ./pcretest myfile.txt
Without argument, there is no any Valgrind report. Command:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes -s ./pcretest
if I comment out the pcre2_jit_compile((*re), PCRE2_JIT_COMPLETE); in line 55, then everything works as well, no any Valgrind reports. Command:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes -s ./pcretest myfile.txt
The Valgrind's relevant output:
==31385== Conditional jump or move depends on uninitialised value(s)
==31385== at 0x4EECD1A: ???
==31385== by 0x1FFEFFFC1F: ???
==31385== Uninitialised value was created by a stack allocation
==31385== at 0x1090FA: main (pcretest.c:27)
...
==31385== HEAP SUMMARY:
==31385== in use at exit: 0 bytes in 0 blocks
==31385== total heap usage: 12 allocs, 12 frees, 13,486 bytes allocated
==31385==
==31385== All heap blocks were freed -- no leaks are possible
==31385==
==31385== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==31385==
==31385== 1 errors in context 1 of 1:
==31385== Conditional jump or move depends on uninitialised value(s)
==31385== at 0x4EECD1A: ???
==31385== by 0x1FFEFFFC1F: ???
==31385== Uninitialised value was created by a stack allocation
==31385== at 0x1090FA: main (pcretest.c:27)
==31385==
==31385== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
In line 27 there is the int main(...).
What do I miss?
Observations:
The Valgrind report is telling you that the uninitialized data being accessed are in the stack frame of the initial call to main(). However,
even though you're compiling with debug information, the Valgrind report does not implicate a specific variable. Also,
the report's stack trace for the error does not present function names, and does not trace back to main(). And of course,
the error is not reported when you disable JIT compilation of the pattern.
Apparently, then, the error is associated with the machine code generated by PCRE2's JIT compiler. If you don't perform JIT compilation then you get correct operation via the ordinary matching path. If you do perform JIT compilation then the JIT-generated code is engaged, and that code triggers the Valgrind error. You might nevertheless get correct matching, but I would not rely on that for code that triggers the Valgrind error observed.
I played around with variations on your code, and discovered that the error is specifically associated with the calls to pcre2_match_data_create_from_pattern() and pcre2_match() in function search(). Either one will cause Valgrind to report the error. But why does the error occur only in some calls to search()?
It seems likely to be because the JIT compilation sets up data structures in main()'s stack frame that are clobbered by executing the body of the if (argc > 2) statement. This is consistent with the fact that I was able to avoid the error by adding an initializer for variable tline in that block:
char tline[2048] = {0};
I can imagine a variety of scenarios for why that might make a difference, all having to do with how the JIT-generated code and the compiler-generated code manipulate the stack pointer.
Personally, discovering such an issue would likely persuade me to stay far away from PCRE's JIT compiler. Definitely I would do that at least until I had evidence of pattern matching being a performance hotspot for my program. If you must engage the JIT, however, then here are some recommendations that might (or might not) help you avoid trouble:
Take "just in time" to heart: perform JIT as close as possible to when you actually use the pattern.
Do not assume that the JIT code is long-term viable. In particular, it probably is unsafe to use after the function that calls the JIT compiler returns, but it might not be good even that long.
Use the JIT-compiled regex (only) in the same function that runs the JIT compiler.
Make that function as simple as possible.
Declare all local variables of that function at the beginning, with initializers.
Test thoroughly.
That's more than seems to have been necessary to resolve the issue for your particular example code, but it's aimed more generally at reducing the cross section for the compiled program violating assumptions made by the JIT.
This is indeed caused by efficient use of SSE2. CPU-s use 1K or bigger pages to map memory, so a 16 byte aligned 16 byte read (SSE2 registers are 16 byte long) which intersects with a valid buffer is always valid. However, bytes before the start or after the end of the buffer might never be initialized. The algorithm ignores these bytes, so the random data (regardless it is initialized or not) have no effect on any computation.
I know it must've been answered somewhere, but I didn't find any information regarding this strange behavior. I was just messing around with the heap, and when I executed the program with zero optimiziations, there was no error. I went to godbolt and it looks like there's no assembly instructions at all.
What happens to the code when you pass only -O without any level?
What's the difference between this and -O0, that by the way, works well?
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
}
$ gcc -O -g -fsanitize=address main.c -o main
$ ./main # No problems
gdb) info locals # Without -fsanitize=address
x = <optimized out>
n = <optimized out>
I went to godbolt and it looks like there's no assembly instructions at all
Indeed, GCC optimized out all your code because you do not use in any way. If you change it slightly then it will stay in generated assembly and AddressSanitizer will find heap overflow.
The following code generates AddressSanitizer heap-buffer-overflow error as you expect:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char* x = malloc(10);
char n = x[11];
return n;
}
What happens to the code when you pass only -O without any level?
It's the same as -O1, "optimize a little bit"
What's the difference between this and -O0, that by the way, works well?
Since your code has no obvious side effects, it will be removed by the optimizer if enabled. This happens no matter if you invoke undefined behavior with x[11] or if you access a valid index x[0]. Writing to the x[0] item first to ensure it isn't indeterminate doesn't have any effect either.
-O0 explicitly disables optimizations, so you'll get some manner of machine code generated... maybe. The compiler doesn't have to generate anything predictable or meaningful in case your code contains undefined behavior.
There's generally no bounds-checking in C, it's the programmer's responsibility to handle it.
As for -fsanitize=address, add a side effect and it might kick in:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
printf("%d\n", n);
}
Results in:
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address-...
The docs answer both of your questions.
What happens to the code when you pass only -O without any level?
-O
-O1
Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function.
[...]
It enables optimizations.
What's the difference between this and -O0
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
As the default, it really does nothing at all.
In this mode, optimizations (or at least non-trivial ones) are effectively disabled.
Zero optimiziation doesn't detect heap overflow
On Compiler Explorer, you did enable optimizations (by using -O).
If you stop optimizing the variables away (by removing -O or by using -O0), you get the expected error.
<source>: In function 'main':
<source>:7:15: warning: unused variable 'n' [-Wunused-variable]
7 | char n = x[11];
| ^
<source>:7:15: warning: 'x[11]' is used uninitialized [-Wuninitialized]
7 | char n = x[11];
| ^
Program returned: 1
Program stderr
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001b at pc 0x0000004011a4 bp 0x7fffc6caef80 sp 0x7fffc6caef78
READ of size 1 at 0x60200000001b thread T0
#0 0x4011a3 in main /app/example.c:7
#1 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
#2 0x40109d in _start (/app/output.s+0x40109d)
0x60200000001b is located 1 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
#0 0x7f88c4e9d1af in malloc (/opt/compiler-explorer/gcc-12.1.0/lib64/libasan.so.8+0xbb1af)
#1 0x401167 in main /app/example.c:6
#2 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
[snip]
Demo on Compiler Explorer
The same would happen with optimizations if the program actually used n.
I want to use LeakSanitizer to detect leaked memory, but the style of the program I am using does not free memory before exit. This is fairly common in my experience.
I want to detect this leak:
int main(int argc, char const *argv[])
{
char *p = malloc(5);
p = 0;
return 0;
}
And ignore this leak:
int main(int argc, char const *argv[])
{
char *p = malloc(5);
return 0;
}
You want LSan to report only unreachable leaks i.e. pointers which are guaranteed to be leaked by the program. Problem is that by default LeakSanitizer runs it's checks at the end of the program, often after global C++ dtors have completed and their contents is no longer considered accessible. So when LSan finally runs, it has to assume that lots of stuff is no longer reachable. To work around this issue you can add
#include <lsan_interface.h>
...
#ifdef __SANITIZE_ADDRESS__
__lsan_do_leak_check();
__lsan_disable();
#endif
before returning from main (inspired by Issue 719 and llvm discussion).
PS: Be careful with extremely simple examples like the ones you post above. GCC will often remove unused assignments and allocations even at -O0 so always check that assembler matches your expectations.
Can I detect a possible segmentation fault at compile-time?
I understand the circumstance of a segmentation fault. But I am curious if GCC as a compiler has some flags to check for the basic scenarios resulting in segmentation faults.
This would help enormously to take precautions before releasing a library.
Can I detect a possible segmentation fault at compile time?
Sometimes, but no, you can't flawlessly detect these scenarios at compile time. Consider the general case in this C code:
volatile extern int mem[];
void foo (int access)
{
mem[access];
}
A compiler would be too noisy if it were to warn about this access at compile time, the code is valid C and a warning is, in general, inappropriate. Static analysis can't do anything with this code unless you have a mechanism for whole-program or link-time analysis.
An additonal optimization flag in GCC 4.8 which can sometimes catch a few out-of-bounds access in loops is `-faggressive-loop-optimizations'. This found a number of issues in the SPEC benchmark suite last year (http://blog.regehr.org/archives/918)
I understand the circumstance of segmentation fault. But i am curious if GCC as a compiler has some flags to check for the basic scenarios resulting in segmention faults.
GCC 4.8 comes with an address sanitizer which can help catch some of these run-time only issues (out of bounds/use-after-free bugs). You can use it with
-fsanitize=address.
http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Debugging-Options.html#Debugging-Options
GCC 4.9 (which will be released within the next few months) comes with an undefined behaviour sanitizer and more aggressive optimization of NULL pointer paths, which might help you catch some more issues. When it comes, it will be available with -fsanitize=undefined
http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options
Note however that neither of these are "compile-time" solutions, they both rely on instrumenting the binary and performing run-time checks.
Yes, there are ways of detecting some faults that may cause runtime errors such as segmentation faults. Those ways are called warnings. Many warnings messages are places where you have undefined behavior, and undefined behavior is often the leading cause of runtime crashes.
When I build, I always use the -Wall, -Wextra and -pedantic flags.
Other than that, there are really no good way of detecting all places that may cause segmentation faults (or other runtime errors), except strict coding guidelines, code reviews and plenty of testing.
gcc -Wall -Werror as mention by Joachim Pileborg are very good ideas. You could also use another compiler maybe. some reports more memory issues. I think you can not do a lot more at compile time.
At running time, I highly recommend Valgrind, which is a amazing tool for detecting memory issues. (don't forget to compile with the -g option)
Can I detect a possible segmentation fault at compile-time?
Yes, it is possible. Unfortunately, it is very limited what the compiler can do. Here is a buggy code example and the output from gcc and clang:
#include <stdlib.h>
int main() {
int a[4];
int x, y;
a[5]=1;
if(x)
y = 5;
x = a[y];
int* p = malloc(3*sizeof(int));
p[5] = 0;
free(p);
free(p);
}
For this buggy code, gcc -Wall -Wextra corrupt.c gives
corrupt.c: In function ‘main’:
corrupt.c:13:1: warning: control reaches end of non-void function [-Wreturn-type]
corrupt.c:6:7: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
clang catches more:
corrupt.c:5:5: warning: array index 5 is past the end of the array (which contains 4 elements) [-Warray-bounds]
a[5]=1;
^ ~
corrupt.c:3:5: note: array 'a' declared here
int a[4];
^
corrupt.c:6:8: warning: variable 'y' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
if(x)
^
corrupt.c:8:11: note: uninitialized use occurs here
x = a[y];
^
corrupt.c:6:5: note: remove the 'if' if its condition is always true
if(x)
^~~~~
corrupt.c:4:13: note: initialize the variable 'y' to silence this warning
int x, y;
^
= 0
corrupt.c:6:8: warning: variable 'x' is uninitialized when used here [-Wuninitialized]
if(x)
^
corrupt.c:4:10: note: initialize the variable 'x' to silence this warning
int x, y;
^
= 0
3 warnings generated.
I believe the above code example gives you insight what to expect. (Even though I tried, I could not get the static analyzer in clang to work.)
This would help enormously to take precautions before releasing a library.
As you can see above, it won't be an enormous help, unfortunately. I can only confirm that instrumentation is currently the best way to debug your code. Here is another code example:
#include <stdlib.h>
int main() {
int* p = malloc(3*sizeof(int));
p[5] = 0; /* line 4 */
free(p);
p[1]=42; /* line 6 */
free(p); /* line 7 */
}
Compiled as clang -O0 -fsanitize=address -g -Weverything memsen.c. (GCC 4.8 also has address santizier but I only have gcc 4.7.2.) The output:
==3476==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000f004 at pc 0x4887a7 bp 0x7fff9544be30 sp 0x7fff9544be28
WRITE of size 4 at 0x60200000f004 thread T0
#0 0x4887a6 in main /home/ali/tmp/memsen.c:4
[...]
Awesome, we know what went wrong (heap-buffer-overflow) and where (in main /home/ali/tmp/memsen.c:4). Now, I comment out line 4 and get:
==3481==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000eff4 at pc 0x4887d7 bp 0x7fff27a00d50 sp 0x7fff27a00d48
WRITE of size 4 at 0x60200000eff4 thread T0
#0 0x4887d6 in main /home/ali/tmp/memsen.c:6
[...]
Again, we see what went wrong and where. Finally, I comment out line 6.
==3486==ERROR: AddressSanitizer: attempting double-free on 0x60200000eff0 in thread T0:
#0 0x46dba1 in free /home/ali/llvm/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:65
#1 0x48878c in main /home/ali/tmp/memsen.c:7
[...]
Also caught the problem.
If your code has tests, or at least you can run your code with different inputs on your machine before releasing the library, you could probably track down a significant portion of the bugs. Unfortunately, it is not a compile-time solution and you probably don't want to release instrumented code (code compiled with -fsanitize=* flag). So if the user runs your code with an input that triggers a bug, the program will still crash with a segmentation fault.
How do I use valgrind to find the memory leaks in a program?
Please someone help me and describe the steps to carryout the procedure?
I am using Ubuntu 10.04 and I have a program a.c, please help me out.
How to Run Valgrind
Not to insult the OP, but for those who come to this question and are still new to Linux—you might have to install Valgrind on your system.
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
sudo pacman -Syu valgrind # Arch, Manjaro, Garuda, etc
Valgrind is readily usable for C/C++ code, but can even be used for other
languages when configured properly (see this for Python).
To run Valgrind, pass the executable as an argument (along with any
parameters to the program).
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
The flags are, in short:
--leak-check=full: "each individual leak will be shown in detail"
--show-leak-kinds=all: Show all of "definite, indirect, possible, reachable" leak kinds in the "full" report.
--track-origins=yes: Favor useful output over speed. This tracks the origins of uninitialized values, which could be very useful for memory errors. Consider turning off if Valgrind is unacceptably slow.
--verbose: Can tell you about unusual behavior of your program. Repeat for more verbosity.
--log-file: Write to a file. Useful when output exceeds terminal space.
Finally, you would like to see a Valgrind report that looks like this:
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I have a leak, but WHERE?
So, you have a memory leak, and Valgrind isn't saying anything meaningful.
Perhaps, something like this:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
Let's take a look at the C code I wrote too:
#include <stdlib.h>
int main() {
char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
return 0;
}
Well, there were 5 bytes lost. How did it happen? The error report just says
main and malloc. In a larger program, that would be seriously troublesome to
hunt down. This is because of how the executable was compiled. We can
actually get line-by-line details on what went wrong. Recompile your program
with a debug flag (I'm using gcc here):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c # add -ggdb3 to it
Now with this debug build, Valgrind points to the exact line of code
allocating the memory that got leaked! (The wording is important: it might not
be exactly where your leak is, but what got leaked. The trace helps you find
where.)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
Techniques for Debugging Memory Leaks & Errors
Make use of www.cplusplus.com! It has great documentation on C/C++ functions.
General advice for memory leaks:
Make sure your dynamically allocated memory does in fact get freed.
Don't allocate memory and forget to assign the pointer.
Don't overwrite a pointer with a new one unless the old memory is freed.
General advice for memory errors:
Access and write to addresses and indices you're sure belong to you. Memory
errors are different from leaks; they're often just IndexOutOfBoundsException
type problems.
Don't access or write to memory after freeing it.
Sometimes your leaks/errors can be linked to one another, much like an IDE discovering that you haven't typed a closing bracket yet. Resolving one issue can resolve others, so look for one that looks a good culprit and apply some of these ideas:
List out the functions in your code that depend on/are dependent on the
"offending" code that has the memory error. Follow the program's execution
(maybe even in gdb perhaps), and look for precondition/postcondition errors. The idea is to trace your program's execution while focusing on the lifetime of allocated memory.
Try commenting out the "offending" block of code (within reason, so your code
still compiles). If the Valgrind error goes away, you've found where it is.
If all else fails, try looking it up. Valgrind has documentation too!
A Look at Common Leaks and Errors
Watch your pointers
60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
And the code:
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
As a teaching assistant, I've seen this mistake often. The student makes use of
a local variable and forgets to update the original pointer. The error here is
noticing that realloc can actually move the allocated memory somewhere else
and change the pointer's location. We then leave resizeArray without telling
array->data where the array was moved to.
Invalid write
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
And the code:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* alphabet = calloc(26, sizeof(char));
for(uint8_t i = 0; i < 26; i++) {
*(alphabet + i) = 'A' + i;
}
*(alphabet + 26) = '\0'; //null-terminate the string?
free(alphabet);
return 0;
}
Notice that Valgrind points us to the commented line of code above. The array
of size 26 is indexed [0,25] which is why *(alphabet + 26) is an invalid
write—it's out of bounds. An invalid write is a common result of
off-by-one errors. Look at the left side of your assignment operation.
Invalid read
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
And the code:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* destination = calloc(27, sizeof(char));
char* source = malloc(26 * sizeof(char));
for(uint8_t i = 0; i < 27; i++) {
*(destination + i) = *(source + i); //Look at the last iteration.
}
free(destination);
free(source);
return 0;
}
Valgrind points us to the commented line above. Look at the last iteration here,
which is *(destination + 26) = *(source + 26);. However, *(source + 26) is
out of bounds again, similarly to the invalid write. Invalid reads are also a
common result of off-by-one errors. Look at the right side of your assignment
operation.
The Open Source (U/Dys)topia
How do I know when the leak is mine? How do I find my leak when I'm using
someone else's code? I found a leak that isn't mine; should I do something? All
are legitimate questions. First, 2 real-world examples that show 2 classes of
common encounters.
Jansson: a JSON library
#include <jansson.h>
#include <stdio.h>
int main() {
char* string = "{ \"key\": \"value\" }";
json_error_t error;
json_t* root = json_loads(string, 0, &error); //obtaining a pointer
json_t* value = json_object_get(root, "key"); //obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value)); //use value
json_decref(value); //Do I free this pointer?
json_decref(root); //What about this one? Does the order matter?
return 0;
}
This is a simple program: it reads a JSON string and parses it. In the making,
we use library calls to do the parsing for us. Jansson makes the necessary
allocations dynamically since JSON can contain nested structures of itself.
However, this doesn't mean we decref or "free" the memory given to us from
every function. In fact, this code I wrote above throws both an "Invalid read"
and an "Invalid write". Those errors go away when you take out the decref line
for value.
Why? The variable value is considered a "borrowed reference" in the Jansson
API. Jansson keeps track of its memory for you, and you simply have to decref
JSON structures independent of each other. The lesson here:
read the documentation. Really. It's sometimes hard to understand, but
they're telling you why these things happen. Instead, we have
existing questions about this memory error.
SDL: a graphics and gaming library
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
What's wrong with this code? It consistently leaks ~212 KiB of memory for me. Take a moment to think about it. We turn SDL on and then off. Answer? There is nothing wrong.
That might sound bizarre at first. Truth be told, graphics are messy and sometimes you have to accept some leaks as being part of the standard library. The lesson here: you need not quell every memory leak. Sometimes you just need to suppress the leaks because they're known issues you can't do anything about. (This is not my permission to ignore your own leaks!)
Answers unto the void
How do I know when the leak is mine?
It is. (99% sure, anyway)
How do I find my leak when I'm using someone else's code?
Chances are someone else already found it. Try Google! If that fails, use the skills I gave you above. If that fails and you mostly see API calls and little of your own stack trace, see the next question.
I found a leak that isn't mine; should I do something?
Yes! Most APIs have ways to report bugs and issues. Use them! Help give back to the tools you're using in your project!
Further Reading
Thanks for staying with me this long. I hope you've learned something, as I tried to tend to the broad spectrum of people arriving at this answer. Some things I hope you've asked along the way: How does C's memory allocator work? What actually is a memory leak and a memory error? How are they different from segfaults? How does Valgrind work? If you had any of these, please do feed your curiousity:
More about malloc, C's memory allocator
Definition of a segmentation fault
Definition of a memory leak
Definition of a memory access error
How does Valgrind work?
Try this:
valgrind --leak-check=full -v ./your_program
As long as valgrind is installed it will go through your program and tell you what's wrong. It can give you pointers and approximate places where your leaks may be found. If you're segfault'ing, try running it through gdb.
You can run:
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
You can create an alias in .bashrc file as follows
alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'
So whenever you want to check memory leaks, just do simply
vg ./<name of your executable> <command line parameters to your executable>
This will generate a Valgrind log file in the current directory.