Skip to content

Commit

Permalink
#119 return a status from buddy_safe_free
Browse files Browse the repository at this point in the history
  • Loading branch information
spaskalev committed Sep 22, 2024
1 parent d2f6e82 commit c7e4291
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 47 deletions.
94 changes: 67 additions & 27 deletions buddy_alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,16 @@ void *buddy_reallocarray(struct buddy *buddy, void *ptr,
/* Use the specified buddy to free memory. See free. */
void buddy_free(struct buddy *buddy, void *ptr);

enum buddy_safe_free_status {
BUDDY_SAFE_FREE_SUCCESS,
BUDDY_SAFE_FREE_BUDDY_IS_NULL,
BUDDY_SAFE_FREE_INVALID_ADDRESS,
BUDDY_SAFE_FREE_SIZE_MISMATCH,
BUDDY_SAFE_FREE_ALREADY_FREE,
};

/* A (safer) free with a size. Will not free unless the size fits the target span. */
void buddy_safe_free(struct buddy *buddy, void *ptr, size_t requested_size);
enum buddy_safe_free_status buddy_safe_free(struct buddy *buddy, void *ptr, size_t requested_size);

/*
* Reservation functions
Expand Down Expand Up @@ -321,8 +329,14 @@ static size_t buddy_tree_status(struct buddy_tree *t, struct buddy_tree_pos pos)
/* Marks the indicated position as allocated and propagates the change */
static void buddy_tree_mark(struct buddy_tree *t, struct buddy_tree_pos pos);

enum buddy_tree_release_status {
BUDDY_TREE_RELEASE_SUCCESS,
BUDDY_TREE_RELEASE_FAIL_ALREADY_RELEASED,
BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED,
};

/* Marks the indicated position as free and propagates the change */
static void buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos);
static enum buddy_tree_release_status buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos);

/* Returns a free position at the specified depth or an invalid position */
static struct buddy_tree_pos buddy_tree_find_free(struct buddy_tree *t, uint8_t depth);
Expand Down Expand Up @@ -849,45 +863,57 @@ void buddy_free(struct buddy *buddy, void *ptr) {
buddy_tree_release(tree, pos);
}

