Dear all,

I have implemented the mprotect system call for Minix 3.1.2a. This allows
you to protect memory agains reading and/or writing, which can be very
useful when debugging a program that performs unexpected memory accesses.

Some notes:
- The call is described here:
http://www.opengroup.org/onlinepubs/.../mprotect.html
- Only PROT_NONE, PROT_READ | PROT_EXEC and
PROT_READ | PROT_WRITE | PROT_EXEC are fully supported. Other
combinations cause additional privileges to be granted (this is a
limitation of the Intel page protection bits and is allowed by the
manual page).
- mprotect will only work reliably for programs that have combined data
and code segments; i have not tested it with binaries that separate the
two, but expect that in this case the call will only work for the data
segment in this case; to get combined segments on ACK use the "-com"
flag; with GCC this is the default.

I have supplied the patch after this post. You will need the GNU patch
program to apply it, as the Minix patch program cannot reliably apply
recursive patches. GNU Patch is not included in GNU diffutils in packman,
so you should get it here: http://www.cs.vu.nl/~dcvmoole/minix

Detailed instructions to apply the patch follow below (you should not
need them if you have expierience applying patches, but just in case).

Assumptions:
- You are running unmodified Minix 3.1.2a (adapting the patch to
different version should be easy though).
- wget has been installed (using packman).
- The patch is stored as mprotect.patch in the current directory.

wget http://www.cs.vu.nl/~dcvmoole/minix/...-2.5.4.tar.bz2
bunzip2 patch-2.5.4.tar.bz2
tar xf patch-2.5.4.tar
/usr/gnu/bin/patch -p0 < mprotect.patch
cd /usr/src/tools
make fresh install

I hope this will be useful to anyone.

With kind regards,
Erik van der Kouwe



------------------------------------------------------------------------
patch follows below
------------------------------------------------------------------------

diff -Naur /usr/src.release/include/errno.h /usr/src/include/errno.h
--- /usr/src.release/include/errno.h 2008-03-07 20:21:35.000000000 +0000
+++ /usr/src/include/errno.h 2008-02-28 08:31:24.000000000 +0000
@@ -72,6 +72,7 @@
#define ENAMETOOLONG (_SIGN 36) /* file name too long */
#define ENOLCK (_SIGN 37) /* no locks available */
#define ENOSYS (_SIGN 38) /* function not implemented */
+#define ENOTSUP ENOSYS
#define ENOTEMPTY (_SIGN 39) /* directory not empty */
#define ELOOP (_SIGN 40) /* too many levels of symlinks detected */

diff -Naur /usr/src.release/include/minix/callnr.h /usr/src/include/minix/callnr.h
--- /usr/src.release/include/minix/callnr.h 2008-03-07 20:21:35.000000000 +0000
+++ /usr/src/include/minix/callnr.h 2008-02-27 19:12:44.000000000 +0000
@@ -1,4 +1,4 @@
-#define NCALLS 95 /* number of system calls allowed */
+#define NCALLS 96 /* number of system calls allowed */

#define EXIT 1
#define FORK 2
@@ -52,6 +52,7 @@
#define CHROOT 61
#define SETSID 62
#define GETPGRP 63
+#define MPROTECT 95

/* The following are not system calls, but are processed like them. */
#define UNPAUSE 65 /* to MM or FS: check for EINTR */
diff -Naur /usr/src.release/include/minix/com.h /usr/src/include/minix/com.h
--- /usr/src.release/include/minix/com.h 2008-03-07 20:21:35.000000000 +0000
+++ /usr/src/include/minix/com.h 2008-02-29 08:48:18.000000000 +0000
@@ -278,8 +278,9 @@
# define SYS_IOPENABLE (KERNEL_CALL + 28) /* sys_enable_iop() */
# define SYS_VM_SETBUF (KERNEL_CALL + 29) /* sys_vm_setbuf() */
# define SYS_VM_MAP (KERNEL_CALL + 30) /* sys_vm_map() */
+# define SYS_MPROTECT (KERNEL_CALL + 31) /* sys_mprotect() */

-#define NR_SYS_CALLS 31 /* number of system calls */
+#define NR_SYS_CALLS 32 /* number of system calls */

/* Subfunctions for SYS_PRIVCTL */
#define SYS_PRIV_INIT 1 /* Initialize a privilege structure */
@@ -409,6 +410,13 @@
#define VM_MAP_SIZE m4_l4
#define VM_MAP_ADDR m4_l5

+/* mprotect */
+#define MPROTECT_ENDPT m4_l1
+#define MPROTECT_BASE m4_l2
+#define MPROTECT_SIZE m4_l3
+#define MPROTECT_BITS m4_l4
+#define MPROTECT_MASK m4_l5
+
/* Field names for SYS_TRACE, SYS_PRIVCTL. */
#define CTL_ENDPT m2_i1 /* process number of the caller */
#define CTL_REQUEST m2_i2 /* server control request */
diff -Naur /usr/src.release/include/minix/syslib.h /usr/src/include/minix/syslib.h
--- /usr/src.release/include/minix/syslib.h 2008-03-07 20:21:36.000000000 +0000
+++ /usr/src/include/minix/syslib.h 2008-02-27 20:55:51.000000000 +0000
@@ -42,6 +42,9 @@
phys_bytes high));
_PROTOTYPE( int sys_vm_map, (int proc_nr, int do_map,
phys_bytes base, phys_bytes size, phys_bytes offset));
+_PROTOTYPE( int sys_mprotect, (int proc_nr,
+ unsigned long page_first, unsigned long page_count,
+ unsigned long protect_bits, unsigned long protect_mask));

