简体   繁体   中英

Trying to use free() to understand how it works

To understand the usage of free in the C programming language I tried running this code on Ubuntu, but on running the EXE file I am receiving a SIGABRT error. Why is the program not exiting normally?

#include<stdio.h>
#include<stdlib.h>

int main()
{
   int ret;
   int *ptr;
   ptr = (int *)malloc(sizeof(int)*10);
   free(ptr);
   ptr = &ret;
   free(ptr);
   return 0;
}

Attempting to free a pointer you didn't get from malloc (or one of its friends) causes undefined behaviour. Your second free(ptr) call attempts just that.

From the C spec, §7.22.3.3 The free function , paragraph 2:

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc , the behavior is undefined.

   ptr = &ret;
   free(ptr);

is the same as:

    free(&ret);

ret resides on the stack, not on the heap. Attempting to free() memory that was not allocated (with malloc() , etc.) on the heap will cause undefined behavior.

While all the the undefined behavior answers are correct, seeing an implementation might be more helpful.

I think a very good reference to this is K&R C malloc . An explanation of how it works is in "The C Programming Langugage" , Chapter 8.7.

Remember when looking at code from standard library functions in C that they have an implementation and a specification, where the implementation might have reproducible behavior that is not required by its specification.

Essentially almost all malloc implementations have a free list where they manage free memory regions in a list (or multiple lists). The free list is often handled in a way, that calling free on a memory region multiple times will put this list in an incorrect state.

Also malicious behavior can be invoked when data structures are purposefully crafted. Phrack has a dated article on how to invoke code execution when passing invalid memory to free while corrupting the freelist.

Other malloc implementations might also be worth looking at (this is an incomplete list of allocation libraries).

The function free() only works for the memory that has been allocated on heap by malloc() . Not for the static allocation, because static allocations are automatically handled. Here is the mistake:

 int ret; 

 ptr = &ret;

 free(ptr);

You can't do this because the memory for ret is not allocated on the heap. It's in the stack and only the memory of the heap should be freed.

Your second free(ptr) is causing undefined behavior as you are attempting to free a pointer which you didn't allocate. Also note that static allocations are automatically reclaimed, so you don't require them to free it.

  ptr = &ret;
   free(ptr);

Here you are trying to free the local storage stack variable's memory. As per rule you should not free it. When the main() exits all local storage memory on stack always get free.

Free only works with Heap allocation memory.

There are three areas where variables may be created in ac or c++ program.

  • global or static variables are at fixed locations in the executable binary file.
  • automatic variables outside of static scope are on the stack
  • malloc'ed or calloc'ed variables are on the heap.

free() is the function that releases previously allocated memory on the heap. A pointer to memory on the heap is returned by malloc or similar functions. The only way to read or write that memory is via the pointer. A pointer is an address, and a pointer* is the content pointed to at that address.

In the example shown, there are two variables, defined in Main, and effectively static, and the returned value from Main, which is on the stack. Using the miniumum int, 16 bits, here's a possible memory map. In this map, the instructions start at 0, the stack starts at some non-zero value, (beginning of stack - bos) and grows by incrementing, and a heap starts at the maximum address (...FFFF, aka -1) and grows by decrememeting:

(Remember, MIN_INT is -32768, MAX_INT is 32767... the spec guarantees only 16 bits, signed)

Each byte has an address which is 'n' bits wide - 16, 32 or 64 bits, typically


-1. (start of heap, for example, 16 bit addr: 0xFFFF, 32 bit addr: 0xFFFFFFFF or 64 bit addr: 0xFFFFFFFFFFFFFFFF)

-2. (1st location down from beginning of heap. 0x...FFFE) ptr[ 9 ], at one time

-3. (2nd location down from beginning of heap. 0x...FFFD)

-4. (3rd location down from beginning of heap. 0x...FFFC) ptr[ 8 ], at one time

[snip]

-17. (16th location down from beginning of heap. 0x...FFEF)

-18. (17th location down from beginning of heap. 0x...FFEE) ptr[ 1 ], at one time

-19. (18th location down from beginning of heap. 0x...FFED)

-20 (19th location down from beginning of heap. 0x...FFEC) ptr[ 0 ], at one time

-21. (top of heap, 10 X 16 bit ints down from beginning of heap. 0x...FFEB), at one time

: A very large range of address on 32 or 64 bit machines... :

tos: ( Top of stack 0x...tos )

bos + ( sizeof( int ) - 1) End of int returned from Main()

bos: (beginning of stack: above static data ) Start of int returned from Mail()

togs: (top of global/static) End of "ptr"

: (size of a pointer is width of address bus... whatever it takes)

togs-(n-1): (top of global/static - (sizeof( int* ) - 1)) start of "ptr"

(togs-n) : End of "ret"

(togs-n)-1: start of "ret"

(any global stuff the compiler adds for itself, the debugger, etc.)

(end of program code)

(start of program code)

(top of non-program code)

0 (start of non-program code, 0x...0000 )


At run-time, "ptr" and "ret" are probably both start at '0', since they're fixed, static, values, read out of the file the executable binary comes from.

As the program runs, the value of "ptr" changes, first, to point into the heap, at the malloc'ed array of 10 ints: "0x...FFEC"

The call to free() doesn't change the value of ptr, its still "0x...FFEC" Free'ing "0x...FFEC" is legit and runs without anything funny.

The assignment "ptr = &ret" sets a new value into "ptr", "(togs-n)-1", the start of "ret".

Free'ing "(togs-n)-1" causes an immediate crash because "free" checks the value of "(togs-n)-1" and it is NOT in the valid range for a heap address.

"ret" remains blank, its never set, but since its global/static, it remains at whatever it was when the linker wrote it to disk.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM