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
|
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
#include "tests.h"
#include "alloc.h"
#include "bit.h"
#include "diag.h"
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
/** Check for_arena() iteration. */
static void check_for_arena(void) {
// Check all 2^bits patterns of allocated/free objects. Every 2 bits of
// the pattern corresponds to a different chunk type:
//
// 0b00: 000...000
// 0b01: 100...000
// 0b10: 011...111
// 0b11: 111...111
const size_t bits = 8;
const size_t patterns = 1 << bits;
const size_t chunk = SIZE_WIDTH;
const size_t count = chunk * bits;
int **ptrs = ALLOC_ARRAY(int *, count);
bfs_everify(ptrs);
struct arena arena;
ARENA_INIT(&arena, int);
for (size_t mask = 0; mask < patterns; ++mask) {
arena_clear(&arena);
// Allocate count objects
for (size_t i = 0; i < count; ++i) {
ptrs[i] = arena_alloc(&arena);
bfs_everify(ptrs[i]);
*ptrs[i] = i;
}
// Create holes according to the mask
size_t remaining = count;
for (size_t bit = 0; bit < bits; bit += 2) {
size_t start = chunk * bit / 2;
size_t end = start + chunk;
for (size_t i = start; i < end; ++i) {
bool keep = (mask >> bit) & (i == start ? 0x1 : 0x2);
if (!keep) {
arena_free(&arena, ptrs[i]);
ptrs[i] = NULL;
--remaining;
}
}
}
// Check the remaining objects
for_arena (int, p, &arena) {
bfs_check(ptrs[*p] == p);
--remaining;
}
bfs_check(remaining == 0);
}
arena_destroy(&arena);
free(ptrs);
}
struct flexible {
alignas(64) int foo[8];
int bar[];
};
/** Check varena_realloc() poisoning for a size combination. */
static struct flexible *check_varena_realloc(struct varena *varena, struct flexible *flexy, size_t old_count, size_t new_count) {
flexy = varena_realloc(varena, flexy, new_count);
bfs_everify(flexy);
for (size_t i = 0; i < new_count; ++i) {
if (i < old_count) {
bfs_check(flexy->bar[i] == (int)i);
} else {
flexy->bar[i] = i;
}
}
return flexy;
}
void check_alloc(void) {
// Check aligned allocation
void *ptr;
bfs_everify((ptr = zalloc(64, 129)));
bfs_check((uintptr_t)ptr % 64 == 0);
bfs_echeck((ptr = xrealloc(ptr, 64, 129, 65)));
bfs_check((uintptr_t)ptr % 64 == 0);
free(ptr);
// Check sizeof_flex()
bfs_check(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible));
bfs_check(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0);
// volatile to suppress -Walloc-size-larger-than
volatile size_t too_many = SIZE_MAX / sizeof(int) + 1;
bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX));
// Make sure we detect allocation size overflows
bfs_check(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
bfs_check(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
bfs_check(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW);
bfs_check(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW);
// arena tests
check_for_arena();
// varena tests
struct varena varena;
VARENA_INIT(&varena, struct flexible, bar);
for (size_t i = 0; i < 256; ++i) {
bfs_everify(varena_alloc(&varena, i));
struct arena *arena = &varena.arenas[varena.narenas - 1];
bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i));
}
// Check varena_realloc() (un)poisoning
struct flexible *flexy = varena_alloc(&varena, 160);
bfs_everify(flexy);
flexy = check_varena_realloc(&varena, flexy, 0, 160);
flexy = check_varena_realloc(&varena, flexy, 160, 192);
flexy = check_varena_realloc(&varena, flexy, 192, 160);
flexy = check_varena_realloc(&varena, flexy, 160, 320);
flexy = check_varena_realloc(&varena, flexy, 320, 96);
varena_destroy(&varena);
}
|