/* Shorthands for sys_sdevio() system call. */
#define sys_insb(port, proc_nr, buffer, count) \
diff -Naur /usr/src.release/include/sys/mman.h /usr/src/include/sys/mman.h
--- /usr/src.release/include/sys/mman.h 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/include/sys/mman.h 2008-02-29 10:09:15.000000000 +0000
@@ -0,0 +1,20 @@
+/*
+ * memory management Author: Erik van der Kouwe
+ */
+
+#ifndef _SYS__MMAN_H
+#define _SYS__MMAN_H
+
+#include
+
+#define PROT_READ 1 /* allow reading */
+#define PROT_WRITE 2 /* allow writing */
+#define PROT_EXEC 4 /* allow code execution */
+#define PROT_NONE 0 /* completely block access */
+
+/*
+ * See http://www.opengroup.org/onlinepubs/.../mprotect.html for documentation
+ */
+_PROTOTYPE( int mprotect, (const void *addr, size_t len, int prot) );
+
+#endif /* _SYS__MMAN_H */
diff -Naur /usr/src.release/include/sys/vm.h /usr/src/include/sys/vm.h
--- /usr/src.release/include/sys/vm.h 2008-03-07 20:21:36.000000000 +0000
+++ /usr/src/include/sys/vm.h 2008-02-28 07:21:19.000000000 +0000
@@ -19,6 +19,9 @@
#define I386_VM_USER 0x004 /* User access allowed */
#define I386_VM_PWT 0x008 /* Write through */
#define I386_VM_PCD 0x010 /* Cache disable */
+#define I386_VM_ACCESSED 0x020 /* Page has been accessed */
+#define I386_VM_DIRTY 0x040 /* Page has been modified */
+#define I386_VM_AVAILABLE 0xe00 /* Bits available for programmer */
#define I386_VM_ADDR_MASK 0xFFFFF000 /* physical address */

#define I386_VM_PT_ENT_SIZE 4 /* Size of a page table entry */
diff -Naur /usr/src.release/kernel/system/do_vm.c /usr/src/kernel/system/do_vm.c
--- /usr/src.release/kernel/system/do_vm.c 2008-03-07 20:21:36.000000000 +0000
+++ /usr/src/kernel/system/do_vm.c 2008-03-02 19:45:44.000000000 +0000
@@ -1,12 +1,20 @@
-/* The system call implemented in this file:
+/* The system calls implemented in this file:
* m_type: SYS_VM_MAP
+ * m_type: SYS_MPROTECT
*
- * The parameters for this system call are:
+ * The parameters for SYS_VM_MAP are:
* m4_l1: Process that requests map (VM_MAP_ENDPT)
* m4_l2: Map (TRUE) or unmap (FALSE) (VM_MAP_MAPUNMAP)
* m4_l3: Base address (VM_MAP_BASE)
* m4_l4: Size (VM_MAP_SIZE)
* m4_l5: Memory address (VM_MAP_ADDR)
+ *
+ * The parameters for SYS_MPROTECT are:
+ * m4_l1: Process that requests map (MPROTECT_ENDPT)
+ * m4_l2: First page to modify (MPROTECT_BASE)
+ * m4_l3: Number of pages to modify (MPROTECT_SIZE)
+ * m4_l4: Protection bits to set (MPROTECT_BITS)
+ * m4_l5: Bitmask of affected protection bits (MPROTECT_MASK)
*/
#include "../system.h"

@@ -23,6 +31,8 @@
FORWARD _PROTOTYPE( void vm_enable_paging, (void) );
FORWARD _PROTOTYPE( void map_range, (u32_t base, u32_t size,
u32_t offset) );
+FORWARD _PROTOTYPE( void protect_range, (u32_t base, u32_t size,
+ u32_t bits, u32_t mask) );

