#include <errno.h>
#include <fcntl.h>
#include <iso/stdlib_iso.h>
#include <libelf.h>
#include <libintl.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#define NOT_SILENT 0
#define SILENT_MODE 1
#define CHILD_SLEEP_PERIOD 2
#define PARENT_SLEEP_PERIOD 1
#define SIG_EVENT SIGUSR1
#define PASS 0
#define FAIL_ZERO 1
#define FAIL_SEGV 2
#define PASS_SEGV 3
#define FAIL_ABORT 4
#define PH_VALID 0
#define PH_INVALID 1
#define WASTE_PAGES 8
#define STACK_SLOP 256
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
extern int _end;
static int data_boundary_test();
static void handler(int);
static int memory_not_shared_after_use();
static int memory_allocation_not_shared();
static int memory_type(const char *);
static void print_message(char *);
static void probe_data_area(void);
static void probe_hole(int);
static void probe_stack(void);
static void probe_text_area(void);
static void segv_action(int, siginfo_t *, void *);
static void set_handler(int);
static int test_stack_end_of_hole();
static int text_area_not_writeable();
static int done_memory_grab = 0;
static int silent;
static int handler_exit_code;
int
main(int argc, char *argv[])
{
int fail_count = 0;
int status = 0;
int bitsize;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
silent = NOT_SILENT;
if (argc == 2) {
if (strncmp(argv[1], "-s", 4) == 0)
silent = SILENT_MODE;
else {
(void) fprintf(stderr, gettext(
"Wrong argument, USAGE: amt [-s]\n"));
exit(EXIT_FAILURE);
}
} else if (argc != 1) {
(void) fprintf(stderr, gettext(
"Wrong usage, USAGE: amt [-s]\n"));
exit(EXIT_FAILURE);
}
bitsize = memory_type(argv[0]);
if (silent == NOT_SILENT)
(void) printf(gettext(
"\n\nAMT Test Program -- %d bit application\n"
"================\n"), bitsize);
if ((status = test_stack_end_of_hole()) == EXIT_FAILURE) {
fail_count++;
print_message(gettext("TEST 1 FAILED\n"));
} else if (status == FAIL_ABORT) {
fail_count++;
print_message(gettext("FAIL: Logic error in test\n"));
} else if (status == EXIT_SUCCESS)
print_message(gettext("TEST 1 PASSED\n"));
if (data_boundary_test() != EXIT_SUCCESS) {
fail_count++;
print_message(gettext("TEST 2 FAILED\n"));
} else
print_message(gettext("TEST 2 PASSED\n"));
if (text_area_not_writeable() != EXIT_SUCCESS) {
fail_count++;
print_message(gettext("TEST 3 FAILED\n"));
} else
print_message(gettext("TEST 3 PASSED\n"));
if (memory_not_shared_after_use() != EXIT_SUCCESS) {
fail_count++;
print_message(gettext("TEST 4 FAILED\n"));
} else
print_message(gettext("TEST 4 PASSED\n"));
if (memory_allocation_not_shared() != EXIT_SUCCESS) {
fail_count++;
print_message(gettext("TEST 5 FAILED\n"));
} else
print_message(gettext("TEST 5 PASSED\n"));
if (silent == NOT_SILENT) {
if (fail_count > 0)
(void) printf(gettext("\n %d TESTS FAILED\n\n"),
fail_count);
else
(void) printf(gettext("\nTESTS SUCCEEDED\n\n"));
}
return (fail_count);
}
static int
data_boundary_test()
{
int exit_status = EXIT_SUCCESS;
pid_t pid;
int status;
print_message(gettext("\n\nTest 2- Data Side Boundary Test.\n"));
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
set_handler(SIGSEGV);
probe_data_area();
}
(void) wait(&status);
status = WEXITSTATUS(status);
if (status == PASS)
print_message(gettext(
"PASS: Successful read/write in data area.\n"));
else if (status == FAIL_SEGV) {
print_message(gettext(
"FAIL: Caught a segmentation fault while "
"attempting to write to the data area.\n"));
exit_status = EXIT_FAILURE;
} else {
(void) printf(gettext("Test program failure: %d\n"),
status);
exit_status = EXIT_FAILURE;
}
return (exit_status);
}
static void
probe_data_area()
{
int *p;
volatile int p1 __unused;
void *address;
handler_exit_code = FAIL_SEGV;
(void) sbrk(PAGESIZE);
address = sbrk(0);
p = ((int *)P2ROUNDUP((uintptr_t)address, PAGESIZE)) - 2;
*p = 9999;
p1 = *p;
p = &_end;
*p = 9898;
p1 = *p;
exit(EXIT_SUCCESS);
}
static int
text_area_not_writeable()
{
int exit_status = EXIT_SUCCESS;
pid_t pid;
int status;
print_message(gettext(
"\n\nTest 3- Text Area Not Writeable\n"
"Verify that a write to the text space does not cause "
"a write to the executable\n"
"file from which it came, or to another process which "
"shares that text.\n"));
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
set_handler(SIGSEGV);
probe_text_area();
}
(void) wait(&status);
status = WEXITSTATUS(status);
if (status == PASS) {
print_message(gettext(
"FAIL: We did not cause a segmentation fault.\n"));
exit_status = EXIT_FAILURE;
} else if (status == FAIL_SEGV) {
print_message(gettext(
"PASS: Caught the segmentation fault, "
"meaning we can't write to text area.\n"));
} else {
(void) printf(gettext(
"Test program failure: %d\n"), status);
exit_status = EXIT_FAILURE;
}
return (exit_status);
}
static void
probe_text_area()
{
handler_exit_code = FAIL_SEGV;
*(caddr_t)probe_text_area = 0xff;
exit(EXIT_FAILURE);
}
static int
memory_not_shared_after_use()
{
pid_t pid;
int x = 1000;
int exit_status = EXIT_SUCCESS;
print_message(gettext("\n\nTest 4- Memory Not Shared After Write\n"
"Verify that anonymous memory initially shared by two "
"processes (e.g. after a\n"
"fork) is not shared after either process writes "
"to it.\n"));
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
x = 2000;
(void) sleep(CHILD_SLEEP_PERIOD);
exit(EXIT_SUCCESS);
}
(void) sleep(PARENT_SLEEP_PERIOD);
if (x == 1000)
exit_status = EXIT_SUCCESS;
else
exit_status = EXIT_FAILURE;
return (exit_status);
}
static int
memory_allocation_not_shared()
{
pid_t pid;
pid_t parent_pid;
int exit_status = 0;
caddr_t address;
caddr_t hole_start;
caddr_t hole_after;
void (*old_handler) ();
print_message(gettext(
"\n\nTest 5- Memory Allocation is Not Shared\n"
"Verify that newly allocated memory in one of two "
"processes created by forking\n"
"does not result in newly allocated memory in the other.\n"));
hole_start = (caddr_t)sbrk(0);
if (silent == NOT_SILENT)
(void) printf(gettext(
"Parent address of hole before child change: %08X\n"),
hole_start);
old_handler = signal(SIG_EVENT, &handler);
if (old_handler == SIG_ERR) {
print_message(gettext(
"Can't establish signal handler, test failed\n"));
return (EXIT_FAILURE);
}
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
address = sbrk(0);
if (silent == NOT_SILENT)
(void) printf(gettext(
"Child end of hole before change: %08X\n"),
address);
if (brk((address+PAGESIZE)) != 0) {
print_message(gettext(
"Can't change start of hole address.\n"));
exit(EXIT_FAILURE);
}
address = sbrk(0);
if (silent == NOT_SILENT)
(void) printf(gettext(
"Child end of hole after change: %08X\n"),
address);
parent_pid = getppid();
if (sigsend(P_PID, parent_pid, SIG_EVENT) != 0) {
print_message(gettext("Can't send signal to parent, "
"test failed\n"));
exit(EXIT_FAILURE);
}
(void) sleep(CHILD_SLEEP_PERIOD);
exit(EXIT_SUCCESS);
}
(void) sleep(PARENT_SLEEP_PERIOD);
if (done_memory_grab != 1) {
print_message(gettext(
"Child failed to do memory alterations, "
"exiting\n"));
return (EXIT_FAILURE);
}
hole_after = sbrk(0);
if (silent == NOT_SILENT)
(void) printf(gettext(
"Parent address of hole after child change: "
"%08X\n"), hole_after);
if (hole_start == hole_after)
print_message(gettext(
"PASS: Hole is same size in parent.\n"));
else {
print_message(gettext(
"FAIL: Hole is a different size.\n"));
exit_status = EXIT_FAILURE;
}
(void) wait(0);
if (signal(SIG_EVENT, old_handler) == SIG_ERR) {
print_message(gettext("Couldn't put back old signal handler, "
"test failed.\n"));
return (EXIT_FAILURE);
}
return (exit_status);
}
static void
print_message(char *message)
{
if (silent == NOT_SILENT)
(void) printf("%s", message);
}
static int
test_stack_end_of_hole()
{
pid_t pid;
int status;
int exit_status = EXIT_SUCCESS;
print_message(gettext("\n\nTest 1- stack Side Boundary Test\n"));
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
set_handler(SIGSEGV);
probe_stack();
}
(void) wait(&status);
status = WEXITSTATUS(status);
if (status == FAIL_ZERO) {
print_message(gettext("Fail with non-zero read.\n"));
exit_status = EXIT_FAILURE;
} else if (status != PASS) {
print_message(gettext("Test program failure\n"));
exit_status = EXIT_FAILURE;
}
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
set_handler(SIGSEGV);
probe_hole(PH_INVALID);
}
(void) wait(&status);
status = WEXITSTATUS(status);
if (status == FAIL_SEGV) {
print_message(
gettext("Fail (SEGV expected, not received).\n"));
exit_status = EXIT_FAILURE;
} else if (status != PASS_SEGV) {
print_message(gettext("Test program failure.\n"));
exit_status = EXIT_FAILURE;
}
if ((pid = fork()) == -1) {
print_message(gettext("Fork failed\n"));
return (EXIT_FAILURE);
} else if (pid == 0) {
set_handler(SIGSEGV);
probe_hole(PH_VALID);
}
(void) wait(&status);
status = WEXITSTATUS(status);
if (status == FAIL_SEGV) {
print_message(gettext("Fail (got SEGV).\n"));
exit_status = EXIT_FAILURE;
} else if (status != PASS) {
print_message(gettext("Test program failure.\n"));
exit_status = EXIT_FAILURE;
}
return (exit_status);
}
static void
set_handler(int sig)
{
struct sigaction act;
act.sa_handler = NULL;
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = segv_action;
(void) sigemptyset(&(act.sa_mask));
if (sigaction(sig, &act, NULL) < 0) {
if (silent == NOT_SILENT) {
(void) fprintf(stderr, gettext(
"sigaction() returned error: %s\n"),
strerror(errno));
}
exit(EXIT_FAILURE);
}
}
static void
segv_action(int which_sig, siginfo_t *t1, void *t2)
{
exit(handler_exit_code);
}
static void
probe_stack(void)
{
unsigned char *end;
unsigned char probe;
long i;
int j;
unsigned char last_fail, *last_fail_address;
unsigned char mark = 0xAA;
handler_exit_code = FAIL_SEGV;
end = &mark;
end -= (WASTE_PAGES * PAGESIZE) + STACK_SLOP;
for (i = 0, j = 0; i < PAGESIZE; i++) {
if ((probe = *end) != 0) {
j++;
last_fail = probe;
last_fail_address = end;
}
end--;
}
if (j != 0) {
if (silent == NOT_SILENT)
(void) fprintf(stderr, gettext(
"probe_stack failed. address=0x%08X; "
"probe=0x%02X; content = %d\n"),
(caddr_t)last_fail_address, last_fail, j);
exit(FAIL_ZERO);
}
exit(EXIT_SUCCESS);
}
static void
probe_hole(int test_type)
{
long i;
volatile unsigned char probe __unused;
unsigned char *probe_adr;
void *address;
address = sbrk(0);
if (address == (void *)-1) {
print_message(gettext("Test program logic error\n"));
exit(FAIL_ABORT);
}
if (test_type == PH_VALID) {
handler_exit_code = FAIL_SEGV;
probe_adr = (unsigned char *)address - sizeof (char);
for (i = 0; i < PAGESIZE; i++)
probe = *probe_adr--;
exit(EXIT_SUCCESS);
} else {
handler_exit_code = PASS_SEGV;
address = (void *)P2ROUNDUP((uintptr_t)address, PAGESIZE);
probe_adr = (unsigned char *)address;
probe = *probe_adr;
exit(FAIL_SEGV);
}
}
void
handler(int signal)
{
done_memory_grab = 1;
}
static int
memory_type(const char *path)
{
char *idarray;
Elf *elf;
int d;
int bits = 0;
if ((d = open(path, O_RDONLY)) < 0) {
(void) fprintf(stderr,
"cannot open: %s -- %s\n",
path, strerror(errno));
return (bits);
}
if (elf_version(EV_CURRENT) == EV_NONE) {
(void) fprintf(stderr,
"internal error: ELF library out of date?\n");
(void) close(d);
return (bits);
}
elf = elf_begin(d, ELF_C_READ, (Elf *)0);
if (elf_kind(elf) != ELF_K_ELF) {
(void) elf_end(elf);
(void) close(d);
return (bits);
}
idarray = elf_getident(elf, 0);
if (idarray[EI_CLASS] == ELFCLASS32) {
bits = 32;
} else if (idarray[EI_CLASS] == ELFCLASS64) {
bits = 64;
}
(void) elf_end(elf);
(void) close(d);
return (bits);
}