'How to detect if a block of memory already freed
I already know that there is no way to know if a pointer target still a valid allocation of it's already freed, so I'm trying to use pointer to pointer to solve this but it didn't work.
My objective is simply making print_block()
detect if block
pointer is Null or not.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void free_block(u_int8_t **_block) {
// Dereference
u_int8_t *block = *_block;
// Info
printf("free_block()\t-> %p Point To %p \n", &block, block);
// Free Block
free(block);
block = NULL;
}
void print_block(u_int8_t **_block) {
// Dereference
u_int8_t *block = *_block;
// Detectc if this block is freed
// This is the objective of this code
if(block == NULL) {
printf("print_block()\t-> %p Is Null.\n", block);
return;
}
// Info
printf("print_block()\t-> %p Point To %p -> ", &block, block);
// Print byte by byte
u_int8_t *p = block;
for(int i = 0; i < 3; i++) {
printf("0x%02X ", *(u_int8_t *)p);
p++;
}
printf("\n");
}
int main(void) {
// Allocat a block in the memory
u_int8_t *block = malloc(3 * sizeof(u_int8_t));
// Set all to zeros
memset(block, 0x00, 3);
// Info
printf("Main()\t\t\t-> %p Point To %p \n", &block, block);
// Print the block content
print_block(&block);
// Free the block
free_block(&block);
// Print the block content gain
// This shold print Null because
// we freed the block.
print_block(&block);
return 0;
}
Result
Main() -> 0x7fffd549cc58 Point To 0xfa42a0
print_block() -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0x00 0x00 0x00
free_block() -> 0x7fffd549cc60 Point To 0xfa42a0
print_block() -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0xA4 0x0F 0x00
Solution 1:[1]
First thing, you need to know that free(p)
flag the block of memory to be available for any new allocation, nothing else; and the pointer p
it self still a Valid pointer, and you can Read and Write from it.
About your question "How to detect if a block of memory is already freed?" the short answer in C is there is no way. To solve this you definitively need a pointer tracker, which is not hard to do, this is an example:
void *ptr_list[64];
int ptr_position = 0;
bool ptr_exist(void *p) {
if(p == NULL)
return false;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == p)
return true;
}
return false;
}
void ptr_add(void *p) {
if(p == NULL)
return;
if(!ptr_exist(p)) {
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == NULL) {
ptr_list[i] = p;
return;
}
}
ptr_list[ptr_position] = p;
ptr_position++;
}
}
void ptr_free(void **p) {
if(*p == NULL)
return;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == *p) {
ptr_list[i] = NULL;
}
}
for(int i = ptr_position; i >= 0; i--) {
if(ptr_list[i] == NULL) {
ptr_position = i;
break;
}
}
free(*p);
*p = NULL;
}
To use it simply after you allocate a block of memory add your pointer to the tracker using ptr_add()
, and when you want to free it use ptr_free()
. Finally you can check at any time from any thread if this block of memory still Valid using ptr_exist()
.
Result and the full code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// -- Pointers Tracker --
void *ptr_list[64];
int ptr_position = 0;
bool ptr_exist(void *p) {
if(p == NULL)
return false;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == p)
return true;
}
return false;
}
void ptr_add(void *p) {
if(p == NULL)
return;
if(!ptr_exist(p)) {
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == NULL) {
ptr_list[i] = p;
return;
}
}
ptr_list[ptr_position] = p;
ptr_position++;
}
}
void ptr_free(void **p) {
if(*p == NULL)
return;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == *p) {
ptr_list[i] = NULL;
}
}
for(int i = ptr_position; i >= 0; i--) {
if(ptr_list[i] == NULL) {
ptr_position = i;
break;
}
}
free(*p);
*p = NULL;
}
// ----------------------
void free_block(u_int8_t *block) {
// Info
printf("free_block()\t-> %p Point To %p \n", &block, block);
// Free Block
// free(block);
// block = NULL;
ptr_free((void *)&block);
}
void print_block(u_int8_t *block) {
// Detectc if this block is freed
// This is the objective of this code
if(!ptr_exist(block)) {
printf("print_block()\t-> %p Is Null.\n", block);
return;
}
// Info
printf("print_block()\t-> %p Point To %p -> ", &block, block);
// Print byte by byte
u_int8_t *p = block;
for(int i = 0; i < 3; i++) {
printf("0x%02X ", *(u_int8_t *)p);
p++;
}
printf("\n");
}
int main(void) {
// Allocat a block in the memory
u_int8_t *block = malloc(3 * sizeof(u_int8_t));
// Add it to the tracker
ptr_add((void *)block);
// Set all to zeros
memset(block, 0x00, 3);
// Info
printf("Main()\t\t\t-> %p Point To %p \n", &block, block);
// Print the block content
print_block(block);
// Free the block
free_block(block);
// Print the block content gain
// This shold print Null because
// we freed the block.
print_block(block);
return 0;
}
Solution 2:[2]
You can use a Block
struct
and a bit of discipline.
As an example
typedef struct
{
size_t size;
uint8_t fill;
uint8_t* data;
} Block;
Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void print_block(Block* block, const char* title);
and
- make the functions operate on pointers to
Block
, encapsulating the data - make
free_block()
returnNULL
to ease the task of invalidating the block pointer - test the block address inside
print_block()
- use an optional title in
print_block()
main as an example
int main(void)
{
const int test_size = 32;
Block* block = get_block(test_size, 0); // zero-filled
printf(
"Main()\t\t\t-> Pointer at %p points to data at "
"%p\n",
&block, block->data);
print_block(block, "\nBlock contents:");
block = free_block(block);
print_block(block, "\nafter free()");
return 0;
}
Maybe you find this way easier to read.
output of main as above
Main() -> Pointer at 00CFF754 points to data at 00E96C70
Block contents
print_block()-> valid block data of size 32 at 00E96C70
fill char is 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
after free()
print_block() -> block is null.
complete code
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
size_t size;
uint8_t fill;
uint8_t* data;
} Block;
Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void print_block(Block* block, const char* title);
int main(void)
{
const int test_size = 32;
Block* block = get_block(test_size, 0); // 3 bytes, zero filled
printf(
"Main()\t\t\t-> Pointer at %p points to data at "
"%p\n",
&block, block->data);
print_block(block, "\nBlock contents:");
block = free_block(block);
print_block(block, "\nafter free()");
return 0;
}
Block* free_block(Block* block)
{
if (block == NULL) return NULL;
if (block->data == NULL) return NULL;
free(block->data); // data is free
free(block);
return NULL;
}
Block* get_block(size_t size, uint8_t fill)
{
Block* block = malloc(sizeof(Block));
if (block == NULL) return NULL;
block->data = malloc(size * sizeof(uint8_t));
if (block->data == NULL) return NULL;
memset(block->data, fill, size);
block->fill = fill;
block->size = size;
return block;
}
void print_block(Block* block, char* title)
{
if (title != NULL) printf("%s\n", title);
if (block == NULL)
{
printf("print_block()\t-> block is null.\n");
return;
}
printf(
"print_block()->\tvalid block data of size %d at "
"%p\n\t\tfill char is %02X\n\n",
block->size, block->data, block->fill);
// Print byte by byte
const int nc = 16; // 16 bytes per line
size_t j = 1;
for (size_t i = 0; i < block->size; i += 1)
{
printf("%02X ", block->data[i]);
if (j >= nc)
printf("\n"), j = 1;
else
j += 1;
}
if ( j != 1 ) printf("\n");
return;
}
Solution 3:[3]
There is no standard way to tell if a pointer is valid, nor if it points to an allocated object nor if it was freed already.
Yet it is possible to design a framework on top of the allocation functions to keep track of all allocations and deallocations and maintain a list of valid pointers. Looking up a pointer value in this list will tell you if the pointer points to a valid allocated block and possibly what size was requested from the heap.
Here is a simplistic implementation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void track_add_pointer(void *p, size_t size);
int track_find_pointer(void *p, size_t *sizep);
void track_remove_pointer(void *p);
void *tmalloc(size_t size) {
void *p = (malloc)(size);
if (p) {
track_add_pointer(p, size);
}
return p;
}
void *tcalloc(size_t size, size_t nmemb) {
void *p = (calloc)(size, nmemb);
if (p) {
track_add_pointer(p, size * nmemb);
}
return p;
}
void *trealloc(void *p, size_t size) {
if (p) {
void *newp = NULL;
if (!track_find_pointer(p, NULL)) {
fprintf(stderr, "trealloc: invalid pointer %p\n", p);
} else {
if (size == 0) {
(free)(p);
} else {
newp = (realloc)(p, size);
}
if (newp != p) {
if (p) track_remove_pointer(p);
if (newp) track_add_pointer(newp);
}
}
return newp;
} else {
return tmalloc(size);
}
}
void tfree(void *p) {
if (p) {
if (!track_find_pointer(p, NULL)) {
fprintf(stderr, "tfree: invalid pointer %p\n", p);
} else {
(free)(p);
track_remove_pointer(p);
}
}
}
char *tstrdup(const char *s) {
char *p = NULL;
if (s) {
size_t len = strlen(s);
p = tmalloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
} else {
fprintf(stderr, "tstrdup: null pointer\n");
}
return p;
}
char *tstrndup(const char *s, size_t n) {
char *p = NULL;
if (s) {
size_t len = strnlen(s, n);
p = tmalloc(len + 1);
if (p) {
memcpy(p, s, len);
p[len] = '\0';
}
} else {
fprintf(stderr, "tstrndup: null pointer\n");
}
return p;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Hassan DRAGA |
Solution 2 | arfneto |
Solution 3 |