summaryrefslogtreecommitdiffstats
path: root/tests/alloc.c
blob: defdac2e3cef0d23c60f1fde59c9a24116a552be (plain)
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);
}