#include <stdlib.h>
#include <errno.h>
#include <libproc.h>
#include <stdio.h>
#include <stdalign.h>
#include <err.h>
#include <sys/sysmacros.h>
#include <sys/mman.h>
#include <sys/debug.h>
#include <stdbool.h>
#include <string.h>
typedef struct {
size_t at_size;
size_t at_align;
int at_errno;
const char *at_desc;
} alloc_test_t;
static const alloc_test_t alloc_tests[] = {
{ 0, sizeof (long), EINVAL, "zero alignment fails with EINVAL" },
{ sizeof (long), 0, EINVAL, "zero size fails with EINVAL" },
{ 128, 3, EINVAL, "3-byte alignment fails with EINVAL" },
{ 128, 7777, EINVAL, "7777-byte alignment fails with EINVAL" },
{ 128, 23, EINVAL, "23-byte alignment fails with EINVAL" },
{ sizeof (char), alignof (char), 0, "alignof (char), 1 byte" },
{ 5, alignof (char), 0, "alignof (char), multiple bytes" },
{ 1, alignof (short), 0, "alignof (short), 1 byte" },
{ 16, alignof (short), 0, "alignof (short), 16 byte" },
{ 1, alignof (int), 0, "alignof (int), 1 byte" },
{ 4, alignof (int), 0, "alignof (int), 4 bytes" },
{ 22, alignof (int), 0, "alignof (int), 22 bytes" },
{ 7, alignof (long long), 0, "alignof (long long), 7 bytes" },
{ 128, alignof (long long), 0, "alignof (long long), 128 bytes" },
{ 511, alignof (long long), 0, "alignof (long long), 511 bytes" },
{ 16, 16, 0, "16-byte alignment), 16 bytes" },
{ 256, 16, 0, "16-byte alignment), 256 bytes" },
{ 256, 4096, 0, "4096-byte alignment), 256 bytes" },
{ 4096, 4096, 0, "4096-byte alignment), 4096 bytes" },
};
const char *
_umem_debug_init(void)
{
return ("default,verbose");
}
const char *
_umem_options_init(void)
{
return ("perthreadcache=0");
}
static bool
alloc_test_one(const alloc_test_t *test)
{
bool ret = false;
void *buf = aligned_alloc(test->at_align, test->at_size);
if (buf == NULL) {
if (test->at_errno == 0) {
warnx("TEST FAILED: %s: allocation failed with %s, but "
"expected success", test->at_desc,
strerrorname_np(errno));
} else if (errno != test->at_errno) {
warnx("TEST FAILED: %s: allocation failed with %s, but "
"expected errno %s", test->at_desc,
strerrorname_np(errno),
strerrorname_np(test->at_errno));
} else {
(void) printf("TEST PASSED: %s\n", test->at_desc);
ret = true;
}
} else if (test->at_errno != 0) {
warnx("TEST FAILED: %s: allocation succeeded, but expected "
"errno %s", test->at_desc, strerrorname_np(test->at_errno));
} else {
(void) printf("TEST PASSED: %s\n", test->at_desc);
ret = true;
}
free(buf);
return (ret);
}
static bool
alloc_test_enomem(const alloc_test_t *test)
{
bool ret = false;
void *buf = aligned_alloc(test->at_align, test->at_size);
if (buf != NULL) {
warnx("TEST FAILED: %s (forced ENOMEM/EAGAIN): succeeded, but "
"expected ENOMEM", test->at_desc);
} else if (errno != ENOMEM && errno != EAGAIN) {
warnx("TEST FAILED: %s (forced ENOMEM/EAGAIN): failed with %s, "
"but expected ENOMEM", test->at_desc,
strerrordesc_np(errno));
} else {
(void) printf("TEST PASSED: %s: ENOMEM/EAGAIN forced\n",
test->at_desc);
ret = true;
}
return (ret);
}
static void
libc_enomem(void)
{
pstatus_t status;
VERIFY0(proc_get_status(getpid(), &status));
VERIFY3P(mmap((caddr_t)P2ROUNDUP(status.pr_brkbase +
status.pr_brksize, 0x1000), 0x1000,
PROT_READ, MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0),
!=, (void *)-1);
for (;;) {
if (malloc(16) == NULL)
break;
}
for (;;) {
if (aligned_alloc(sizeof (void *), 16) == NULL)
break;
}
}
static void
libumem_enomem(void)
{
void (*mtbf)(uint32_t) = dlsym(RTLD_DEFAULT, "umem_setmtbf");
if (mtbf == NULL) {
errx(EXIT_FAILURE, "failed to find umem_setmtbf: %s",
dlerror());
}
mtbf(1);
}
int
main(void)
{
const char *preload;
int ret = EXIT_SUCCESS;
for (size_t i = 0; i < ARRAY_SIZE(alloc_tests); i++) {
if (!alloc_test_one(&alloc_tests[i])) {
ret = EXIT_FAILURE;
}
}
preload = getenv("LD_PRELOAD");
if (preload != NULL) {
if (strstr(preload, "umem") != NULL) {
libumem_enomem();
} else {
warnx("malloc(3C) library %s not supported, skipping "
"ENOMEM tests", preload);
goto skip;
}
} else {
libc_enomem();
}
for (size_t i = 0; i < ARRAY_SIZE(alloc_tests); i++) {
if (alloc_tests[i].at_errno != 0)
continue;
if (!alloc_test_enomem(&alloc_tests[i])) {
ret = EXIT_FAILURE;
}
}
skip:
if (ret == EXIT_SUCCESS) {
(void) printf("All tests passed successfully\n");
}
return (ret);
}