/*================================================= ==========================*
* do_vm_map *
@@ -75,6 +85,78 @@
}

/*================================================= ==========================*
+ * do_mprotect *
+ *================================================= ==========================*/
+
+/* page table bits that may be set or cleared */
+#define I386_VM_MASK_SETTABLE (I386_VM_WRITE | I386_VM_USER | I386_VM_PWT | \
+ I386_VM_PCD | I386_VM_ACCESSED | \
+ I386_VM_DIRTY | I386_VM_AVAILABLE)
+
+PUBLIC int do_mprotect(m_ptr)
+message *m_ptr; /* pointer to request message */
+{
+ phys_bytes base, size, offset, p_phys;
+ unsigned long bits, mask;
+ int proc_nr;
+ struct proc *pp;
+
+ /* check parameters */
+ base = m_ptr->MPROTECT_BASE;
+ size = m_ptr->MPROTECT_SIZE;
+ bits = m_ptr->MPROTECT_BITS;
+ mask = m_ptr->MPROTECT_MASK;
+ if (base % PAGE_SIZE || /* must be page boundary */
+ size % PAGE_SIZE || /* must be integer number of pages */
+ bits & ~I386_VM_MASK_SETTABLE || /* only set allowed bits */
+ mask & ~I386_VM_MASK_SETTABLE) /* only clear allowed bits */
+ return EINVAL;
+
+ /* determine calling process */
+ if (m_ptr->MPROTECT_ENDPT == SELF)
+ proc_nr = who_p;
+ else
+ if (!isokendpt(m_ptr->MPROTECT_ENDPT, &proc_nr))
+ return EINVAL;
+
+ /* if needed, initialize page table and related structures */
+ if (vm_needs_init)
+ {
+ vm_needs_init = 0;
+ vm_init();
+ }
+
+ /* determine physical address of base */
+ pp = proc_addr(proc_nr);
+ p_phys = umap_local(pp, D, base, size);
+ if (p_phys == 0)
+ return ENOMEM;
+
+ if (p_phys % PAGE_SIZE)
+ return EINVAL;
+
+#ifdef DEBUG_MPROTECT
+ kprintf("do_mprotect(0x%x, 0x%x, 0x%x, 0x%x) p#=%d pname=\"%s\" p_phys=0x%x\n"
+ " text: vir=0x%.8x phys=0x%.8x size=0x%.8x\n"
+ " data: vir=0x%.8x phys=0x%.8x size=0x%.8x\n"
+ " stck: vir=0x%.8x phys=0x%.8x size=0x%.8x\n",
+ base, size, bits, mask, proc_nr, pp->p_name, p_phys,
+ pp->p_memmap[T].mem_vir << CLICK_SHIFT, pp->p_memmap[T].mem_phys << CLICK_SHIFT, pp->p_memmap[T].mem_len << CLICK_SHIFT,
+ pp->p_memmap[D].mem_vir << CLICK_SHIFT, pp->p_memmap[D].mem_phys << CLICK_SHIFT, pp->p_memmap[D].mem_len << CLICK_SHIFT,
+ pp->p_memmap[S].mem_vir << CLICK_SHIFT, pp->p_memmap[S].mem_phys << CLICK_SHIFT, pp->p_memmap[S].mem_len << CLICK_SHIFT);
+#endif
+
+ /* update protection in page table - make sure vm_map_default will get called */
+ pp->p_misc_flags |= MF_VM;
+ protect_range(p_phys, size, bits, mask);
+
+ /* refresh TLBs to apply the new page table */
+ vm_set_cr3(vm_cr3);
+
+ return OK;
+}
+
+/*================================================= ==========================*
* vm_map_default *
*================================================= ==========================*/
PUBLIC void vm_map_default(pp)
@@ -222,3 +304,43 @@
size -= PAGE_SIZE;
}
}
+
+PRIVATE void protect_range(base, size, bits, mask)
+u32_t base;
+u32_t size;
+u32_t bits;
+u32_t mask;
+{
+ u32_t curr_pt, curr_pt_addr, entry;
+ int dir_ent, pt_ent;
+
+ /* Keep track of current page table */
+ curr_pt = -1;
+ curr_pt_addr = 0;
+ while (size != 0)
+ {
+ /* Compute location in page directory and table */
+ dir_ent = (base >> I386_VM_DIR_ENT_SHIFT);
+ pt_ent = (base >> I386_VM_PT_ENT_SHIFT) & I386_VM_PT_ENT_MASK;
+
+ if (dir_ent != curr_pt)
+ {
+ /* Get address of page table */
+ curr_pt = dir_ent;
+ curr_pt_addr = phys_get32(vm_cr3 + dir_ent * I386_VM_PT_ENT_SIZE);
+ curr_pt_addr &= I386_VM_ADDR_MASK;
+ }
+
+ /* Change protection bits */
+ entry = phys_get32(curr_pt_addr + pt_ent * I386_VM_PT_ENT_SIZE);
+#ifdef DEBUG_MPROTECT
+ kprintf(" 0x%.8x: 0x%.8x -> 0x%.8x\n", base, entry, entry & ~mask | bits);
+#endif
+ entry = entry & ~mask | bits;
+ phys_put32(curr_pt_addr + pt_ent * I386_VM_PT_ENT_SIZE, entry);
+
+ /* Move on to next page */
+ base += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
diff -Naur /usr/src.release/kernel/system.c /usr/src/kernel/system.c
--- /usr/src.release/kernel/system.c 2008-03-07 20:21:36.000000000 +0000
+++ /usr/src/kernel/system.c 2008-02-27 20:30:36.000000000 +0000
@@ -163,6 +163,7 @@
map(SYS_MEMSET, do_memset); /* write char to memory area */
map(SYS_VM_SETBUF, do_vm_setbuf); /* PM passes buffer for page tables */
map(SYS_VM_MAP, do_vm_map); /* Map/unmap physical (device) memory */
+ map(SYS_MPROTECT, do_mprotect); /* Change page access bits */

/* Copying. */
map(SYS_UMAP, do_umap); /* map virtual to physical address */
diff -Naur /usr/src.release/kernel/system.h /usr/src/kernel/system.h
--- /usr/src.release/kernel/system.h 2008-03-07 20:21:36.000000000 +0000
+++ /usr/src/kernel/system.h 2008-02-27 20:30:49.000000000 +0000
@@ -95,6 +95,7 @@

_PROTOTYPE( int do_vm_setbuf, (message *m_ptr) );
_PROTOTYPE( int do_vm_map, (message *m_ptr) );
+_PROTOTYPE( int do_mprotect, (message *m_ptr) );

_PROTOTYPE( int do_abort, (message *m_ptr) );
#if ! USE_ABORT
diff -Naur /usr/src.release/lib/posix/Makedepend-ack /usr/src/lib/posix/Makedepend-ack
--- /usr/src.release/lib/posix/Makedepend-ack 2008-03-07 20:21:44.000000000 +0000
+++ /usr/src/lib/posix/Makedepend-ack 2008-02-29 08:03:15.000000000 +0000
@@ -48,6 +48,7 @@
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _mkfifo.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _mknod.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _mount.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
+ mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _mprotect.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _open.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _opendir.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' _pathconf.c | sed -e 's:^\(.\):../obj-ack//./posix/\1:' >> .depend-ack
diff -Naur /usr/src.release/lib/posix/Makedepend-gnu /usr/src/lib/posix/Makedepend-gnu
--- /usr/src.release/lib/posix/Makedepend-gnu 2008-03-07 20:21:44.000000000 +0000
+++ /usr/src/lib/posix/Makedepend-gnu 2008-02-29 08:03:04.000000000 +0000
@@ -48,6 +48,7 @@
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _mkfifo.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _mknod.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _mount.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
+ mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _mprotect.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _open.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _opendir.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' _pathconf.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./posix/\1:' >> .depend-gnu
diff -Naur /usr/src.release/lib/posix/Makefile /usr/src/lib/posix/Makefile
--- /usr/src.release/lib/posix/Makefile 2008-03-07 20:21:44.000000000 +0000
+++ /usr/src/lib/posix/Makefile 2008-02-29 08:04:26.000000000 +0000
@@ -66,6 +66,7 @@
../obj-ack//libc.a: ../obj-ack//libc.a(_mkfifo.o)
../obj-ack//libc.a: ../obj-ack//libc.a(_mknod.o)
../obj-ack//libc.a: ../obj-ack//libc.a(_mount.o)
+../obj-ack//libc.a: ../obj-ack//libc.a(_mprotect.o)
../obj-ack//libc.a: ../obj-ack//libc.a(_open.o)
../obj-ack//libc.a: ../obj-ack//libc.a(_opendir.o)
../obj-ack//libc.a: ../obj-ack//libc.a(_pathconf.o)
@@ -215,6 +216,8 @@
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./posix/_mknod.o _mknod.c
../obj-ack//libc.a(_mount.o): _mount.c
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./posix/_mount.o _mount.c
+../obj-ack//libc.a(_mprotect.o): _mprotect.c
+ cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./posix/_mprotect.o _mprotect.c
../obj-ack//libc.a(_open.o): _open.c
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./posix/_open.o _open.c
../obj-ack//libc.a(_opendir.o): _opendir.c
@@ -365,6 +368,7 @@
../obj-gnu/libc.a: ../obj-gnu/./posix/_mkfifo.o
../obj-gnu/libc.a: ../obj-gnu/./posix/_mknod.o
../obj-gnu/libc.a: ../obj-gnu/./posix/_mount.o
+../obj-gnu/libc.a: ../obj-gnu/./posix/_mprotect.o
../obj-gnu/libc.a: ../obj-gnu/./posix/_open.o
../obj-gnu/libc.a: ../obj-gnu/./posix/_opendir.o
../obj-gnu/libc.a: ../obj-gnu/./posix/_pathconf.o
@@ -560,6 +564,9 @@
../obj-gnu/./posix/_mount.o: _mount.c
gcc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-gnu/./posix/_mount.o _mount.c

+../obj-gnu/./posix/_mprotect.o: _mprotect.c
+ gcc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-gnu/./posix/_mprotect.o _mprotect.c
+
../obj-gnu/./posix/_open.o: _open.c
gcc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-gnu/./posix/_open.o _open.c

diff -Naur /usr/src.release/lib/posix/Makefile.in /usr/src/lib/posix/Makefile.in
--- /usr/src.release/lib/posix/Makefile.in 2008-03-07 20:21:44.000000000 +0000
+++ /usr/src/lib/posix/Makefile.in 2008-02-29 08:03:30.000000000 +0000
@@ -52,6 +52,7 @@
_mkfifo.c \
_mknod.c \
_mount.c \
+ _mprotect.c \
_open.c \
_opendir.c \
_pathconf.c \
diff -Naur /usr/src.release/lib/posix/_mprotect.c /usr/src/lib/posix/_mprotect.c
--- /usr/src.release/lib/posix/_mprotect.c 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/lib/posix/_mprotect.c 2008-02-29 08:07:59.000000000 +0000
@@ -0,0 +1,17 @@
+#include
+#define mprotect _mprotect
+#include
+
+PUBLIC int mprotect(addr, len, prot)
+const void *addr;
+size_t len;
+int prot;
+{
+ message m;
+
+ m.m4_l1 = (long) addr;
+ m.m4_l2 = (long) len;
+ m.m4_l3 = (long) prot;
+
+ return(_syscall(MM, MPROTECT, &m));
+}
diff -Naur /usr/src.release/lib/syscall/Makedepend-ack /usr/src/lib/syscall/Makedepend-ack
--- /usr/src.release/lib/syscall/Makedepend-ack 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syscall/Makedepend-ack 2008-02-28 20:10:58.000000000 +0000
@@ -58,6 +58,7 @@
mkdep 'cc -E' mkfifo.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
mkdep 'cc -E' mknod.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
mkdep 'cc -E' mount.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
+ mkdep 'cc -E' mprotect.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
mkdep 'cc -E' open.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
mkdep 'cc -E' opendir.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
mkdep 'cc -E' pathconf.s | sed -e 's:^\(.\):../obj-ack//./syscall/\1:' >> .depend-ack
diff -Naur /usr/src.release/lib/syscall/Makedepend-gnu /usr/src/lib/syscall/Makedepend-gnu
--- /usr/src.release/lib/syscall/Makedepend-gnu 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syscall/Makedepend-gnu 2008-02-28 20:10:53.000000000 +0000
@@ -58,6 +58,7 @@
mkdep 'gcc -E -x assembler-with-cpp -I.' mkfifo.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
mkdep 'gcc -E -x assembler-with-cpp -I.' mknod.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
mkdep 'gcc -E -x assembler-with-cpp -I.' mount.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
+ mkdep 'gcc -E -x assembler-with-cpp -I.' mprotect.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
mkdep 'gcc -E -x assembler-with-cpp -I.' open.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
mkdep 'gcc -E -x assembler-with-cpp -I.' opendir.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
mkdep 'gcc -E -x assembler-with-cpp -I.' pathconf.s | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syscall/\1:' >> .depend-gnu
diff -Naur /usr/src.release/lib/syscall/Makefile /usr/src/lib/syscall/Makefile
--- /usr/src.release/lib/syscall/Makefile 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syscall/Makefile 2008-02-28 20:10:47.000000000 +0000
@@ -76,6 +76,7 @@
../obj-ack//libc.a: ../obj-ack//libc.a(mkfifo.o)
../obj-ack//libc.a: ../obj-ack//libc.a(mknod.o)
../obj-ack//libc.a: ../obj-ack//libc.a(mount.o)
+../obj-ack//libc.a: ../obj-ack//libc.a(mprotect.o)
../obj-ack//libc.a: ../obj-ack//libc.a(open.o)
../obj-ack//libc.a: ../obj-ack//libc.a(opendir.o)
../obj-ack//libc.a: ../obj-ack//libc.a(pathconf.o)
@@ -246,6 +247,8 @@
cc -c -o ../obj-ack//./syscall/mknod.o mknod.s
../obj-ack//libc.a(mount.o): mount.s
cc -c -o ../obj-ack//./syscall/mount.o mount.s
+../obj-ack//libc.a(mprotect.o): mprotect.s
+ cc -c -o ../obj-ack//./syscall/mprotect.o mprotect.s
../obj-ack//libc.a(open.o): open.s
cc -c -o ../obj-ack//./syscall/open.o open.s
../obj-ack//libc.a(opendir.o): opendir.s
@@ -408,6 +411,7 @@
../obj-gnu/libc.a: ../obj-gnu/./syscall/mkfifo.o
../obj-gnu/libc.a: ../obj-gnu/./syscall/mknod.o
../obj-gnu/libc.a: ../obj-gnu/./syscall/mount.o
+../obj-gnu/libc.a: ../obj-gnu/./syscall/mprotect.o
../obj-gnu/libc.a: ../obj-gnu/./syscall/open.o
../obj-gnu/libc.a: ../obj-gnu/./syscall/opendir.o
../obj-gnu/libc.a: ../obj-gnu/./syscall/pathconf.o
@@ -691,6 +695,10 @@
gcc -E -x assembler-with-cpp -I. mount.s | asmconv -mi386 ack gnu > ../obj-gnu/./syscall/mount.s.gnu || true
gas -o ../obj-gnu/./syscall/mount.o ../obj-gnu/./syscall/mount.s.gnu

+../obj-gnu/./syscall/mprotect.o: mprotect.s
+ gcc -E -x assembler-with-cpp -I. mprotect.s | asmconv -mi386 ack gnu > ../obj-gnu/./syscall/mprotect.s.gnu || true
+ gas -o ../obj-gnu/./syscall/mprotect.o ../obj-gnu/./syscall/mprotect.s.gnu
+
../obj-gnu/./syscall/open.o: open.s
gcc -E -x assembler-with-cpp -I. open.s | asmconv -mi386 ack gnu > ../obj-gnu/./syscall/open.s.gnu || true
gas -o ../obj-gnu/./syscall/open.o ../obj-gnu/./syscall/open.s.gnu
diff -Naur /usr/src.release/lib/syscall/Makefile.in /usr/src/lib/syscall/Makefile.in
--- /usr/src.release/lib/syscall/Makefile.in 2008-03-07 20:21:45.000000000 +0000
+++ /usr/src/lib/syscall/Makefile.in 2008-02-28 20:10:40.000000000 +0000
@@ -60,6 +60,7 @@
mkfifo.s \
mknod.s \
mount.s \
+ mprotect.s \
open.s \
opendir.s \
pathconf.s \
diff -Naur /usr/src.release/lib/syscall/mprotect.s /usr/src/lib/syscall/mprotect.s
--- /usr/src.release/lib/syscall/mprotect.s 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/lib/syscall/mprotect.s 2008-02-28 20:07:24.000000000 +0000
@@ -0,0 +1,7 @@
+.sect .text
+.extern __mprotect
+.define _mprotect
+.align 2
+
+_mprotect:
+ jmp __mprotect
diff -Naur /usr/src.release/lib/syslib/Makedepend-ack /usr/src/lib/syslib/Makedepend-ack
--- /usr/src.release/lib/syslib/Makedepend-ack 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syslib/Makedepend-ack 2008-02-28 16:18:58.000000000 +0000
@@ -32,6 +32,7 @@
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_irqctl.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_kill.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_memset.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
+ mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_mprotect.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_newmap.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_nice.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
mkdep 'cc -O -D_MINIX -D_POSIX_SOURCE -E' sys_out.c | sed -e 's:^\(.\):../obj-ack//./syslib/\1:' >> .depend-ack
diff -Naur /usr/src.release/lib/syslib/Makedepend-gnu /usr/src/lib/syslib/Makedepend-gnu
--- /usr/src.release/lib/syslib/Makedepend-gnu 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syslib/Makedepend-gnu 2008-02-28 16:20:32.000000000 +0000
@@ -32,6 +32,7 @@
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_irqctl.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_kill.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_memset.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
+ mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_mprotect.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_newmap.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_nice.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
mkdep 'gcc -O -D_MINIX -D_POSIX_SOURCE -E' sys_out.c | sed -e '//d' -e '//d' -e 's:^\(.\):../obj-gnu/./syslib/\1:' >> .depend-gnu
diff -Naur /usr/src.release/lib/syslib/Makefile /usr/src/lib/syslib/Makefile
--- /usr/src.release/lib/syslib/Makefile 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syslib/Makefile 2008-02-28 16:20:10.000000000 +0000
@@ -50,6 +50,7 @@
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_irqctl.o)
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_kill.o)
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_memset.o)
+../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_mprotect.o)
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_newmap.o)
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_nice.o)
../obj-ack//libsys.a: ../obj-ack//libsys.a(sys_out.o)
@@ -140,6 +141,8 @@
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./syslib/sys_kill.o sys_kill.c
../obj-ack//libsys.a(sys_memset.o): sys_memset.c
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./syslib/sys_memset.o sys_memset.c
+../obj-ack//libsys.a(sys_mprotect.o): sys_mprotect.c
+ cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./syslib/sys_mprotect.o sys_mprotect.c
../obj-ack//libsys.a(sys_newmap.o): sys_newmap.c
cc -O -D_MINIX -D_POSIX_SOURCE -c -o ../obj-ack//./syslib/sys_newmap.o sys_newmap.c
../obj-ack//libsys.a(sys_nice.o): sys_nice.c
diff -Naur /usr/src.release/lib/syslib/Makefile.in /usr/src/lib/syslib/Makefile.in
--- /usr/src.release/lib/syslib/Makefile.in 2008-03-07 20:21:46.000000000 +0000
+++ /usr/src/lib/syslib/Makefile.in 2008-02-28 16:20:32.000000000 +0000
@@ -36,6 +36,7 @@
sys_irqctl.c \
sys_kill.c \
sys_memset.c \
+ sys_mprotect.c \
sys_newmap.c \
sys_nice.c \
sys_out.c \
diff -Naur /usr/src.release/lib/syslib/sys_mprotect.c /usr/src/lib/syslib/sys_mprotect.c
--- /usr/src.release/lib/syslib/sys_mprotect.c 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/lib/syslib/sys_mprotect.c 2008-03-02 19:47:17.000000000 +0000
@@ -0,0 +1,25 @@
+#include "syslib.h"
+
+/*================================================= ==========================*
+ * sys_mprotect *
+ *================================================= ==========================*/
+PUBLIC int sys_mprotect(proc_nr, base, size, bits, mask)
+int proc_nr;
+phys_bytes base;
+phys_bytes size;
+unsigned long bits;
+unsigned long mask;
+{
+ message m;
+ int result;
+
+ m.m4_l1 = proc_nr;
+ m.m4_l2 = base;
+ m.m4_l3 = size;
+ m.m4_l4 = bits;
+ m.m4_l5 = mask;
+
+ result = _taskcall(SYSTASK, SYS_MPROTECT, &m);
+ return(result);
+}
+
diff -Naur /usr/src.release/mprotect-test.c /usr/src/mprotect-test.c
--- /usr/src.release/mprotect-test.c 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/mprotect-test.c 2008-03-02 20:13:14.000000000 +0000
@@ -0,0 +1,335 @@
+#define _POSIX_SOURCE 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* PAGE_SIZE not defined in Linux; normally fixed for x86 anyways */
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+/* specify protection as string */
+#define TEST_ALL(prot, data, page_count) test_all(prot, data, page_count, #prot)
+
+/* max test size: 16 pages; reserve 31 pages for plenty of space */
+#define TEST_PAGE_COUNT 31
+
+static int error_allowed_ENOMEM;
+static int error_allowed_ENOTSUP;
+static int error_allowed_EINVAL;
+static int error_required;
+
+static void print_expectation(void);
+static int template_function(int x, int y);
+static void test_all(int prot, char *data, int page_count, const char *s_prot);
+static int test_mprotect(void *base, size_t len, int prot);
+static void test_segv(void (*func)(char *), const char *name, char *data, int byte_count, int expect_segv, int require_segv);
+static void test_segv_exec(char *data);
+static void test_segv_handler(int signum);
+static void test_segv_read(char *data);
+static void test_segv_write(char *data);
+static void test_usr1_handler(int signum);
+static void test_with_settings(int prot, char *data, int byte_count, const char *s_prot);
+
+static void print_expectation(void)
+{
+ int i = 0;
+
+ printf("Expecting one of: ");
+
+ if (!error_required)
+ {
+ if (i++) printf(", ");
+ printf("success");
+ }
+
+ if (error_allowed_ENOMEM)
+ {
+ if (i++) printf(", ");
+ printf("failure with ENOMEM");
+ }
+
+ if (error_allowed_ENOTSUP)
+ {
+ if (i++) printf(", ");
+ printf("failure with ENOTSUP");
+ }
+
+ if (error_allowed_EINVAL)
+ {
+ if (i++) printf(", ");
+ printf("failure with EINVAL");
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static int template_function(int x, int y)
+{
+ return x * x + y * y;
+}
+
+static void test_all(int prot, char *data, int page_count, const char *s_prot)
+{
+ int byte_count, flag, i;
+ char s_prot_temp[256];
+
+ /* test with different offsets from page - should succeed only if byte_count_change == 0 */
+ byte_count = page_count * PAGE_SIZE;
+ for (i = -1; i <= 1; i++)
+ {
+ if (i) error_required++;
+ if (i) error_allowed_EINVAL++;
+
+ test_with_settings(prot, data + i, byte_count, s_prot);
+ test_with_settings(prot, data + i, byte_count + 1, s_prot);
+
+ if (i) error_allowed_EINVAL--;
+ if (i) error_required--;
+ }
+
+ /* test with invalid flags */
+ flag = 1;
+ while (flag)
+ {
+ sprintf(s_prot_temp, "%s | %d", s_prot, flag);
+ if (!(flag & (PROT_EXEC | PROT_WRITE | PROT_READ)))
+ {
+ error_allowed_EINVAL++;
+ test_with_settings(prot | flag, data, byte_count, s_prot_temp);
+ error_allowed_EINVAL--;
+ }
+
+ flag <<= 1;
+ }
+}
+
+static int test_mprotect(void *base, size_t len, int prot)
+{
+ int result;
+
+ printf("mprotect(0x%x, 0x%x, 0x%x)\n", base, len, prot);
+ fflush(stdout);
+
+ result = mprotect(base, len, prot);
+
+ if (result != 0 && result != -1)
+ {
+ printf("ERROR: unexpected return value %d\n", result);
+ return result;
+ }
+ else if (!result)
+ printf("%s: success\n", error_required ? "ERROR" : "As expected");
+ else if (errno == EINVAL)
+ printf("%s: failure with errno EINVAL\n", error_allowed_EINVAL ? "As expected" : "ERROR");
+ else if (errno == ENOMEM)
+ printf("%s: failure with errno ENOMEM\n", error_allowed_ENOMEM ? "As expected" : "ERROR");
+ else if (errno == ENOTSUP)
+ printf("%s: failure with errno ENOTSUP\n", error_allowed_ENOTSUP ? "As expected" : "ERROR");
+ else
+ printf("ERROR: failure with errno %d\n", errno);
+ fflush(stdout);
+
+ return result;
+}
+
+static jmp_buf test_segv_jmp_buf;
+static void test_segv(void (*func)(char *), const char *name, char *data, int byte_count, int expect_segv, int require_segv)
+{
+ int i, have_segv;
+
+ /* anything to do? */
+ if (!byte_count)
+ return;
+
+ /* print expectation */
+ printf("Testing access %s\n", name);
+ if (require_segv)
+ printf("Expecting signal SIGSEGV\n");
+ else if (expect_segv)
+ printf("Expecting signal SIGSEGV or success\n");
+ else
+ printf("Expecting success\n");
+ fflush(stdout);
+
+ /* test page by page */
+ for (i = 0; i < byte_count; i += PAGE_SIZE)
+ {
+ printf("Testing page %d\n", i / PAGE_SIZE);
+ fflush(stdout);
+
+ /* attempt to provoke SIGSEGV in func */
+ if (!setjmp(test_segv_jmp_buf))
+ {
+ signal(SIGSEGV, test_segv_handler);
+ func(data + i);
+ signal(SIGSEGV, NULL);
+ have_segv = 0;
+ }
+ else
+ have_segv = 1;
+
+ /* print test result */
+ if (have_segv)
+ if (expect_segv)
+ printf("As expected: got SIGSEGV on page %d\n", i / PAGE_SIZE);
+ else
+ printf("ERROR: got unexpected SIGSEGV on page %d\n", i / PAGE_SIZE);
+ else
+ if (require_segv)
+ printf("ERROR: did not get expected SIGSEGV on page %d\n", i / PAGE_SIZE);
+ else
+ printf("As expected: did not get SIGSEGV on page %d\n", i / PAGE_SIZE);
+ fflush(stdout);
+ }
+}
+
+static void test_segv_exec(char *data)
+{
+ int result, x = 0x1234, y = 0x5678;
+
+ result = ((int (*)(int, int)) data)(x, y);
+ if (result != x * x + y * y)
+ {
+ printf("ERROR: unexpected function result %d\n", result);
+ fflush(stdout);
+ }
+}
+
+static void test_segv_handler(int signum)
+{
+ longjmp(test_segv_jmp_buf, -1);
+}
+
+static void test_segv_read(char *data)
+{
+ char buffer[PAGE_SIZE];
+
+ memcpy(buffer, data, PAGE_SIZE);
+}
+
+static void test_segv_write(char *data)
+{
+ memcpy(data, template_function, PAGE_SIZE);
+}
+
+static void test_usr1_handler(int signum)
+{
+ void **frame, **frame_next;
+
+ /* dump stack */
+ frame_next = (void **) &signum - 2;
+ do
+ {
+ frame = frame_next;
+ fprintf(stderr, "EBP: 0x%.8x; EIP: 0x%.8x; Params: 0x%.8x 0x%.8x 0x%.8x\n",
+ frame[0], frame[1], frame[2], frame[3], frame[4]);
+
+ frame_next = (void **) frame[0];
+ } while (frame_next > frame);
+
+ /* re-register signal handler */
+ signal(SIGUSR1, test_usr1_handler);
+}
+
+static void test_with_settings(int prot, char *data, int byte_count, const char *s_prot)
+{
+ int result;
+
+ if (prot & ~(PROT_READ | PROT_WRITE)) error_allowed_ENOTSUP++;
+ if (!byte_count) error_allowed_EINVAL++;
+
+ printf("Protection: %s; byte count: %d (%d pages and %d bytes)\n",
+ s_prot, byte_count, byte_count / PAGE_SIZE, byte_count % PAGE_SIZE);
+ print_expectation();
+ fflush(stdout);
+
+ result = test_mprotect(data, byte_count, prot);
+
+ if (!byte_count) error_allowed_EINVAL--;
+ if (prot & ~(PROT_READ | PROT_WRITE)) error_allowed_ENOTSUP--;
+
+ if (result)
+ return;
+
+ test_segv(test_segv_read, "READ", data, byte_count, !(prot & PROT_READ), prot == PROT_NONE);
+ test_segv(test_segv_write, "WRITE", data, byte_count, !(prot & PROT_WRITE), !(prot & PROT_WRITE));
+ /*
+ test_segv(test_segv_exec, "EXEC", data, byte_count, !(prot & PROT_EXEC), prot == PROT_NONE);
+ */
+}
+
+int main(int argc, char **argv)
+{
+ static char test_data_unaligned[(TEST_PAGE_COUNT + 1) * PAGE_SIZE];
+
+ int i, page_count;
+ unsigned long stack_top;
+ char *invalid_ptr;
+ size_t invalid_len;
+ char *test_data;
+
+ /* initialize test_data by setting each page to template_function */
+ test_data = test_data_unaligned;
+ test_data += PAGE_SIZE - (long) test_data % PAGE_SIZE;
+ for (i = 0; i < TEST_PAGE_COUNT; i++)
+ memcpy(test_data + i * PAGE_SIZE, template_function, PAGE_SIZE);
+
+ /* SIGUSR1 dumps stack */
+ signal(SIGUSR1, test_usr1_handler);
+
+ /* test for 0, 1, 2, 4, 8 and 16 pages */
+ page_count = 0;
+ while (page_count <= TEST_PAGE_COUNT)
+ {
+ /* test for each protection level */
+ TEST_ALL(PROT_NONE, test_data, page_count);
+ TEST_ALL(PROT_READ, test_data, page_count);
+ TEST_ALL(PROT_WRITE, test_data, page_count);
+ TEST_ALL(PROT_READ | PROT_WRITE, test_data, page_count);
+ TEST_ALL(PROT_EXEC, test_data, page_count);
+ TEST_ALL(PROT_EXEC | PROT_READ, test_data, page_count);
+ TEST_ALL(PROT_EXEC | PROT_WRITE, test_data, page_count);
+ TEST_ALL(PROT_EXEC | PROT_READ | PROT_WRITE, test_data, page_count);
+ page_count = page_count ? 2 * page_count : 1;
+ }
+
+ /* test outside acceptable memory region */
+ error_required++;
+ error_allowed_ENOMEM++;
+
+ stack_top = (unsigned long) &argv;
+ stack_top = stack_top + PAGE_SIZE - stack_top % PAGE_SIZE;
+ invalid_ptr = (char *) (stack_top + PAGE_SIZE * 16);
+ test_with_settings(PROT_NONE, invalid_ptr, PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) 0x4000000, PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) 0x8000000 - PAGE_SIZE, PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) 0x8000000 - PAGE_SIZE, 2 * PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) 0x8000000, PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) 0xc000000, PAGE_SIZE, "PROT_NONE");
+ error_allowed_EINVAL++;
+ test_with_settings(PROT_NONE, (char *) NULL - PAGE_SIZE, PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, (char *) NULL - PAGE_SIZE, 2 * PAGE_SIZE, "PROT_NONE");
+ error_allowed_EINVAL--;
+
+ /* test outside acceptable length */
+ invalid_len = stack_top - (size_t) test_data + PAGE_SIZE * 16;
+ test_with_settings(PROT_NONE, test_data, invalid_len, "PROT_NONE");
+ test_with_settings(PROT_NONE, test_data, 0x80000000, "PROT_NONE");
+ error_allowed_EINVAL++;
+ test_with_settings(PROT_NONE, test_data, -PAGE_SIZE, "PROT_NONE");
+ test_with_settings(PROT_NONE, test_data, 0, "PROT_NONE");
+ error_allowed_EINVAL--;
+
+ error_allowed_ENOMEM--;
+ error_required--;
+
+ return 0;
+}
diff -Naur /usr/src.release/servers/fs/table.c /usr/src/servers/fs/table.c
--- /usr/src.release/servers/fs/table.c 2008-03-07 20:21:52.000000000 +0000
+++ /usr/src/servers/fs/table.c 2008-02-27 19:14:11.000000000 +0000
@@ -112,6 +112,7 @@
no_sys, /* 92 = setegid */
do_truncate, /* 93 = truncate */
do_ftruncate, /* 94 = truncate */
+ no_sys, /* 95 = mprotect */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];
diff -Naur /usr/src.release/servers/pm/Makefile /usr/src/servers/pm/Makefile
--- /usr/src.release/servers/pm/Makefile 2008-03-07 20:21:53.000000000 +0000
+++ /usr/src/servers/pm/Makefile 2008-02-27 19:16:53.000000000 +0000
@@ -14,7 +14,7 @@
LDFLAGS = -i

