Skip to content

Commit 58b6394

Browse files
committed
refactor standard_allocator and fix realloc overflow bugs
1 parent 6839b26 commit 58b6394

1 file changed

Lines changed: 79 additions & 78 deletions

File tree

Lines changed: 79 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,124 @@
11
#include <stddef.h>
2-
#include <stdlib.h>
32
#include <stdint.h>
3+
#include <stdlib.h>
44
#include <string.h>
55

6-
typedef struct __attribute__((packed)) block
7-
{
8-
struct block *ptr;
9-
size_t size;
6+
typedef struct __attribute__((packed)) block {
7+
struct block *next;
8+
size_t total_size;
9+
uint8_t data[];
1010
} __attribute__((packed)) block_t;
1111

1212
extern uint8_t __heap_low[];
1313
extern uint8_t __heap_high[];
1414
static uintptr_t heap_ptr = (uintptr_t)__heap_low;
15-
static block_t _alloc_base;
15+
// heap_base is the only node where total_size = 0
16+
static block_t heap_base = { .next = NULL, .total_size = 0 };
1617

18+
#define BLOCK_HEADER_SIZE offsetof(block_t, data)
19+
20+
// malloc(0) returns NULL
1721
void *malloc(size_t alloc_size)
1822
{
19-
block_t *q;
20-
block_t *r;
21-
22-
/* add size of block header to real size */
23-
const size_t size = alloc_size + sizeof(block_t);
24-
/* abort if alloc_size is 0 or size overflowed */
25-
if (size <= alloc_size)
26-
{
23+
const size_t block_size = alloc_size + BLOCK_HEADER_SIZE;
24+
if (block_size <= BLOCK_HEADER_SIZE) {
25+
// reject if alloc_size is 0 or size overflowed
2726
return NULL;
2827
}
2928

30-
for (block_t *p = &_alloc_base; (q = p->ptr); p = q)
31-
{
32-
if (q->size >= size)
33-
{
34-
if (q->size <= size + sizeof(block_t))
35-
{
36-
p->ptr = q->ptr;
37-
}
38-
else
39-
{
40-
q->size -= size;
41-
q = (block_t*)(((uint8_t*)q) + q->size);
42-
q->size = size;
43-
}
29+
// search through the free-list for an open block (sorted by address)
30+
block_t *previous_block = &heap_base;
31+
while (previous_block->next != NULL) {
32+
block_t *current_block = previous_block->next;
4433

45-
return q + 1;
34+
if (current_block->total_size >= block_size) {
35+
if (current_block->total_size - BLOCK_HEADER_SIZE <= block_size) {
36+
// region is too small to split into two parts, so just claim the whole region
37+
previous_block->next = current_block->next;
38+
return current_block->data;
39+
}
40+
// split from the high end so the original free-list node stays valid
41+
current_block->total_size -= block_size;
42+
current_block = (block_t*)(((uint8_t*)current_block) + current_block->total_size);
43+
current_block->total_size = block_size;
44+
return current_block->data;
4645
}
46+
47+
previous_block = previous_block->next;
4748
}
4849

49-
/* compute next heap pointer */
50-
if (heap_ptr + size < heap_ptr || heap_ptr + size >= (uintptr_t)__heap_high)
51-
{
50+
// no suitable free block exists, extend into fresh heap space
51+
size_t heap_available = (uintptr_t)__heap_high - (uintptr_t)heap_ptr;
52+
if (block_size > heap_available) {
5253
return NULL;
5354
}
5455

55-
r = (block_t*)heap_ptr;
56-
r->size = size;
56+
block_t *new_block = (block_t*)heap_ptr;
57+
new_block->total_size = block_size;
58+
heap_ptr = heap_ptr + block_size;
5759

58-
heap_ptr = heap_ptr + size;
59-
60-
return r + 1;
60+
return new_block->data;
6161
}
6262

6363
void free(void *ptr)
6464
{
65-
if (ptr != NULL)
66-
{
67-
block_t *p;
68-
block_t *q;
69-
70-
q = (block_t*)ptr - 1;
65+
if (ptr == NULL) {
66+
return;
67+
}
7168

72-
for (p = &_alloc_base; p->ptr && p->ptr < q; p = p->ptr);
69+
block_t *previous_block = &heap_base;
70+
block_t *current_block = (block_t*)((uint8_t*)ptr - BLOCK_HEADER_SIZE);
71+
while (
72+
previous_block->next != NULL &&
73+
(uintptr_t)previous_block->next < (uintptr_t)current_block
74+
) {
75+
previous_block = previous_block->next;
76+
}
7377

74-
if (p->ptr && (uint8_t*)p->ptr == ((uint8_t*)q) + q->size)
75-
{
76-
q->size += p->ptr->size;
77-
q->ptr = p->ptr->ptr;
78-
}
79-
else
80-
{
81-
q->ptr = p->ptr;
82-
}
78+
// merge with the following free block if it is directly adjacent
79+
if (
80+
previous_block->next != NULL &&
81+
(uint8_t*)previous_block->next == ((uint8_t*)current_block) + current_block->total_size
82+
) {
83+
current_block->total_size += previous_block->next->total_size;
84+
current_block->next = previous_block->next->next;
85+
} else {
86+
current_block->next = previous_block->next;
87+
}
8388

84-
if (p->size && ((uint8_t*)p) + p->size == (uint8_t*)q)
85-
{
86-
p->size += q->size;
87-
p->ptr = q->ptr;
88-
}
89-
else
90-
{
91-
p->ptr = q;
92-
}
89+
// merge with the preceding free block, unless that predecessor is heap_base
90+
if (
91+
previous_block->total_size != 0 &&
92+
((uint8_t*)previous_block) + previous_block->total_size == (uint8_t*)current_block
93+
) {
94+
previous_block->total_size += current_block->total_size;
95+
previous_block->next = current_block->next;
96+
} else {
97+
previous_block->next = current_block;
9398
}
9499
}
95100

96-
void *realloc(void *ptr, size_t size)
101+
// realloc(ptr, 0) returns ptr and does nothing
102+
void *realloc(void *ptr, size_t alloc_size)
97103
{
98-
block_t *h;
99-
void *p;
100-
101-
if (ptr == NULL)
102-
{
103-
return malloc(size);
104+
if (ptr == NULL) {
105+
return malloc(alloc_size);
104106
}
105107

106-
h = (block_t*)ptr - 1;
107-
108-
if (h->size >= size + sizeof(block_t))
109-
{
108+
block_t *header = (block_t*)((uint8_t*)ptr - BLOCK_HEADER_SIZE);
109+
if (header->total_size - BLOCK_HEADER_SIZE >= alloc_size) {
110+
// realloc(ptr, 0) is undefined in C23 and returns here
110111
return ptr;
111112
}
112113

113-
p = malloc(size);
114-
if (p == NULL)
115-
{
114+
// increase allocation size
115+
void *new_ptr = malloc(alloc_size);
116+
if (new_ptr == NULL) {
116117
return NULL;
117118
}
118119

119-
memcpy(p, ptr, h->size - sizeof(block_t));
120+
memcpy(new_ptr, ptr, header->total_size - BLOCK_HEADER_SIZE);
120121
free(ptr);
121122

122-
return p;
123+
return new_ptr;
123124
}

0 commit comments

Comments
 (0)