From cffd32bc1a865ee383fee4271ea7d7bd098c2aa6 Mon Sep 17 00:00:00 2001 From: David Jones Date: Fri, 18 Apr 1997 15:36:22 +0100 Subject: [PATCH] New unit protection for DIGITAL UNIX Copied from Perforce Change: 17625 ServerID: perforce.ravenbrook.com --- mps/src/proto1.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 mps/src/proto1.c diff --git a/mps/src/proto1.c b/mps/src/proto1.c new file mode 100644 index 00000000000..5db63146004 --- /dev/null +++ b/mps/src/proto1.c @@ -0,0 +1,195 @@ +/* impl.c.protso: PROTECTION FOR DIGITAL UNIX + * + * $HopeName$ + * Copyright (C) 1995,1997 Harlequin Group, all rights reserved + * + */ + + +/* open sesame magic, see standards(5) */ +#define _POSIX_C_SOURCE 199309L +#define _XOPEN_SOURCE_EXTENDED 1 + +#include "mpm.h" + +#ifndef MPS_OS_O1 +#error "proto1.c is DIGITAL UNIX specific, but MPS_OS_O1 is not set" +#endif + +#include +#include +#include +#include +#include +#include +/* for getpid() */ +#include + +SRCID(proto1, "$HopeName$"); + + +/* The previously-installed signal action, as returned by */ +/* sigaction(3). See ProtSetup. */ + +static struct sigaction sigNext; + + +/* == Protection Signal Handler == + * + * This is the signal handler installed by ProtSetup to deal with + * protection faults. It is installed on the SIGSEGV signal. + * It decodes the protection fault details from the signal context + * and passes them to SpaceAccess, which attempts to handle the + * fault and remove its cause. If the fault is handled, then + * the handler returns and execution resumes. If it isn't handled, + * then sigHandle does its best to pass the signal on to the + * previously installed signal handler (sigNext). + * + * .sigh.addr: We assume that the OS decodes the address to something + * sensible + * .sigh.limit: We throw away the limit information. + */ + +static void sigHandle(int sig, siginfo_t *info, void *context) +{ + int e; + sigset_t sigset, oldset; + struct sigaction sa; + + AVER(sig == SIGSEGV); + AVER(info != NULL); + + if(info->si_code == SEGV_ACCERR) { + AccessSet mode; + Addr base, limit; + + /* We can't determine the access mode (read, write, etc.) */ + /* under Solaris without decoding the faulting instruction. */ + /* Don't bother, yet. We can do this if necessary. */ + + mode = AccessREAD | AccessWRITE; + + /* We assume that the access is for one word at the address. */ + + base = (Addr)info->si_addr; + limit = AddrAdd(base, (Size)sizeof(Addr)); + + /* Offer each protection structure the opportunity to handle the */ + /* exception. If it succeeds, then allow the mutator to continue. */ + + if(SpaceAccess(base, mode)) + return; + } + + /* The exception was not handled by any known protection structure, */ + /* so throw it to the previously installed handler. */ + + /* @@ This is really weak. + * Need to implement rest of the contract of sigaction */ + + e = sigaction(SIGSEGV, &sigNext, &sa); + AVER(e == 0); + sigemptyset(&sigset); + sigaddset(&sigset, SIGSEGV); + e = sigprocmask(SIG_UNBLOCK, &sigset, &oldset); + AVER(e == 0); + kill(getpid(), SIGSEGV); + e = sigprocmask(SIG_SETMASK, &oldset, NULL); + AVER(e == 0); + e = sigaction(SIGSEGV, &sa, NULL); + AVER(e == 0); +} + + +/* == Global Protection Setup == + * + * Under DIGITAL UNIX, the global setup involves installing a signal handler + * on SIGSEGV to catch and handle protection faults (see sigHandle). + * The previous handler is recorded so that it can be reached from + * sigHandle if it fails to handle the fault. + * + * NOTE: There are problems with this approach: + * 1. we can't honor the wishes of the sigaction(2) entry for the + * previous handler, + * 2. what if this thread is suspended just after calling signal(3)? + * The sigNext variable will never be initialized! + */ + +void ProtSetup(void) +{ + struct sigaction sa; + int result; + + sa.sa_sigaction = sigHandle; + sa.sa_flags = SA_SIGINFO; + + result = sigaction(SIGSEGV, &sa, &sigNext); + AVER(result == 0); +} + + +/* == Set Protection == + * + * This is just a thin veneer on top of mprotect(2). + */ + +void ProtSet(Addr base, Addr limit, AccessSet mode) +{ + int flags; + + AVER(sizeof(size_t) == sizeof(Addr)); + AVER(base < limit); + AVER(base != 0); + AVER(AddrOffset(base, limit) <= INT_MAX); /* should be redundant */ + + /* convert between MPS AccessSet and UNIX PROT thingies. */ + switch(mode) { + case AccessWRITE | AccessREAD: + case AccessREAD: /* forbids writes as well */ + flags = PROT_NONE; + break; + case AccessWRITE: + flags = PROT_READ | PROT_EXEC; + break; + case AccessSetEMPTY: + flags = PROT_READ | PROT_WRITE | PROT_EXEC; + break; + default: + NOTREACHED; + flags = PROT_NONE; + } + + if(mprotect((void *)base, (size_t)AddrOffset(base, limit), flags) != 0) + NOTREACHED; +} + + +/* ProtSync -- synchronize protection settings with hardware + * + * This does nothing under Solaris. + */ + +void ProtSync(Space space) +{ + UNUSED(space); + NOOP; +} + + + +/* == Protection Trampoline == + * + * The protection trampoline is trivial under DIGITAL UNIX, as there is + * nothing that needs to be done in the dynamic context of the mutator in + * order to catch faults. (Contrast this with Win32 Structured Exception + * Handling.) + */ + +void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), + void *p, size_t s) +{ + AVER(f != NULL); + AVER(resultReturn != NULL); + + *resultReturn = (*f)(p, s); +}