File size: 4,777 Bytes
4f0bd4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * memory_allocator.c — Simple bump allocator
 *
 * A small self-contained example for GRACE evaluation (Phase 4).
 *
 * Intentional bug:
 *   alloc_free() does not check whether `ptr` is NULL before dereferencing
 *   it to set the freed flag.  Passing NULL causes a null-pointer dereference.
 */

#include <stddef.h>
#include <stdint.h>
#include <string.h>

/* ------------------------------------------------------------------ */
/* Data structures                                                      */
/* ------------------------------------------------------------------ */

#define ALLOC_ALIGN 8   /* alignment in bytes */

/* Header prepended to each allocation */
typedef struct alloc_header {
    size_t size;         /* allocation size (excluding header) */
    int    freed;        /* 1 if this block has been freed     */
} alloc_header_t;

typedef struct {
    uint8_t *pool;       /* backing memory pool                */
    size_t   pool_size;  /* total pool size in bytes           */
    size_t   used;       /* bytes already allocated (incl. headers) */
    int      initialized;
} bump_allocator_t;

/* Error codes */
#define ALLOC_OK         0
#define ALLOC_ERR_NULL  -1
#define ALLOC_ERR_OOM   -2
#define ALLOC_ERR_INIT  -3

/* ------------------------------------------------------------------ */
/* Lifecycle                                                           */
/* ------------------------------------------------------------------ */

/*
 * alloc_init — initialise the bump allocator with a backing pool.
 *
 * Precondition:  alloc != NULL, pool != NULL, size >= sizeof(alloc_header_t)
 * Postcondition: alloc->initialized == 1, alloc->used == 0
 */
int alloc_init(bump_allocator_t *alloc, uint8_t *pool, size_t size)
{
    if (alloc == NULL || pool == NULL)
        return ALLOC_ERR_NULL;
    if (size < sizeof(alloc_header_t))
        return ALLOC_ERR_OOM;

    alloc->pool        = pool;
    alloc->pool_size   = size;
    alloc->used        = 0;
    alloc->initialized = 1;
    return ALLOC_OK;
}

/*
 * alloc_reset — reset the allocator, freeing all allocations.
 *
 * Precondition:  alloc != NULL, alloc->initialized == 1
 * Postcondition: alloc->used == 0
 */
int alloc_reset(bump_allocator_t *alloc)
{
    if (alloc == NULL)
        return ALLOC_ERR_NULL;
    if (!alloc->initialized)
        return ALLOC_ERR_INIT;

    alloc->used = 0;
    return ALLOC_OK;
}

/*
 * alloc_available — return the number of bytes still available.
 *
 * Precondition:  alloc != NULL, alloc->initialized == 1
 * Postcondition: return value == alloc->pool_size - alloc->used
 */
size_t alloc_available(const bump_allocator_t *alloc)
{
    if (alloc == NULL || !alloc->initialized)
        return 0;
    if (alloc->used >= alloc->pool_size)
        return 0;
    return alloc->pool_size - alloc->used;
}

/* ------------------------------------------------------------------ */
/* Allocation / deallocation                                           */
/* ------------------------------------------------------------------ */

/*
 * alloc_malloc — allocate `size` bytes, returning a pointer.
 *
 * Precondition:  alloc != NULL, alloc->initialized == 1, size > 0,
 *                alloc_available(alloc) >= sizeof(alloc_header_t) + size
 * Postcondition: returned pointer is non-NULL and within the pool,
 *                alloc->used increases by (sizeof(alloc_header_t) + aligned_size)
 */
void *alloc_malloc(bump_allocator_t *alloc, size_t size)
{
    if (alloc == NULL || !alloc->initialized)
        return NULL;
    if (size == 0)
        return NULL;

    /* Align size to ALLOC_ALIGN */
    size_t aligned = (size + ALLOC_ALIGN - 1) & ~(size_t)(ALLOC_ALIGN - 1);
    size_t total   = sizeof(alloc_header_t) + aligned;

    if (alloc->used + total > alloc->pool_size)
        return NULL;   /* out of memory */

    alloc_header_t *hdr = (alloc_header_t *)(alloc->pool + alloc->used);
    hdr->size  = aligned;
    hdr->freed = 0;
    alloc->used += total;

    return (void *)(hdr + 1);
}

/*
 * alloc_free — mark an allocated block as freed.
 *
 * Note: bump allocators do not reclaim memory; this just records the block
 * as freed for accounting purposes.
 *
 * Precondition:  ptr != NULL (i.e. a valid pointer returned by alloc_malloc)
 * Postcondition: the header preceding ptr has freed == 1
 *
 * BUG: missing NULL check before dereferencing ptr.
 *      If ptr == NULL, subtracting sizeof(alloc_header_t) gives an
 *      invalid address and the subsequent write is a null-pointer dereference.
 */
int alloc_free(void *ptr)
{
    /* BUG: no NULL check — null-pointer dereference when ptr == NULL */
    alloc_header_t *hdr = (alloc_header_t *)ptr - 1;   /* <-- intentional bug */
    hdr->freed = 1;
    return ALLOC_OK;
}