mirror of
https://github.com/openbsd/src.git
synced 2025-01-10 06:47:55 -08:00
Add a regress test to test various malloc API and heap mismanagement
errors which should cause abort. A few are not enabled yet, they will be once the corresponding diffs in malloc are committed.
This commit is contained in:
parent
4b9d1c52db
commit
83821b1ea0
5
regress/lib/libc/malloc/malloc_errs/Makefile
Normal file
5
regress/lib/libc/malloc/malloc_errs/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# $OpenBSD: Makefile,v 1.1 2023/05/08 11:12:44 otto Exp $
|
||||
|
||||
PROG= malloc_errs
|
||||
|
||||
.include <bsd.regress.mk>
|
286
regress/lib/libc/malloc/malloc_errs/malloc_errs.c
Normal file
286
regress/lib/libc/malloc/malloc_errs/malloc_errs.c
Normal file
@ -0,0 +1,286 @@
|
||||
/* $OpenBSD: malloc_errs.c,v 1.1 2023/05/08 11:12:44 otto Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2023 Otto Moerbeek <otto@drijf.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Test erroneous use of API and heap that malloc should catch */
|
||||
|
||||
void
|
||||
clearq(void *p)
|
||||
{
|
||||
int i;
|
||||
void *q;
|
||||
|
||||
/* Clear delayed free queue */
|
||||
for (i = 0; i < 400; i++) {
|
||||
q = malloc(100);
|
||||
free(q);
|
||||
if (p == q) {
|
||||
fprintf(stderr, "Re-use\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* test the test setup */
|
||||
void
|
||||
t0(void)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
/* double free >= page size */
|
||||
void
|
||||
t1(void)
|
||||
{
|
||||
void *p = malloc(10000);
|
||||
free(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
/* double free chunks are different, have a delayed free list */
|
||||
void
|
||||
t2(void)
|
||||
{
|
||||
void *p, *q;
|
||||
int i;
|
||||
|
||||
p = malloc(100);
|
||||
free(p);
|
||||
clearq(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
/* double free without clearing delayed free list, needs F */
|
||||
void
|
||||
t3(void)
|
||||
{
|
||||
void *p = malloc(100);
|
||||
free(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
/* free without prior allocation */
|
||||
void
|
||||
t4(void)
|
||||
{
|
||||
free((void*)1);
|
||||
}
|
||||
|
||||
/* realloc of bogus pointer */
|
||||
void
|
||||
t5(void)
|
||||
{
|
||||
realloc((void*)1, 10);
|
||||
}
|
||||
|
||||
/* write after free for chunk */
|
||||
void
|
||||
t6(void)
|
||||
{
|
||||
char *p = malloc(32);
|
||||
free(p);
|
||||
p[0] = ~p[0];
|
||||
clearq(NULL);
|
||||
}
|
||||
|
||||
/* write after free large alloction */
|
||||
void
|
||||
t7(void)
|
||||
{
|
||||
char *p, *q;
|
||||
int i;
|
||||
|
||||
p = malloc(10000);
|
||||
free(p);
|
||||
p[0] = ~p[0];
|
||||
/* force re-use from the cache */
|
||||
for (i = 0; i < 100; i++) {
|
||||
q = malloc(10000);
|
||||
free(q);
|
||||
}
|
||||
}
|
||||
|
||||
/* write after free for chunk, no clearing of delayed free queue */
|
||||
void
|
||||
t8(void)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
p = malloc(32);
|
||||
q = malloc(32);
|
||||
free(p);
|
||||
p[0] = ~p[0];
|
||||
free(q);
|
||||
}
|
||||
|
||||
/* canary check */
|
||||
void
|
||||
t9(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = malloc(100);
|
||||
p[100] = 0;
|
||||
free(p);
|
||||
}
|
||||
|
||||
/* t10 is the same as t9 with different flags */
|
||||
|
||||
/* modified chunk pointer */
|
||||
void
|
||||
t11(void)
|
||||
{
|
||||
char *p = malloc(100);
|
||||
free(p + 1);
|
||||
}
|
||||
|
||||
/* free chunk pointer */
|
||||
void
|
||||
t12(void)
|
||||
{
|
||||
char *p = malloc(16);
|
||||
free(p + 16);
|
||||
}
|
||||
|
||||
/* freezero with wrong size */
|
||||
void
|
||||
t13(void)
|
||||
{
|
||||
char *p = malloc(16);
|
||||
freezero(p, 17);
|
||||
}
|
||||
|
||||
/* freezero with wrong size 2 */
|
||||
void
|
||||
t14(void)
|
||||
{
|
||||
char *p = malloc(15);
|
||||
freezero(p, 16);
|
||||
}
|
||||
|
||||
/* freezero with wrong size, pages */
|
||||
void
|
||||
t15(void)
|
||||
{
|
||||
char *p = malloc(getpagesize());
|
||||
freezero(p, getpagesize() + 1);
|
||||
}
|
||||
|
||||
/* recallocarray with wrong size */
|
||||
void
|
||||
t16(void)
|
||||
{
|
||||
abort(); /* not yet */
|
||||
char *p = recallocarray(NULL, 0, 16, 1);
|
||||
char *q = recallocarray(p, 2, 3, 16);
|
||||
}
|
||||
|
||||
/* recallocarray with wrong size 2 */
|
||||
void
|
||||
t17(void)
|
||||
{
|
||||
char *p = recallocarray(NULL, 0, 15, 1);
|
||||
char *q = recallocarray(p, 2, 3, 15);
|
||||
}
|
||||
|
||||
/* recallocarray with wrong size, pages */
|
||||
void
|
||||
t18(void)
|
||||
{
|
||||
abort(); /* not yet */
|
||||
char *p = recallocarray(NULL, 0, 1, getpagesize());
|
||||
char *q = recallocarray(p, 2, 3, getpagesize());
|
||||
}
|
||||
|
||||
struct test {
|
||||
void (*test)(void);
|
||||
const char *flags;
|
||||
};
|
||||
|
||||
struct test tests[] = {
|
||||
{ t0, "" },
|
||||
{ t1, "" },
|
||||
{ t2, "" },
|
||||
{ t3, "F" },
|
||||
{ t4, "" },
|
||||
{ t5, "" },
|
||||
{ t6, "J" },
|
||||
{ t7, "JJ" },
|
||||
{ t8, "FJ" },
|
||||
{ t9, "C" },
|
||||
{ t9, "JC" }, /* t10 re-uses code from t9 */
|
||||
{ t11, "" },
|
||||
{ t12, "" },
|
||||
{ t13, "" },
|
||||
{ t14, "C" },
|
||||
{ t15, "" },
|
||||
{ t16, "" },
|
||||
{ t17, "C" },
|
||||
{ t18, "" },
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
const struct rlimit lim = {0, 0};
|
||||
int i, status;
|
||||
pid_t pid;
|
||||
char num[10];
|
||||
char options[10];
|
||||
extern char* malloc_options;
|
||||
|
||||
if (argc == 3) {
|
||||
malloc_options = argv[2];
|
||||
/* prevent coredumps */
|
||||
setrlimit(RLIMIT_CORE, &lim);
|
||||
i = atoi(argv[1]);
|
||||
fprintf(stderr, "Test %d\n", i);
|
||||
(*tests[i].test)();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
snprintf(options, sizeof(options), "cfjgu%s", tests[i].flags);
|
||||
snprintf(num, sizeof(num), "%d", i);
|
||||
execl(argv[0], argv[0], num, options, NULL);
|
||||
err(1, "exec");
|
||||
break;
|
||||
case -1:
|
||||
err(1, "fork");
|
||||
break;
|
||||
default:
|
||||
if (waitpid(pid, &status, 0) == -1)
|
||||
err(1, "wait");
|
||||
if (!WIFSIGNALED(status) ||
|
||||
WTERMSIG(status) != SIGABRT)
|
||||
errx(1, "Test %d did not abort", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user