I was trying to replace the standard library functions malloc and free with my own versions to detect memory leak. I did this through dynamic linking:
// malloc.c
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef __GLIBC__
#error glibc must be used.
#endif
extern void *__libc_malloc(size_t size);
extern void *__libc_free(void *ptr);
void *malloc(size_t size) {
const char msg[] = "malloc called.\n";
void *ptr = __libc_malloc(size);
if (ptr != NULL)
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
return ptr;
}
void free(void *ptr) {
const char msg[] = "free called.\n";
if (ptr != NULL)
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
__libc_free(ptr);
}
Compiled with
gcc -shared malloc.c -o libmalloc.so -fpic
Then I wrote another C program:
// hello.c
#include <stdio.h>
#include <stdlib.h>
int compare_less(const void *a_, const void *b_) {
const int a = *(const int *)a_, b = *(const int *)b_;
return a < b ? -1 : (a == b ? 0 : 1);
}
int main() {
int n;
scanf("%d", &n);
int *a = malloc(sizeof(int) * n);
for (int i = 0; i != n; ++i) {
scanf("%d", a + i);
}
qsort(a, n, sizeof(int), &compare_less);
for (int i = 0; i < n - 1; ++i)
printf("%d, ", a[i]);
printf("%d\n", a[n - 1]);
free(a);
return 0;
}
and link them by
gcc hello.c -o hello -L. -lmalloc
When I run ./hello, I found that malloc called. was printed three times: during the first call to scanf, during the direct call to malloc, and during the second call to scanf. However free called. was only printed once.
I added a breakpoint at malloc and GDB backtrace shows the following:
(gdb) bt
#0 malloc (size=1024) at malloc.c:12
#1 0x00007ffff7df2c24 in __GI__IO_file_doallocate (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/filedoalloc.c:101
#2 0x00007ffff7e01d60 in __GI__IO_doallocbuf (fp=fp#entry=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/libioP.h:947
#3 0x00007ffff7e00d5c in _IO_new_file_underflow (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/fileops.c:485
#4 0x00007ffff7e01e16 in __GI__IO_default_uflow (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/libioP.h:947
#5 0x00007ffff7dd7150 in __vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr#entry=0x7fffffffdfb0, mode_flags=mode_flags#entry=2)
at ./stdio-common/vfscanf-internal.c:628
#6 0x00007ffff7dd61c2 in __isoc99_scanf (format=<optimized out>) at ./stdio-common/isoc99_scanf.c:30
#7 0x000055555555525e in main () at hello.c:11
It seems that scanf calls malloc to allocate memory for its buffer, but I did not see scanf calling free. However, valgrind shows that all heap blocks were freed, so that memory must have been freed somehow.
My questions:
How is the memory allocated by scanf deallocated?
If I want to record the calls to malloc and free from users' code only (i.e. ignoring those from standard libraries like scanf), what should I do?
Related
I'm trying to wrap the C library functions malloc and free to detect whether there is memory leakage in my code. I extend the malloc/free functions by adding to them an fprintf to write to a file the address of the malloc/free and the size.
Compiling this code with gcc or clang gives a segmentation fault in the fopen() line.
Here is the command:
gcc -o mainapp main.c -Wall -Wextra
I placed the fopen inside the malloc and free function but also get the same issue: Segmentation fault (core dumped)
I can't find an explanation for the issue.
Here is my complete code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define __USE_GNU
#include <dlfcn.h>
#define TEST_MEM_LEAK 1 // a value of 1 means to join the memory leak detection, and a value of 0 means not to join
#if TEST_MEM_LEAK
typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;
typedef void (*free_t)(void *p);
free_t free_f = NULL;
int malloc_flag = 1; // It is used to prevent repeated recursion and cannot exit because the printf function will call malloc for memory allocation
int free_flag = 1;
const char* logFileName = "/home/hammamiw/Documents/HeapMonitor/allocs.log";
FILE* fp = NULL;
void initCheck()
{
fp = fopen("/home/hammamiw/Documents/HeapMonitor/allocs.log", "w");
}
void *malloc(size_t size)
{
if(malloc_flag) {
initCheck();
malloc_flag = 0; // Used to prevent printf from causing an error when calling malloc recursively
void *p = malloc_f(size);
fprintf(fp, "malloc, %lx, %lu\n", (uintptr_t)p, size);
//printf("m\n");
malloc_flag = 1; // It is used to ensure that the initial value of flag flag is consistent when malloc in this file is called again
return p;
}
else {
return malloc_f(size); // Here, the malloc function in the system library obtained by dlsym is called
}
}
void free(void *p)
{
initCheck();
if(free_flag) {
//initCheck();
free_flag = 0;
fprintf(fp, "F, %lx\n", (uintptr_t)p);
//printf("f\n");
free_f(p);
free_flag = 1;
} else {
free_f(p);
}
}
#endif
int main()
{
#if TEST_MEM_LEAK // the part from if to endif can be divided into function calls
malloc_f = dlsym(RTLD_NEXT, "malloc");
if(!malloc_f) {
printf("load malloc failed: %s\n", dlerror());
return 1;
}
free_f = dlsym(RTLD_NEXT, "free");
if(!free_f) {
printf("load free failed: %s\n", dlerror());
return 1;
}
#endif
void *p1 = malloc(10); //The malloc function in this article will be called first
void *p2 = malloc(20);
//Here, p2 is not released and there is a memory leak. Judge by checking whether the number of malloc and free times printed is the same
free(p2);
free(p1);
return 0;
}
NOTE: the code works well if I use printf instead of fprintf -> prints "f" and "m" at each free and malloc call.
Environment: Ubuntu 22.04, C language, GCC compiler version 11.3.0
You need to set malloc_flag before calling fopen(). Then you will protect against recursion not only via fprintf() but also via fopen().
I have a simple program let's call it a victim:
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
int *a = malloc(200);
printf("%p\n", a);
}
And I want to inject a shared object into this program that will log all the calls to the malloc function. So it will works like a memory logger:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
void *
malloc(size_t n)
{
static void * (*real_malloc)(size_t) = NULL;
if (real_malloc == NULL) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
if (real_malloc == NULL) {
#if 0
printf("MEMORY FILTER: can't find the real malloc() function: %s\n", dlerror());
#endif
exit(EXIT_FAILURE);
}
}
void *result = real_malloc(n);
#if 0
if (result != NULL)
printf("MEMORY FILTER: allocated %zu bytes at address %p.\n", n, result);
else
printf("MEMORY FILTER: failed to allocate %zu bytes.\n", n);
#endif
return result;
}
And then I run the victim as follows:
$ LD_LIBRARY_PATH=. LD_PRELOAD=libmemlog.so ./victim
0x55e0747792a0
It works fine but when I uncomment #if 0 ... #endif blocks when the printf function is called then I get a segmentation fault. This is happens because printf calls malloc inside itself and we got an endless recursion which eventually crushes.
How can I get logging work in this case?
This question already has an answer here:
Dynamic memory access only works inside function
(1 answer)
Closed 2 years ago.
I came across the problem where a dynamic array of structures is properly initialized and filled with data in a function from an included file, but an attempt to access the content of the array causes segmentation faults in the main function even though the pointer was defined globally. Please have a look at the example below:
my_struct.h
typedef struct my_struct {
int one;
int two;
} my_struct_t;
void update_my_struct(my_struct_t*, int);
my_struct.c
#include <stdlib.h>
#include <stdio.h>
#include "my_struct.h"
void
update_my_struct(my_struct_t *my_s, int num)
{
int i;
for (i = 0; i < num; i++ ) {
my_s = realloc(my_s, sizeof(my_struct_t)*(i+1));
my_s[i].one = 1*i;
my_s[i].two = 2*i;
}
printf(" my_s[0] one: %d two: %d\n", my_s[0].one, my_s[0].two);
}
main.c
#include <stdlib.h>
#include <stdio.h>
#include "my_struct.h"
my_struct_t *my_structs;
void
main(void)
{
int i;
update_my_struct(my_structs, 4);
for (i = 0; i < 4; i++)
printf(" %d. one = %d, two = %d\n", i, my_structs[i].one, my_structs[i].two);
free(my_structs);
}
Compile and run:
$ gcc main.c my_struct.c
$ ./a.out
my_s[0] one: 0 two: 0
Segmentation fault (core dumped)
I checked with gdb and the seg fault occurs in main, so it puzzles me how come the dynamic array can be accessed in the included function, but in the main function even though the pointer was declared in main.c. I will appreciate some helpful hints and comments on this issue.
update
Following up on EML's answer I changed the code of the update function to:
void
update_my_struct(my_struct_t *my_s, int num)
{
int i;
for (i = 0; i < num; i++ ) {
if (my_s == NULL)
my_s = (my_struct_t *) malloc(sizeof(my_struct_t));
else
my_s = (my_struct_t *) realloc(my_s, sizeof(my_struct_t)*(i+1));
my_s[i].one = 1*i;
my_s[i].two = 2*i;
}
printf(" my_s[3] one: %d two: %d\n", my_s[3].one, my_s[3].two);
}
Seg fault still occurs:
$ ./a.out
my_s[3] one: 3 two: 6
Segmentation fault (core dumped)
Valgrind did not provide me with any insightful information, here is the snippet of its output:
==5301== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==5301== Command: ./a.out
==5301==
my_s[3] one: 3 two: 6
==5301== Invalid read of size 4
==5301== at 0x40118F: main (in a.out)
==5301== Address 0x4 is not stack'd, malloc'd or (recently) free'd
==5301==
==5301==
==5301== Process terminating with default action of signal 11 (SIGSEGV): dumping core
...
Here is what I implied in my comments. This assumes *my_s is initially NULL, so realloc in that case will act like malloc.
#include <stdlib.h>
#include <stdio.h>
#include "my_struct.h"
void
update_my_struct(my_struct_t **my_s, int num)
{
int i;
*my_s = (my_struct_t *)realloc(*my_s, sizeof(my_struct_t)*num);
for (i = 0; i < num; i++ ) {
(*my_s)[i].one = 1*i;
(*my_s)[i].two = 2*i;
}
printf(" (*my_s)[0] one: %d two: %d\n", (*my_s)[0].one, (*my_s)[0].two);
}
You can then call it like this.
my_struct_t *my_structs = NULL;
...
update_my_struct(&my_structs, 4);
This will work fine for one initialization and the number of structs allocated is kept by the caller. A second call will reallocate to the new number of structs, and reinitialize everything from the beginning.
I omitted realloc return value check for brevity.
And here is the full code as tested on godbolt.
#include <stdlib.h>
#include <stdio.h>
typedef struct my_struct {
int one;
int two;
} my_struct_t;
void
update_my_struct(my_struct_t **my_s, int num)
{
int i;
*my_s = (my_struct_t *)realloc(*my_s, sizeof(my_struct_t)*num);
for (i = 0; i < num; i++ ) {
(*my_s)[i].one = 1*i;
(*my_s)[i].two = 2*i;
}
}
my_struct_t *my_structs = NULL;
int main ()
{
int i;
update_my_struct(&my_structs, 4);
for (int i=0; i<4; i++)
printf(" (*my_s)[%d] one: %d two: %d\n", i, my_structs[i].one, my_structs[i].two);
return 0;
}
Check your realloc docs: the memory must have been previously allocated with with malloc/etc, or the results are undefined.
Your code doesn't really work either - the realloc is inside your for loop, etc.
And try running valgrind - it would have found this problem.
I tried to use inline assembly to call the function with three arguments, but it fails with Segmentation fault.
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>
void callee_test3(char* dest, char* src, size_t srclen)
{
printf("%s %s %li\n", dest, src, srclen);
};
int main()
{
char* dest = "test";
char* src = "src";
size_t srclen = 4;
asm(
"movq %[arg1], %%rdi\n\t"
"movq %[arg2], %%rsi\n\t"
"movq %[arg3], %%rdx\n\t"
"call *%[callee]"
:
:[arg1]"r"((u_int64_t)(dest)),
[arg2]"r"((u_int64_t)(src)),
[arg3]"r"((u_int64_t)(srclen)),
[callee]"r"(callee_test3)
:"cc"
);
return 0;
}
I can call a function with two arguments, but when I added into three arguments, it just failed. Tried to use gdb to trace where the code break it shows :
Breakpoint 1, main () at test.c:24
24 char* dest = "test";
(gdb) next
25 char* src = "src";
(gdb)
26 size_t srclen = 4;
(gdb)
34 :[arg1]"r"((u_int64_t)(dest)),
(gdb)
35 [arg2]"r"((u_int64_t)(src)),
(gdb)
28 asm(
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554872 in ?? ()
For some reasons, the third argument didn't store into the register. My assumption is I'm calling the wrong register to store the third argument. But I couldn't find the resource about that.
In the given example below I try to set the stacksize to 1kb.
Why is it now possible to allocate an array of ints on the stack with size 8kb in foo() ?
#include <stdio.h>
#include <sys/resource.h>
void foo(void);
int main() {
struct rlimit lim = {1024, 1024};
if (setrlimit(RLIMIT_STACK, &lim) == -1)
return 1;
foo();
return 0;
}
void foo() {
unsigned ints[2048];
printf("foo: %u\n", ints[2047]=42);
}
The limit is set immediately but only checked when trying to allocate a new stack or trying to grow the existing stack. A grep for RLIMIT_STACK (or a LXR identifier search) on the kernel sources should tell.
Apparently, the initial size of the stack is whatever is needed to the filename + env strings + arg strings plus some extra pages allocated on setup_arg_pages (20 pages in 2.6.33 1,2, 128 Kb on 2.6.34 3).
In summary:
initial stack size = MIN(size for filename + arg strings + env strings + extra pages, MAX(size for filename + arg strings + env strings, RLIMIT_STACK))
where
size for filename + arg strings + env strings <= MAX(ARG_MAX(32 pages), RLIMIT_STACK/4)
Additionally, kernels with Ingo Molnar's exec-shield patch (Fedora, Ubuntu, ...) have an additional EXEC_STACK_BIAS "(2MB more to cover randomization effects.)", see the call to the new function over_stack_limit() from acct_stack_growth() ([Ubuntu1], [Ubuntu2], [Ubuntu3]).
I've edited the original program to show this:
#include <stdio.h>
#include <sys/resource.h>
void foo(void);
int main(int argc, char *argv[]) {
struct rlimit lim = {1, 1};
if (argc > 1 && argv[1][0] == '-' && argv[1][8]=='l') {
printf("limiting stack size\n");
if (setrlimit(RLIMIT_STACK, &lim) == -1) {
printf("rlimit failed\n");
return 1;
}
}
foo();
return 0;
}
void foo() {
unsigned ints[32768];
printf("foo: %u\n", ints[2047]=42);
}
Which results in:
$./rl
foo: 42
$./rl -l
limiting stack size
Segmentation fault
$
I think setrlimit moves the "resource pointers" but doesn't apply the new limits until you exec a new copy of the program.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
void foo(int chk) {
unsigned ints[2048];
ints[2047] = 42;
printf("foo %d: %u\n", chk, ints[2047]);
}
int main(int argc, char **argv) {
char *newarg[] = { "argv[0]", "one", "two" };
char *newenv[] = { NULL };
struct rlimit lim;
newarg[0] = argv[0];
getrlimit(RLIMIT_STACK, &lim);
printf("lim: %d / %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
switch (argc) {
case 1: /* first call from command line */
lim.rlim_cur = 65536;
lim.rlim_max = 65536;
if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
newarg[2] = NULL;
foo(1);
execve(argv[0], newarg, newenv);
break;
case 2: /* second call */
lim.rlim_cur = 1024;
lim.rlim_max = 1024;
if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
foo(2);
execve(argv[0], newarg, newenv);
break;
default: /* third call */
foo(3);
break;
}
return 0;
}
And a test run:
$ ./a.out
lim: 8388608 / -1
foo 1: 42
lim: 65536 / 65536
foo 2: 42
Killed
Why the process gets killed before printing the limits (and before calling foo), I don't know.