xref: /illumos-gate/usr/src/test/libc-tests/tests/aligned_alloc.c (revision 3448eae2e9d99e7fadeb43056eb8e788c7ea93be)
1fc2512cfSRobert Mustacchi /*
2fc2512cfSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3fc2512cfSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4fc2512cfSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5fc2512cfSRobert Mustacchi  * 1.0 of the CDDL.
6fc2512cfSRobert Mustacchi  *
7fc2512cfSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8fc2512cfSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9fc2512cfSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10fc2512cfSRobert Mustacchi  */
11fc2512cfSRobert Mustacchi 
12fc2512cfSRobert Mustacchi /*
13fc2512cfSRobert Mustacchi  * Copyright 2016 Joyent, Inc.
14*3448eae2SRobert Mustacchi  * Copyright 2024 Oxide Computer Company
15fc2512cfSRobert Mustacchi  */
16fc2512cfSRobert Mustacchi 
17fc2512cfSRobert Mustacchi /*
18fc2512cfSRobert Mustacchi  * Basic tests for aligned_alloc(3C). Note that we test ENOMEM failure by
19fc2512cfSRobert Mustacchi  * relying on the implementation of the current libc malloc. Specifically we go
20fc2512cfSRobert Mustacchi  * through and add a mapping so we can't expand the heap and then use it up. If
21fc2512cfSRobert Mustacchi  * the memory allocator is ever changed, this test will start failing, at which
22fc2512cfSRobert Mustacchi  * point, it may not be worth the cost of keeping it around.
23fc2512cfSRobert Mustacchi  */
24fc2512cfSRobert Mustacchi 
25fc2512cfSRobert Mustacchi #include <stdlib.h>
26fc2512cfSRobert Mustacchi #include <errno.h>
27fc2512cfSRobert Mustacchi #include <libproc.h>
28*3448eae2SRobert Mustacchi #include <stdio.h>
29*3448eae2SRobert Mustacchi #include <stdalign.h>
30*3448eae2SRobert Mustacchi #include <err.h>
31fc2512cfSRobert Mustacchi #include <sys/sysmacros.h>
32fc2512cfSRobert Mustacchi #include <sys/mman.h>
33fc2512cfSRobert Mustacchi #include <sys/debug.h>
34*3448eae2SRobert Mustacchi #include <stdbool.h>
35*3448eae2SRobert Mustacchi #include <string.h>
36fc2512cfSRobert Mustacchi 
37*3448eae2SRobert Mustacchi typedef struct {
38*3448eae2SRobert Mustacchi 	size_t at_size;
39*3448eae2SRobert Mustacchi 	size_t at_align;
40*3448eae2SRobert Mustacchi 	int at_errno;
41*3448eae2SRobert Mustacchi 	const char *at_desc;
42*3448eae2SRobert Mustacchi } alloc_test_t;
43*3448eae2SRobert Mustacchi 
44*3448eae2SRobert Mustacchi static const alloc_test_t alloc_tests[] = {
45*3448eae2SRobert Mustacchi 	{ 0, sizeof (long), EINVAL, "zero alignment fails with EINVAL" },
46*3448eae2SRobert Mustacchi 	{ sizeof (long), 0, EINVAL, "zero size fails with EINVAL" },
47*3448eae2SRobert Mustacchi 	{ 128, 3, EINVAL, "3-byte alignment fails with EINVAL" },
48*3448eae2SRobert Mustacchi 	{ 128, 7777, EINVAL, "7777-byte alignment fails with EINVAL" },
49*3448eae2SRobert Mustacchi 	{ 128, 23, EINVAL, "23-byte alignment fails with EINVAL" },
50*3448eae2SRobert Mustacchi 	{ sizeof (char), alignof (char), 0, "alignof (char), 1 byte" },
51*3448eae2SRobert Mustacchi 	{ 5, alignof (char), 0, "alignof (char), multiple bytes" },
52*3448eae2SRobert Mustacchi 	{ 1, alignof (short), 0, "alignof (short), 1 byte" },
53*3448eae2SRobert Mustacchi 	{ 16, alignof (short), 0, "alignof (short), 16 byte" },
54*3448eae2SRobert Mustacchi 	{ 1, alignof (int), 0, "alignof (int), 1 byte" },
55*3448eae2SRobert Mustacchi 	{ 4, alignof (int), 0, "alignof (int), 4 bytes" },
56*3448eae2SRobert Mustacchi 	{ 22, alignof (int), 0, "alignof (int), 22 bytes" },
57*3448eae2SRobert Mustacchi 	/* We skip long here because it varies between ILP32/LP64 */
58*3448eae2SRobert Mustacchi 	{ 7, alignof (long long), 0, "alignof (long long), 7 bytes" },
59*3448eae2SRobert Mustacchi 	{ 128, alignof (long long), 0, "alignof (long long), 128 bytes" },
60*3448eae2SRobert Mustacchi 	{ 511, alignof (long long), 0, "alignof (long long), 511 bytes" },
61*3448eae2SRobert Mustacchi 	{ 16, 16, 0, "16-byte alignment), 16 bytes" },
62*3448eae2SRobert Mustacchi 	{ 256, 16, 0, "16-byte alignment), 256 bytes" },
63*3448eae2SRobert Mustacchi 	{ 256, 4096, 0, "4096-byte alignment), 256 bytes" },
64*3448eae2SRobert Mustacchi 	{ 4096, 4096, 0, "4096-byte alignment), 4096 bytes" },
65*3448eae2SRobert Mustacchi };
66*3448eae2SRobert Mustacchi 
67*3448eae2SRobert Mustacchi /*
68*3448eae2SRobert Mustacchi  * Disable the per-thread caches and enable debugging if launched with umem.
69*3448eae2SRobert Mustacchi  */
70*3448eae2SRobert Mustacchi const char *
_umem_debug_init(void)71*3448eae2SRobert Mustacchi _umem_debug_init(void)
72*3448eae2SRobert Mustacchi {
73*3448eae2SRobert Mustacchi 	return ("default,verbose");
74*3448eae2SRobert Mustacchi }
75*3448eae2SRobert Mustacchi 
76*3448eae2SRobert Mustacchi const char *
_umem_options_init(void)77*3448eae2SRobert Mustacchi _umem_options_init(void)
78*3448eae2SRobert Mustacchi {
79*3448eae2SRobert Mustacchi 	return ("perthreadcache=0");
80*3448eae2SRobert Mustacchi }
81*3448eae2SRobert Mustacchi 
82*3448eae2SRobert Mustacchi static bool
alloc_test_one(const alloc_test_t * test)83*3448eae2SRobert Mustacchi alloc_test_one(const alloc_test_t *test)
84*3448eae2SRobert Mustacchi {
85*3448eae2SRobert Mustacchi 	bool ret = false;
86*3448eae2SRobert Mustacchi 	void *buf = aligned_alloc(test->at_align, test->at_size);
87*3448eae2SRobert Mustacchi 
88*3448eae2SRobert Mustacchi 	if (buf == NULL) {
89*3448eae2SRobert Mustacchi 		if (test->at_errno == 0) {
90*3448eae2SRobert Mustacchi 			warnx("TEST FAILED: %s: allocation failed with %s, but "
91*3448eae2SRobert Mustacchi 			    "expected success", test->at_desc,
92*3448eae2SRobert Mustacchi 			    strerrorname_np(errno));
93*3448eae2SRobert Mustacchi 		} else if (errno != test->at_errno) {
94*3448eae2SRobert Mustacchi 			warnx("TEST FAILED: %s: allocation failed with %s, but "
95*3448eae2SRobert Mustacchi 			    "expected errno %s", test->at_desc,
96*3448eae2SRobert Mustacchi 			    strerrorname_np(errno),
97*3448eae2SRobert Mustacchi 			    strerrorname_np(test->at_errno));
98*3448eae2SRobert Mustacchi 		} else {
99*3448eae2SRobert Mustacchi 			(void) printf("TEST PASSED: %s\n", test->at_desc);
100*3448eae2SRobert Mustacchi 			ret = true;
101*3448eae2SRobert Mustacchi 		}
102*3448eae2SRobert Mustacchi 	} else if (test->at_errno != 0) {
103*3448eae2SRobert Mustacchi 		warnx("TEST FAILED: %s: allocation succeeded, but expected "
104*3448eae2SRobert Mustacchi 		    "errno %s", test->at_desc, strerrorname_np(test->at_errno));
105*3448eae2SRobert Mustacchi 	} else {
106*3448eae2SRobert Mustacchi 		(void) printf("TEST PASSED: %s\n", test->at_desc);
107*3448eae2SRobert Mustacchi 		ret = true;
108*3448eae2SRobert Mustacchi 	}
109*3448eae2SRobert Mustacchi 
110*3448eae2SRobert Mustacchi 	free(buf);
111*3448eae2SRobert Mustacchi 	return (ret);
112*3448eae2SRobert Mustacchi }
113*3448eae2SRobert Mustacchi 
114*3448eae2SRobert Mustacchi static bool
alloc_test_enomem(const alloc_test_t * test)115*3448eae2SRobert Mustacchi alloc_test_enomem(const alloc_test_t *test)
116*3448eae2SRobert Mustacchi {
117*3448eae2SRobert Mustacchi 	bool ret = false;
118*3448eae2SRobert Mustacchi 	void *buf = aligned_alloc(test->at_align, test->at_size);
119*3448eae2SRobert Mustacchi 
120*3448eae2SRobert Mustacchi 	if (buf != NULL) {
121*3448eae2SRobert Mustacchi 		warnx("TEST FAILED: %s (forced ENOMEM/EAGAIN): succeeded, but "
122*3448eae2SRobert Mustacchi 		    "expected ENOMEM", test->at_desc);
123*3448eae2SRobert Mustacchi 	} else if (errno != ENOMEM && errno != EAGAIN) {
124*3448eae2SRobert Mustacchi 		warnx("TEST FAILED: %s (forced ENOMEM/EAGAIN): failed with %s, "
125*3448eae2SRobert Mustacchi 		    "but expected ENOMEM", test->at_desc,
126*3448eae2SRobert Mustacchi 		    strerrordesc_np(errno));
127*3448eae2SRobert Mustacchi 	} else {
128*3448eae2SRobert Mustacchi 		(void) printf("TEST PASSED: %s: ENOMEM/EAGAIN forced\n",
129*3448eae2SRobert Mustacchi 		    test->at_desc);
130*3448eae2SRobert Mustacchi 		ret = true;
131*3448eae2SRobert Mustacchi 	}
132*3448eae2SRobert Mustacchi 
133*3448eae2SRobert Mustacchi 	return (ret);
134*3448eae2SRobert Mustacchi }
135*3448eae2SRobert Mustacchi 
136*3448eae2SRobert Mustacchi static void
libc_enomem(void)137*3448eae2SRobert Mustacchi libc_enomem(void)
138fc2512cfSRobert Mustacchi {
139fc2512cfSRobert Mustacchi 	pstatus_t status;
140fc2512cfSRobert Mustacchi 
141fc2512cfSRobert Mustacchi 	VERIFY0(proc_get_status(getpid(), &status));
142fc2512cfSRobert Mustacchi 	VERIFY3P(mmap((caddr_t)P2ROUNDUP(status.pr_brkbase +
143fc2512cfSRobert Mustacchi 	    status.pr_brksize, 0x1000), 0x1000,
144fc2512cfSRobert Mustacchi 	    PROT_READ, MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0),
145fc2512cfSRobert Mustacchi 	    !=, (void *)-1);
146fc2512cfSRobert Mustacchi 
147fc2512cfSRobert Mustacchi 	for (;;) {
148fc2512cfSRobert Mustacchi 		if (malloc(16) == NULL)
149fc2512cfSRobert Mustacchi 			break;
150fc2512cfSRobert Mustacchi 	}
151fc2512cfSRobert Mustacchi 
152fc2512cfSRobert Mustacchi 	for (;;) {
153fc2512cfSRobert Mustacchi 		if (aligned_alloc(sizeof (void *), 16) == NULL)
154fc2512cfSRobert Mustacchi 			break;
155fc2512cfSRobert Mustacchi 	}
156*3448eae2SRobert Mustacchi }
157fc2512cfSRobert Mustacchi 
158*3448eae2SRobert Mustacchi /*
159*3448eae2SRobert Mustacchi  * Because this test is leveraging LD_PRELOAD in the test runner to test
160*3448eae2SRobert Mustacchi  * different malloc libraries, we can't call umem_setmtbf() directly. Instead we
161*3448eae2SRobert Mustacchi  * ask rtld to find it for us, which is a bit gross, but works.
162*3448eae2SRobert Mustacchi  */
163*3448eae2SRobert Mustacchi static void
libumem_enomem(void)164*3448eae2SRobert Mustacchi libumem_enomem(void)
165*3448eae2SRobert Mustacchi {
166*3448eae2SRobert Mustacchi 	void (*mtbf)(uint32_t) = dlsym(RTLD_DEFAULT, "umem_setmtbf");
167*3448eae2SRobert Mustacchi 	if (mtbf == NULL) {
168*3448eae2SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to find umem_setmtbf: %s",
169*3448eae2SRobert Mustacchi 		    dlerror());
170*3448eae2SRobert Mustacchi 	}
171fc2512cfSRobert Mustacchi 
172*3448eae2SRobert Mustacchi 	mtbf(1);
173*3448eae2SRobert Mustacchi }
174*3448eae2SRobert Mustacchi 
175*3448eae2SRobert Mustacchi int
main(void)176*3448eae2SRobert Mustacchi main(void)
177*3448eae2SRobert Mustacchi {
178*3448eae2SRobert Mustacchi 	const char *preload;
179*3448eae2SRobert Mustacchi 	int ret = EXIT_SUCCESS;
180*3448eae2SRobert Mustacchi 
181*3448eae2SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(alloc_tests); i++) {
182*3448eae2SRobert Mustacchi 		if (!alloc_test_one(&alloc_tests[i])) {
183*3448eae2SRobert Mustacchi 			ret = EXIT_FAILURE;
184*3448eae2SRobert Mustacchi 		}
185*3448eae2SRobert Mustacchi 	}
186*3448eae2SRobert Mustacchi 
187*3448eae2SRobert Mustacchi 	/*
188*3448eae2SRobert Mustacchi 	 * To catch failure tests, we need to know what memory allocator we're
189*3448eae2SRobert Mustacchi 	 * using which we expect to be indicated via an LD_PRELOAD environment
190*3448eae2SRobert Mustacchi 	 * variable. If it's not set, assume libc.
191*3448eae2SRobert Mustacchi 	 */
192*3448eae2SRobert Mustacchi 	preload = getenv("LD_PRELOAD");
193*3448eae2SRobert Mustacchi 	if (preload != NULL) {
194*3448eae2SRobert Mustacchi 		if (strstr(preload, "umem") != NULL) {
195*3448eae2SRobert Mustacchi 			libumem_enomem();
196*3448eae2SRobert Mustacchi 		} else {
197*3448eae2SRobert Mustacchi 			warnx("malloc(3C) library %s not supported, skipping "
198*3448eae2SRobert Mustacchi 			    "ENOMEM tests", preload);
199*3448eae2SRobert Mustacchi 			goto skip;
200*3448eae2SRobert Mustacchi 		}
201*3448eae2SRobert Mustacchi 	} else {
202*3448eae2SRobert Mustacchi 		libc_enomem();
203*3448eae2SRobert Mustacchi 	}
204*3448eae2SRobert Mustacchi 
205*3448eae2SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(alloc_tests); i++) {
206*3448eae2SRobert Mustacchi 		if (alloc_tests[i].at_errno != 0)
207*3448eae2SRobert Mustacchi 			continue;
208*3448eae2SRobert Mustacchi 		if (!alloc_test_enomem(&alloc_tests[i])) {
209*3448eae2SRobert Mustacchi 			ret = EXIT_FAILURE;
210*3448eae2SRobert Mustacchi 		}
211*3448eae2SRobert Mustacchi 	}
212*3448eae2SRobert Mustacchi 
213*3448eae2SRobert Mustacchi skip:
214*3448eae2SRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
215*3448eae2SRobert Mustacchi 		(void) printf("All tests passed successfully\n");
216*3448eae2SRobert Mustacchi 	}
217*3448eae2SRobert Mustacchi 
218*3448eae2SRobert Mustacchi 	return (ret);
219fc2512cfSRobert Mustacchi }
220