void buddy_safe_free(struct buddy *buddy, void *ptr, size_t requested_size) {
unsigned char *dst, *main;
struct buddy_tree *tree;
enum buddy_safe_free_status buddy_safe_free(struct buddy* buddy, void* ptr, size_t requested_size) {
unsigned char* dst, * main;
struct buddy_tree* tree;
struct buddy_tree_pos pos;
size_t allocated_size_for_depth;
enum buddy_tree_release_status status;

if (buddy == NULL) {
return;
return BUDDY_SAFE_FREE_BUDDY_IS_NULL;
}
if (ptr == NULL) {
return;
return BUDDY_SAFE_FREE_INVALID_ADDRESS;
}
dst = (unsigned char *)ptr;
dst = (unsigned char*)ptr;
main = buddy_main(buddy);
if ((dst < main) || (dst >= (main + buddy->memory_size))) {
return;
return BUDDY_SAFE_FREE_INVALID_ADDRESS;
}

/* Find the position tracking this address */
tree = buddy_tree(buddy);
pos = position_for_address(buddy, dst);

if (! buddy_tree_valid(tree, pos)) {
return;
if (!buddy_tree_valid(tree, pos)) {
return BUDDY_SAFE_FREE_INVALID_ADDRESS;
}

allocated_size_for_depth = size_for_depth(buddy, pos.depth);
if (requested_size < buddy->alignment) {
requested_size = buddy->alignment;
}
if (requested_size > allocated_size_for_depth) {
return;
return BUDDY_SAFE_FREE_SIZE_MISMATCH;
}
if (requested_size <= (allocated_size_for_depth / 2)) {
return;
return BUDDY_SAFE_FREE_SIZE_MISMATCH;
}

/* Release the position */
buddy_tree_release(tree, pos);
status = buddy_tree_release(tree, pos);

switch (status) {
case BUDDY_TREE_RELEASE_FAIL_ALREADY_RELEASED:
return BUDDY_SAFE_FREE_ALREADY_FREE;
case BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED:
return BUDDY_SAFE_FREE_INVALID_ADDRESS;
case BUDDY_TREE_RELEASE_SUCCESS:
break;
}

return BUDDY_SAFE_FREE_SUCCESS;
}

void buddy_reserve_range(struct buddy *buddy, void *ptr, size_t requested_size) {
Expand Down Expand Up @@ -1062,7 +1088,6 @@ static unsigned int buddy_relative_mode(struct buddy *buddy) {

static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state) {
size_t delta, memory_size, effective_memory_size;
void (*toggle)(struct buddy_tree *, struct buddy_tree_pos);
struct buddy_tree *tree;
struct buddy_tree_pos pos;

Expand All @@ -1077,16 +1102,18 @@ static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state)
/* Node memory size is already aligned to buddy->alignment */
delta = effective_memory_size - memory_size;

/* Determine whether to mark or release */
toggle = state ? &buddy_tree_mark : &buddy_tree_release;

tree = buddy_tree(buddy);
pos = buddy_tree_right_child(buddy_tree_root());
while (delta) {
size_t current_pos_size = size_for_depth(buddy, buddy_tree_depth(pos));
if (delta == current_pos_size) {
/* toggle current pos */
(*toggle)(tree, pos);
if (state) {
buddy_tree_mark(tree, pos);
}
else {
buddy_tree_release(tree, pos);
}
break;
}
if (delta <= (current_pos_size / 2)) {
Expand All @@ -1095,7 +1122,12 @@ static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state)
continue;
} else {
/* toggle right child */
(*toggle)(tree, buddy_tree_right_child(pos));
if (state) {
buddy_tree_mark(tree, buddy_tree_right_child(pos));
}
else {
buddy_tree_release(tree, buddy_tree_right_child(pos));
}
/* reduce delta */
delta -= current_pos_size / 2;
/* re-run for left child */
Expand All @@ -1107,7 +1139,6 @@ static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state)

static void buddy_toggle_range_reservation(struct buddy *buddy, void *ptr, size_t requested_size, unsigned int state) {
unsigned char *dst, *main;
void (*toggle)(struct buddy_tree *, struct buddy_tree_pos);
struct buddy_tree *tree;
size_t offset;
struct buddy_tree_pos pos;
Expand All @@ -1127,17 +1158,19 @@ static void buddy_toggle_range_reservation(struct buddy *buddy, void *ptr, size_
return;
}

/* Determine whether to mark or release */
toggle = state ? &buddy_tree_mark : &buddy_tree_release;

/* Find the deepest position tracking this address */
tree = buddy_tree(buddy);
offset = (size_t) (dst - main);
pos = deepest_position_for_offset(buddy, offset);

/* Advance one position at a time and process */
while (requested_size) {
(*toggle)(tree, pos);
if (state) {
buddy_tree_mark(tree, pos);
}
else {
buddy_tree_release(tree, pos);
}
requested_size = (requested_size < buddy->alignment) ? 0 : (requested_size - buddy->alignment);
pos.index++;
}
Expand Down Expand Up @@ -1597,19 +1630,26 @@ static void buddy_tree_mark(struct buddy_tree *t, struct buddy_tree_pos pos) {
update_parent_chain(t, pos, internal, internal.local_offset);
}

static void buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos) {
static enum buddy_tree_release_status buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos) {
/* Calling release on an unused or a partially-used position a bug in caller */
struct internal_position internal = buddy_tree_internal_position_tree(t, pos);
size_t value = read_from_internal_position(buddy_tree_bits(t), internal);

if (!value) {
return BUDDY_TREE_RELEASE_FAIL_ALREADY_RELEASED;
}

if (read_from_internal_position(buddy_tree_bits(t), internal) != internal.local_offset) {
return;
return BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED;
}

/* Mark the node as unused */
write_to_internal_position(buddy_tree_bits(t), internal, 0);

/* Update the tree upwards */
update_parent_chain(t, pos, internal, 0);

return BUDDY_TREE_RELEASE_SUCCESS;
}

static void update_parent_chain(struct buddy_tree *t, struct buddy_tree_pos pos,
Expand Down
38 changes: 18 additions & 20 deletions tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -883,11 +883,10 @@ void test_buddy_safe_free_coverage(void) {
start_test;
buddy = buddy_init(buddy_buf, data_buf+1024, 1024);
assert(buddy != NULL);
buddy_safe_free(NULL, NULL, 0);
buddy_safe_free(NULL, data_buf+1024, 0);
buddy_safe_free(buddy, NULL, 0);
buddy_safe_free(buddy, data_buf, 0);
buddy_safe_free(buddy, data_buf+2048, 0);
assert(buddy_safe_free(NULL, NULL, 0) == BUDDY_SAFE_FREE_BUDDY_IS_NULL);
assert(buddy_safe_free(buddy, NULL, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
assert(buddy_safe_free(buddy, data_buf, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
assert(buddy_safe_free(buddy, data_buf+2048, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
free(buddy_buf);
}

Expand All @@ -897,7 +896,7 @@ void test_buddy_safe_free_alignment(void) {
struct buddy *buddy;
start_test;
buddy = buddy_init(buddy_buf, data_buf, 4096);
buddy_safe_free(buddy, data_buf+1, 0);
assert(buddy_safe_free(buddy, data_buf+1, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
free(buddy_buf);
}

Expand All @@ -915,11 +914,11 @@ void test_buddy_safe_free_invalid_free_01(void) {
r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN);
assert(r != NULL);
assert(l != r);
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// double-free on a right node
// that will propagate to a partially-allocated parent
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -939,12 +938,12 @@ void test_buddy_safe_free_invalid_free_02(void) {
r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN);
assert(r != NULL);
assert(l != r);
buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN);
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// double-free on a right node
// that will propagate to a unallocated parent
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -964,14 +963,14 @@ void test_buddy_safe_free_invalid_free_03(void) {
r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN);
assert(r != NULL);
assert(l != r);
buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN);
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
m = buddy_malloc(buddy, size); // full allocation
assert(m == l); // at the start of the arena
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// double-free on a right node
// that will propagate to a fully-allocated parent
buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -991,12 +990,11 @@ void test_buddy_safe_free_invalid_free_04(void) {
r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN);
assert(r != NULL);
assert(l != r);
buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// double-free on a left node
// that will propagate to a partially-allocated parent
buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN);
/* the use check in buddy_tree_release handles this case */
assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SIZE_MISMATCH);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -1014,7 +1012,7 @@ void test_buddy_safe_free_invalid_free_05(void) {
l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// safe free with double size
buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN * 2);
assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN * 2) == BUDDY_SAFE_FREE_SIZE_MISMATCH);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -1032,7 +1030,7 @@ void test_buddy_safe_free_invalid_free_06(void) {
m = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN*2);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// safe free with half size
buddy_safe_free(buddy, m, BUDDY_ALLOC_ALIGN);
assert(buddy_safe_free(buddy, m, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SIZE_MISMATCH);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand All @@ -1050,7 +1048,7 @@ void test_buddy_safe_free_invalid_free_07(void) {
m = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN*2);
memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size));
// safe free with zero size
buddy_safe_free(buddy, m, 0);
assert(buddy_safe_free(buddy, m, 0) == BUDDY_SAFE_FREE_SIZE_MISMATCH);
assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0);
free(buddy_buf);
free(buddy_buf_control);
Expand Down

0 comments on commit c7e4291

Please sign in to comment.