232 lines
4.7 KiB
C
232 lines
4.7 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
* Copyright (C) 2005-2006 IBM Corporation.
|
|
* Author: David Gibson & Adam Litke
|
|
*/
|
|
|
|
/*\
|
|
* [Description]
|
|
*
|
|
* This test uses mprotect to change protection of hugepage mapping and
|
|
* perform read/write operation. It checks if the operation results in
|
|
* expected behaviour as per the protection.
|
|
*/
|
|
#include <setjmp.h>
|
|
#include "hugetlb.h"
|
|
|
|
#define MNTPOINT "hugetlbfs/"
|
|
#define RANDOM_CONSTANT 0x1234ABCD
|
|
|
|
static int fd = -1;
|
|
static sigjmp_buf sig_escape;
|
|
static void *sig_expected = MAP_FAILED;
|
|
static long hpage_size;
|
|
static void *addr;
|
|
|
|
static struct tcase {
|
|
char *tname;
|
|
unsigned long len1;
|
|
int prot1;
|
|
char *prot1_str;
|
|
unsigned long len2;
|
|
int prot2;
|
|
char *prot2_str;
|
|
} tcases[] = {
|
|
{"R->RW", 1, PROT_READ, "PROT_READ",
|
|
1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
|
|
|
|
{"RW->R", 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
|
|
1, PROT_READ, "PROT_READ"},
|
|
|
|
{"R->RW 1/2", 2, PROT_READ, "PROT_READ",
|
|
1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
|
|
|
|
{"RW->R 1/2", 2, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
|
|
1, PROT_READ, "PROT_READ"},
|
|
|
|
{"NONE->R", 1, PROT_NONE, "PROT_NONE",
|
|
1, PROT_READ, "PROT_READ"},
|
|
|
|
{"NONE->RW", 1, PROT_NONE, "PROT_NONE",
|
|
1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
|
|
};
|
|
|
|
static void sig_handler(int signum, siginfo_t *si, void *uc)
|
|
{
|
|
(void)uc;
|
|
|
|
if (signum == SIGSEGV) {
|
|
tst_res(TINFO, "SIGSEGV at %p (sig_expected=%p)", si->si_addr,
|
|
sig_expected);
|
|
if (si->si_addr == sig_expected)
|
|
siglongjmp(sig_escape, 1);
|
|
tst_res(TFAIL, "SIGSEGV somewhere unexpected");
|
|
} else {
|
|
tst_res(TFAIL, "Unexpected signal %s", strsignal(signum));
|
|
}
|
|
}
|
|
|
|
static int test_read(void *p)
|
|
{
|
|
volatile unsigned long *pl = p;
|
|
unsigned long x;
|
|
|
|
if (sigsetjmp(sig_escape, 1)) {
|
|
/* We got a SEGV */
|
|
sig_expected = MAP_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
sig_expected = p;
|
|
barrier();
|
|
x = *pl;
|
|
tst_res(TINFO, "Read back %lu", x);
|
|
barrier();
|
|
sig_expected = MAP_FAILED;
|
|
return 0;
|
|
}
|
|
|
|
static int test_write(void *p, unsigned long val)
|
|
{
|
|
volatile unsigned long *pl = p;
|
|
unsigned long x;
|
|
|
|
if (sigsetjmp(sig_escape, 1)) {
|
|
/* We got a SEGV */
|
|
sig_expected = MAP_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
sig_expected = p;
|
|
barrier();
|
|
*pl = val;
|
|
x = *pl;
|
|
barrier();
|
|
sig_expected = MAP_FAILED;
|
|
|
|
return (x != val);
|
|
}
|
|
|
|
static int test_prot(void *p, int prot, char *prot_str)
|
|
{
|
|
int r, w;
|
|
|
|
r = test_read(p);
|
|
tst_res(TINFO, "On Read: %d", r);
|
|
w = test_write(p, RANDOM_CONSTANT);
|
|
tst_res(TINFO, "On Write: %d", w);
|
|
|
|
if (prot & PROT_READ) {
|
|
if (r != 0) {
|
|
tst_res(TFAIL, "read failed on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (r != -1) {
|
|
tst_res(TFAIL, "read succeeded on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (prot & PROT_WRITE) {
|
|
switch (w) {
|
|
case -1:
|
|
tst_res(TFAIL, "write failed on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
default:
|
|
tst_res(TWARN, "Bug in test");
|
|
return -1;
|
|
}
|
|
} else {
|
|
switch (w) {
|
|
case -1:
|
|
break;
|
|
case 0:
|
|
tst_res(TFAIL, "write succeeded on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
case 1:
|
|
tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
|
|
return -1;
|
|
default:
|
|
tst_res(TWARN, "Bug in test");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void run_test(unsigned int i)
|
|
{
|
|
void *p;
|
|
int ret;
|
|
struct tcase *tc = &tcases[i];
|
|
|
|
tst_res(TINFO, "Test Name: %s", tc->tname);
|
|
|
|
p = SAFE_MMAP(NULL, tc->len1*hpage_size, tc->prot1, MAP_SHARED, fd, 0);
|
|
|
|
ret = test_prot(p, tc->prot1, tc->prot1_str);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
ret = mprotect(p, tc->len2*hpage_size, tc->prot2);
|
|
if (ret != 0) {
|
|
tst_res(TFAIL|TERRNO, "%s: mprotect(prot %s)",
|
|
tc->tname, tc->prot2_str);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = test_prot(p, tc->prot2, tc->prot2_str);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
if (tc->len2 < tc->len1)
|
|
ret = test_prot(p + tc->len2*hpage_size, tc->prot1, tc->prot1_str);
|
|
|
|
tst_res(TPASS, "Successfully tested mprotect %s", tc->tname);
|
|
|
|
cleanup:
|
|
SAFE_MUNMAP(p, tc->len1*hpage_size);
|
|
}
|
|
|
|
static void setup(void)
|
|
{
|
|
struct sigaction sa = {
|
|
.sa_sigaction = sig_handler,
|
|
.sa_flags = SA_SIGINFO,
|
|
};
|
|
|
|
hpage_size = tst_get_hugepage_size();
|
|
SAFE_SIGACTION(SIGSEGV, &sa, NULL);
|
|
|
|
fd = tst_creat_unlinked(MNTPOINT, 0);
|
|
addr = SAFE_MMAP(NULL, 2*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
|
memset(addr, 0, hpage_size);
|
|
SAFE_MUNMAP(addr, hpage_size);
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
SAFE_MUNMAP(addr+hpage_size, hpage_size);
|
|
if (fd >= 0)
|
|
SAFE_CLOSE(fd);
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.tcnt = ARRAY_SIZE(tcases),
|
|
.needs_root = 1,
|
|
.mntpoint = MNTPOINT,
|
|
.needs_hugetlbfs = 1,
|
|
.needs_tmpdir = 1,
|
|
.setup = setup,
|
|
.cleanup = cleanup,
|
|
.test = run_test,
|
|
.hugepages = {2, TST_NEEDS},
|
|
};
|