OBJ = main.o forkexit.o break.o exec.o time.o timers.o \
- signal.o alloc.o utility.o table.o trace.o getset.o misc.o
+ signal.o alloc.o utility.o table.o trace.o getset.o misc.o mprotect.o

# build local binary
all build: $(SERVER)
diff -Naur /usr/src.release/servers/pm/mprotect.c /usr/src/servers/pm/mprotect.c
--- /usr/src.release/servers/pm/mprotect.c 1970-01-01 00:00:00.000000000 +0000
+++ /usr/src/servers/pm/mprotect.c 2008-02-29 10:17:31.000000000 +0000
@@ -0,0 +1,60 @@
+/*
+ * This file deals with memory protection.
+ *
+ * The entry point into this file is:
+ * do_mprotect: perform the MPROTECT system call
+ */
+
+#include "pm.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include "mproc.h"
+#include "param.h"
+
+/*
+ * page table access masks; see Intel Architecture Software Developer's manual,
+ * volume 3, section 11.3.4, "Page-Table Entries"
+ */
+#define I386_VM_MASK_ACCESS (I386_VM_WRITE | I386_VM_USER)
+
+/*================================================= ==========================*
+ * do_mprotect *
+ *================================================= ==========================*/
+PUBLIC int do_mprotect(void)
+{
+ unsigned long address, len, prot, pt_bits;
+ int s;
+
+ /* check arguments */
+ address = (unsigned long) m_in.m4_l1;
+ if (address % PAGE_SIZE) /* address must be multiple of page size */
+ return EINVAL;
+
+ len = (unsigned long) m_in.m4_l2;
+ if (len % PAGE_SIZE) /* round up to next page boundary */
+ len = len + PAGE_SIZE - len % PAGE_SIZE;
+
+ if (len <= 0 || /* length must be positive */
+ address + len < address) /* address must not overflow */
+ return EINVAL;
+
+ prot = (unsigned long) m_in.m4_l3;
+ if (prot & ~(PROT_EXEC | PROT_WRITE | PROT_READ)) /* only known flags */
+ return EINVAL;
+
+ /* convert prot to page table protection mask */
+ pt_bits =
+ ((prot & PROT_WRITE) ? I386_VM_WRITE : 0) |
+ ((prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) ? I386_VM_USER : 0);
+
+ /* kernel will perform the mprotect by changing page permissions */
+ s = sys_mprotect(who_e, address, len, pt_bits, I386_VM_MASK_ACCESS);
+ if (s != OK && s != ENOMEM)
+ panic(__FILE__, "sys_mprotect failed", s);
+
+ return s;
+}
diff -Naur /usr/src.release/servers/pm/proto.h /usr/src/servers/pm/proto.h
--- /usr/src.release/servers/pm/proto.h 2008-03-07 20:21:53.000000000 +0000
+++ /usr/src/servers/pm/proto.h 2008-02-27 19:16:19.000000000 +0000
@@ -117,3 +117,5 @@
_PROTOTYPE( int proc_from_pid, (pid_t p));
_PROTOTYPE( int pm_isokendpt, (int ep, int *proc));

+/* mprotect.c */
+_PROTOTYPE( int do_mprotect, (void) );
diff -Naur /usr/src.release/servers/pm/table.c /usr/src/servers/pm/table.c
--- /usr/src.release/servers/pm/table.c 2008-03-07 20:21:53.000000000 +0000
+++ /usr/src/servers/pm/table.c 2008-02-27 19:14:33.000000000 +0000
@@ -110,6 +110,7 @@
do_getset, /* 92 = setegid */
no_sys, /* 93 = truncate */
no_sys, /* 94 = ftruncate */
+ do_mprotect, /* 95 = mprotect */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];