/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Abstract Machine Test; executes memory access tests to show
* compliance with Common Criteria object reuse and process address
* space separation requirements.
*/
#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/resource.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <wait.h>
#define NOT_SILENT 0
/* access */
/* is likely to exist */
/* of stack I'll find unused space */
#if !defined(TEXT_DOMAIN)
#endif
extern int _end; /* first address after the end of initialized data */
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;
/*
* Main Routine
*/
int
{
int fail_count = 0;
int status = 0;
int bitsize;
/* Internationalization */
(void) textdomain(TEXT_DOMAIN);
silent = NOT_SILENT;
if (argc == 2) {
/* Pull out argument provided */
/* -s silent mode, no status or error messages. */
else {
/* Wrong argument */
"Wrong argument, USAGE: amt [-s]\n"));
}
} else if (argc != 1) {
/* Illegal number of arguments. */
"Wrong usage, USAGE: amt [-s]\n"));
}
if (silent == NOT_SILENT)
"\n\nAMT Test Program -- %d bit application\n"
"================\n"), bitsize);
/*
* test_stack_end_of_hole must be the first test, or the stack
* is of an unknown size.
*/
/* Normal fail */
fail_count++;
} else if (status == FAIL_ABORT) {
/* Test logic failure */
fail_count++;
} else if (status == EXIT_SUCCESS)
/* Carry out test 2 */
if (data_boundary_test() != EXIT_SUCCESS) {
fail_count++;
} else
/* Carry out test 3 */
if (text_area_not_writeable() != EXIT_SUCCESS) {
fail_count++;
} else
/* Carry out test 4 */
if (memory_not_shared_after_use() != EXIT_SUCCESS) {
fail_count++;
} else
/* Carry out test 5 */
if (memory_allocation_not_shared() != EXIT_SUCCESS) {
fail_count++;
} else
if (silent == NOT_SILENT) {
if (fail_count > 0)
else
}
return (fail_count);
}
/*
* Test the data boundaries. First test inside the data area at the boundary
* of the "hole" area. Second test inside the data area at the text area
* boundary. Both should pass.
*/
static int
{
int status;
return (EXIT_FAILURE);
} else if (pid == 0) { /* Am I my child? */
/* probe_data_area() does exit() */
}
/* still parent */
"FAIL: Caught a segmentation fault while "
"attempting to write to the data area.\n"));
} else {
status);
}
return (exit_status);
}
static void
{
int *p;
/* LINTED */
volatile int p1;
void *address;
/* set handler status */
/*
* Get an address in the data area, near to the "hole".
* sbrk returns prior address value; rather than calculating
* the sbrk result, sbrk is called twice, so address points
* to the new end of data
*/
/*
* Get to the inside edge of a page boundary
* two integer words short of a new page
*/
/* Try writing to it, shouldn't cause a segmentation fault. */
*p = 9999;
/* Should be able to read back with no problems. */
p1 = *p;
/*
* Get an address near the text area boundary, but in the data
* area. _etext rounded up a page isn't correct since the
* initialized data area isn't writeable.
*
* Future versions should consider handling initialized data
* separately -- writing to initialized data should generate
* a fault.
*/
p = &_end;
/* Try writing to it, should succeed. */
*p = 9898;
/* Should be able to read back with no problems. */
p1 = *p;
}
/*
* Test that we cannot write to the text area. An attempt to write to
* the text area will result in a segmentation fault. So if we catch it,
* test has succeed, else it has failed.
*/
static int
{
int status;
"\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"));
return (EXIT_FAILURE);
} else if (pid == 0) { /* Am I my child? */
/* probe_text_area() does exit() */
}
/* still parent */
"FAIL: We did not cause a segmentation fault.\n"));
"PASS: Caught the segmentation fault, "
"meaning we can't write to text area.\n"));
} else {
"Test program failure: %d\n"), status);
}
return (exit_status);
}
/*
* write to text area, trigger a SEGV
*/
static void
{
}
/*
* Test that when we set some values and fork a process, when the child
* writes to these inherited values, the parents copies are not changed.
*/
static int
{
int x = 1000;
"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"));
return (EXIT_FAILURE);
} else if (pid == 0) { /* I am the child. */
/*
* Change child value; this should not change
* parent value.
*/
x = 2000;
/* Wait for parent to test value */
(void) sleep(CHILD_SLEEP_PERIOD);
}
/* Wait for child to do its stuff. */
(void) sleep(PARENT_SLEEP_PERIOD);
if (x == 1000)
else
return (exit_status);
}
/*
* If we fork a process and then allocate some memory in that process,
* we should not see any memory changes in the parent.
*/
static int
{
int exit_status = 0;
void (*old_handler) ();
"\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"));
/* Save Size of data area and 1st block address of "hole" */
if (silent == NOT_SILENT)
"Parent address of hole before child change: %08X\n"),
/* Set handler for signal SIG_EVENT (define at start) */
if (old_handler == SIG_ERR) {
"Can't establish signal handler, test failed\n"));
return (EXIT_FAILURE);
}
return (EXIT_FAILURE);
} else if (pid == 0) { /* We are the child. */
if (silent == NOT_SILENT)
"Child end of hole before change: %08X\n"),
address);
"Can't change start of hole address.\n"));
}
if (silent == NOT_SILENT)
"Child end of hole after change: %08X\n"),
address);
/* Tell the parent we're done. */
parent_pid = getppid();
"test failed\n"));
}
/* Sleep before exiting to allow parent to finish processing. */
(void) sleep(CHILD_SLEEP_PERIOD);
}
/* Wait for child to do its work. */
(void) sleep(PARENT_SLEEP_PERIOD);
if (done_memory_grab != 1) {
"Child failed to do memory alterations, "
"exiting\n"));
return (EXIT_FAILURE);
}
hole_after = sbrk(0);
if (silent == NOT_SILENT)
"Parent address of hole after child change: "
"%08X\n"), hole_after);
/* Test size of hole and data region. */
if (hole_start == hole_after)
"PASS: Hole is same size in parent.\n"));
else {
"FAIL: Hole is a different size.\n"));
}
/* Wait for child to finish. */
(void) wait(0);
"test failed.\n"));
return (EXIT_FAILURE);
}
return (exit_status);
}
static void
{
if (silent == NOT_SILENT)
}
static int
{
int status;
/* sub test 1: the space the stack grows into is zero */
return (EXIT_FAILURE);
} else if (pid == 0) { /* Am I my child? */
/* probe_stack() does exit */
probe_stack();
}
/* still parent */
}
/* sub test 2: the space in hole is not readable */
return (EXIT_FAILURE);
} else if (pid == 0) { /* Am I my child? */
/* probe_hole does exit */
}
/* still parent */
gettext("Fail (SEGV expected, not received).\n"));
}
/* sub test 3: the space in new page below hole is zero */
return (EXIT_FAILURE);
} else if (pid == 0) { /* Am I my child? */
/* probe_hole does exit */
}
/* still parent */
}
return (exit_status);
}
/*
* set_handler
*/
static void
{
if (silent == NOT_SILENT) {
"sigaction() returned error: %s\n"),
}
}
}
/*ARGSUSED*/
static void
{
}
/*
* probe_stack
*
* Warning -- if you do a printf or fprintf prior to the actual
* reading from the stack, you've changed the stack to an unknown
* state. (stack memory isn't free'd automatically and this function
* needs to touch virgin stack space.)
*/
static void
probe_stack(void)
{
unsigned char probe;
long i;
int j;
/* stack growth is negative */
for (i = 0, j = 0; i < PAGESIZE; i++) {
j++;
}
end--;
}
if (j != 0) {
if (silent == NOT_SILENT)
"probe_stack failed. address=0x%08X; "
"probe=0x%02X; content = %d\n"),
}
}
static void
{
long i;
/* LINTED */
volatile unsigned char probe;
unsigned char *probe_adr;
void *address;
if (address == (void *)-1) {
}
/* show that access works inside the hole */
for (i = 0; i < PAGESIZE; i++)
} else {
/* show that a trap occurs in the hole */
}
}
/*
* Catch signal, child to parent
*/
/*ARGSUSED*/
void
{
done_memory_grab = 1;
}
/*
* memory_type: Determine whether a given executable file is compiled
* as 32 or 64 bit.
*
* The following code was stolen from isainfo (1)
*/
static int
char *idarray;
int d;
int bits = 0;
"cannot open: %s -- %s\n",
return (bits);
}
"internal error: ELF library out of date?\n");
(void) close(d);
return (bits);
}
(void) close(d);
return (bits);
}
bits = 32;
bits = 64;
}
(void) close(d);
return (bits);
}