Changed do_devio not to require DIO_TYPE, but to extract type

from DIO_REQUEST. Also do_vdevio. Also do_sdevio, but this
function also supports grant id's and offsets.

do_segctl: rename protected to prot.

do_umap: support for GRANT_SEG umap.

do_privctl: support SYS_PRIV_SET_GRANTS, which sets location and size
of in-own-address-space grant table.

do_safecopy: functions to verify and perform 'safe' (grant-based) copies.
This commit is contained in:
Ben Gras 2006-06-20 10:03:10 +00:00
parent ada6592af9
commit 3061d7b17a
8 changed files with 345 additions and 37 deletions

View file

@ -37,6 +37,7 @@ OBJECTS = \
$(SYSTEM)(do_memset.o) \
$(SYSTEM)(do_privctl.o) \
$(SYSTEM)(do_segctl.o) \
$(SYSTEM)(do_safecopy.o) \
$(SYSTEM)(do_getksig.o) \
$(SYSTEM)(do_endksig.o) \
$(SYSTEM)(do_kill.o) \
@ -139,6 +140,9 @@ $(SYSTEM)(do_abort.o): do_abort.c
$(SYSTEM)(do_privctl.o): do_privctl.c
$(CC) do_privctl.c
$(SYSTEM)(do_safecopy.o): do_safecopy.c
$(CC) do_safecopy.c
$(SYSTEM)(do_segctl.o): do_segctl.c
$(CC) do_segctl.c

View file

@ -3,7 +3,6 @@
*
* The parameters for this kernel call are:
* m2_i3: DIO_REQUEST (request input or output)
* m2_i1: DIO_TYPE (flag indicating byte, word, or long)
* m2_l1: DIO_PORT (port to read/ write)
* m2_l2: DIO_VALUE (value to write/ return value read)
*/
@ -25,6 +24,10 @@ register message *m_ptr; /* pointer to request message */
port_t port;
struct io_range *iorp;
int i, size, nr_io_range;
int io_type, io_dir;
io_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
io_dir = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
rp= proc_addr(who_p);
privp= priv(rp);
@ -35,11 +38,11 @@ register message *m_ptr; /* pointer to request message */
}
if (privp->s_flags & CHECK_IO_PORT)
{
switch (m_ptr->DIO_TYPE)
switch (io_type)
{
case DIO_BYTE: size= 1; break;
case DIO_WORD: size= 2; break;
case DIO_LONG: size= 4; break;
case _DIO_BYTE: size= 1; break;
case _DIO_WORD: size= 2; break;
case _DIO_LONG: size= 4; break;
default: size= 4; break; /* Be conservative */
}
port= m_ptr->DIO_PORT;
@ -61,18 +64,18 @@ register message *m_ptr; /* pointer to request message */
doit:
/* Process a single I/O request for byte, word, and long values. */
if (m_ptr->DIO_REQUEST == DIO_INPUT) {
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: m_ptr->DIO_VALUE = inb(m_ptr->DIO_PORT); break;
case DIO_WORD: m_ptr->DIO_VALUE = inw(m_ptr->DIO_PORT); break;
case DIO_LONG: m_ptr->DIO_VALUE = inl(m_ptr->DIO_PORT); break;
if (io_dir == _DIO_INPUT) {
switch (io_type) {
case _DIO_BYTE: m_ptr->DIO_VALUE = inb(m_ptr->DIO_PORT); break;
case _DIO_WORD: m_ptr->DIO_VALUE = inw(m_ptr->DIO_PORT); break;
case _DIO_LONG: m_ptr->DIO_VALUE = inl(m_ptr->DIO_PORT); break;
default: return(EINVAL);
}
} else {
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: outb(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
case DIO_WORD: outw(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
case DIO_LONG: outl(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
switch (io_type) {
case _DIO_BYTE: outb(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
case _DIO_WORD: outw(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
case _DIO_LONG: outl(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;
default: return(EINVAL);
}
}

View file

@ -40,7 +40,8 @@ message *m_ptr; /* pointer to request message */
*/
caller_ptr = proc_addr(who_p);
if (! (priv(caller_ptr)->s_flags & SYS_PROC)) return(EPERM);
if(!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
if(m_ptr->PR_ENDPT == SELF) proc_nr = who_p;
else if(!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
rp = proc_addr(proc_nr);
switch(m_ptr->CTL_REQUEST)
@ -79,10 +80,12 @@ message *m_ptr; /* pointer to request message */
}
}
/* No I/O resources, no memory resources, no IRQs */
/* No I/O resources, no memory resources, no IRQs, no grant table */
priv(rp)->s_nr_io_range= 0;
priv(rp)->s_nr_mem_range= 0;
priv(rp)->s_nr_irq= 0;
priv(rp)->s_grant_table= 0;
priv(rp)->s_grant_entries= 0;
/* Done. Privileges have been set. Allow process to run again. */
old_flags = rp->p_rts_flags; /* save value of the flags */
@ -160,6 +163,11 @@ message *m_ptr; /* pointer to request message */
priv(rp)->s_nr_irq++;
return OK;
case SYS_PRIV_SET_GRANTS:
if ((rp->p_rts_flags & NO_PRIV) || !(priv(rp))) return(EPERM);
_K_SET_GRANT_TABLE(rp,
(vir_bytes) m_ptr->CTL_ARG_PTR, m_ptr->CTL_MM_PRIV);
return OK;
default:
kprintf("do_privctl: bad request %d\n", m_ptr->CTL_REQUEST);

269
kernel/system/do_safecopy.c Normal file
View file

@ -0,0 +1,269 @@
/* The kernel call implemented in this file:
* m_type: SYS_SAFECOPYFROM or SYS_SAFECOPYTO
*
* The parameters for this kernel call are:
* SCP_FROM_TO other endpoint
* SCP_INFO encoded: caller's own src/dst segment
* SCP_GID grant id
* SCP_OFFSET offset within granted space
* SCP_ADDRESS address in own address space
* SCP_BYTES bytes to be copied
*/
#include "../system.h"
#include <minix/type.h>
#include <minix/safecopies.h>
#define MEM_TOP 0xFFFFFFFFUL
/*===========================================================================*
* verify_grant *
*===========================================================================*/
PUBLIC int verify_grant(granter, grantee, grant, bytes, access,
offset_in, offset_result, e_granter)
endpoint_t granter, grantee; /* copyee, copyer */
cp_grant_id_t grant; /* grant id */
vir_bytes bytes; /* copy size */
int access; /* direction (read/write) */
vir_bytes offset_in; /* copy offset within grant */
vir_bytes *offset_result; /* copy offset within virtual address space */
endpoint_t *e_granter; /* new granter (magic grants) */
{
static cp_grant_t g;
static int proc_nr;
static struct proc *granter_proc;
static phys_bytes phys_grant;
/* Get granter process slot (if valid), and check range of
* grant id.
*/
if(!isokendpt(granter, &proc_nr) || !GRANT_VALID(grant)) {
kprintf("grant verify failed: invalid granter or grant\n");
return(EINVAL);
}
granter_proc = proc_addr(proc_nr);
/* If there is no priv. structure, or no grant table in the
* priv. structure, or the grant table in the priv. structure
* is too small for the grant,
*
* then there exists no such grant, so
*
* return EPERM.
*
* (Don't leak how big the grant table is by returning
* EINVAL for grant-out-of-range, in case this turns out to be
* interesting information.)
*/
if((granter_proc->p_rts_flags & NO_PRIV) || !(priv(granter_proc)) ||
priv(granter_proc)->s_grant_table < 1) {
kprintf("grant verify failed in ep %d proc %d: "
"no priv table, or no grant table\n",
granter, proc_nr);
return(EPERM);
}
if(priv(granter_proc)->s_grant_entries <= grant) {
kprintf("grant verify failed in ep %d proc %d: "
"grant %d out of range for table size %d\n",
granter, proc_nr, grant, priv(granter_proc)->s_grant_entries);
return(EPERM);
}
/* Copy the grant entry corresponding to this id to see what it
* looks like. If it fails, hide the fact that granter has
* (presumably) set an invalid grant table entry by returning
* EPERM, just like with an invalid grant id.
*/
if(!(phys_grant = umap_local(granter_proc, D,
priv(granter_proc)->s_grant_table + sizeof(g)*grant, sizeof(g)))) {
kprintf("grant verify failed: umap failed\n");
return EPERM;
}
phys_copy(phys_grant, vir2phys(&g), sizeof(g));
/* Check validity. */
if(!(g.cp_flags & CPF_USED)) {
kprintf("grant verify failed: invalid\n");
return EPERM;
}
/* Check access of grant. */
if(((g.cp_flags & access) != access)) {
kprintf("grant verify failed: access invalid; want %x, have %x\n",
access, g.cp_flags);
return EPERM;
}
if((g.cp_flags & CPF_DIRECT)) {
/* Don't fiddle around with grants that wrap, arithmetic
* below may be confused.
*/
if(MEM_TOP - g.cp_u.cp_direct.cp_len <
g.cp_u.cp_direct.cp_start - 1) {
kprintf("direct grant verify failed: len too long\n");
return EPERM;
}
/* Verify actual grantee. */
if(g.cp_u.cp_direct.cp_who_to != grantee && grantee != ANY) {
kprintf("direct grant verify failed: bad grantee\n");
return EPERM;
}
/* Verify actual copy range. */
if((offset_in+bytes < offset_in) ||
offset_in+bytes > g.cp_u.cp_direct.cp_len) {
kprintf("direct grant verify failed: bad size or range. "
"granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
g.cp_u.cp_direct.cp_len, g.cp_u.cp_direct.cp_start,
bytes, offset_in);
return EPERM;
}
/* Verify successful - tell caller what address it is. */
*offset_result = g.cp_u.cp_direct.cp_start + offset_in;
*e_granter = granter;
} else if(g.cp_flags & CPF_MAGIC) {
/* Currently, it is hardcoded that only FS may do
* magic grants.
*/
#if 0
kprintf("magic grant ..\n");
#endif
if(granter != FS_PROC_NR) {
kprintf("magic grant verify failed: granter (%d) "
"is not FS (%d)\n", granter, FS_PROC_NR);
return EPERM;
}
/* Don't fiddle around with grants that wrap, arithmetic
* below may be confused.
*/
if(MEM_TOP - g.cp_u.cp_magic.cp_len <
g.cp_u.cp_magic.cp_start - 1) {
kprintf("magic grant verify failed: len too long\n");
return EPERM;
}
/* Verify actual grantee. */
if(g.cp_u.cp_magic.cp_who_to != grantee && grantee != ANY) {
kprintf("magic grant verify failed: bad grantee\n");
return EPERM;
}
/* Verify actual copy range. */
if((offset_in+bytes < offset_in) ||
offset_in+bytes > g.cp_u.cp_magic.cp_len) {
kprintf("magic grant verify failed: bad size or range. "
"granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
g.cp_u.cp_magic.cp_len, g.cp_u.cp_magic.cp_start,
bytes, offset_in);
return EPERM;
}
/* Verify successful - tell caller what address it is. */
*offset_result = g.cp_u.cp_magic.cp_start + offset_in;
*e_granter = g.cp_u.cp_magic.cp_who_from;
} else {
kprintf("grant verify failed: unknown grant type\n");
return EPERM;
}
#if 0
kprintf("grant verify successful %d -> %d (grant %d): %d bytes\n",
granter, grantee, grant, bytes);
#endif
return OK;
}
/*===========================================================================*
* do_safecopy *
*===========================================================================*/
PUBLIC int do_safecopy(m_ptr)
register message *m_ptr; /* pointer to request message */
{
static endpoint_t granter, grantee, *src, *dst, new_granter;
static int access, r, src_seg, dst_seg;
static struct vir_addr v_src, v_dst;
static vir_bytes offset, bytes;
granter = m_ptr->SCP_FROM_TO;
grantee = who_e;
/* Set src and dst. One of these is always the caller (who_e),
* depending on whether the call was SYS_SAFECOPYFROM or
* SYS_SAFECOPYTO. The caller's seg is encoded in the SCP_INFO
* field.
*/
if(sys_call_code == SYS_SAFECOPYFROM) {
src = &granter;
dst = &grantee;
src_seg = D;
dst_seg = SCP_INFO2SEG(m_ptr->SCP_INFO);
access = CPF_READ;
} else if(sys_call_code == SYS_SAFECOPYTO) {
src = &grantee;
dst = &granter;
src_seg = SCP_INFO2SEG(m_ptr->SCP_INFO);
dst_seg = D;
access = CPF_WRITE;
} else panic("Impossible system call nr. ", sys_call_code);
#if 0
kprintf("safecopy: %d -> %d\n", *src, *dst);
#endif
bytes = m_ptr->SCP_BYTES;
/* Verify permission exists. */
if((r=verify_grant(granter, grantee, m_ptr->SCP_GID, bytes, access,
m_ptr->SCP_OFFSET, &offset, &new_granter)) != OK) {
kprintf("grant %d verify to copy %d->%d by %d failed: err %d\n",
m_ptr->SCP_GID, *src, *dst, grantee, r);
return r;
}
/* verify_grant() can redirect the grantee to someone else,
* meaning the source or destination changes.
*/
granter = new_granter;
#if 0
kprintf("verified safecopy: %d -> %d\n", *src, *dst);
#endif
/* Now it's a regular copy. */
v_src.segment = src_seg;
v_dst.segment = dst_seg;
v_src.proc_nr_e = *src;
v_dst.proc_nr_e = *dst;
/* Now the offset in virtual addressing is known in 'offset'.
* Depending on the call, this is the source or destination
* address.
*/
if(sys_call_code == SYS_SAFECOPYFROM) {
v_src.offset = offset;
v_dst.offset = (vir_bytes) m_ptr->SCP_ADDRESS;
} else {
v_src.offset = (vir_bytes) m_ptr->SCP_ADDRESS;
v_dst.offset = offset;
}
#if 0
kprintf("copy 0x%lx (%d) in %d to 0x%lx (%d) in %d (%d bytes)\n",
v_src.offset, v_src.segment, *src,
v_dst.offset, v_dst.segment, *dst,
bytes);
#endif
#if DEBUG_SAFE_COUNT
unsafe_copy_log(0,0);
#endif
/* Do the regular copy. */
return virtual_copy(&v_src, &v_dst, bytes);
}

View file

@ -26,6 +26,7 @@ register message *m_ptr; /* pointer to request message */
int count = m_ptr->DIO_VEC_SIZE;
long port = m_ptr->DIO_PORT;
phys_bytes phys_buf;
int req_type, req_dir;
/* Check if process endpoint is OK.
* A driver may directly provide a pointer to a buffer at the user-process
@ -38,21 +39,38 @@ register message *m_ptr; /* pointer to request message */
return(EINVAL);
if (iskerneln(proc_nr)) return(EPERM);
/* Get and check physical address. */
if ((phys_buf = numap_local(proc_nr, (vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0)
return(EFAULT);
/* Extract direction (in or out) and type (size). */
req_dir = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
req_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
/* Check for 'safe' variants. */
if((m_ptr->DIO_REQUEST & _DIO_SAFEMASK) == _DIO_SAFE) {
/* Map grant address to physical address. */
if ((phys_buf = umap_verify_grant(proc_addr(proc_nr), who_e,
(vir_bytes) m_ptr->DIO_VEC_ADDR,
(vir_bytes) m_ptr->DIO_OFFSET, count,
req_dir == _DIO_INPUT ? CPF_WRITE : CPF_READ)) == 0)
return(EPERM);
} else {
if(proc_nr != who_p)
kprintf("unsafe sdevio by %d in %d\n", who_e, proc_nr_e);
/* Get and check physical address. */
if ((phys_buf = numap_local(proc_nr,
(vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0)
return(EFAULT);
}
/* Perform device I/O for bytes and words. Longs are not supported. */
if (m_ptr->DIO_REQUEST == DIO_INPUT) {
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: phys_insb(port, phys_buf, count); break;
case DIO_WORD: phys_insw(port, phys_buf, count); break;
if (req_dir == _DIO_INPUT) {
switch (req_type) {
case _DIO_BYTE: phys_insb(port, phys_buf, count); break;
case _DIO_WORD: phys_insw(port, phys_buf, count); break;
default: return(EINVAL);
}
} else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) {
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: phys_outsb(port, phys_buf, count); break;
case DIO_WORD: phys_outsw(port, phys_buf, count); break;
} else if (req_dir == _DIO_OUTPUT) {
switch (req_type) {
case _DIO_BYTE: phys_outsb(port, phys_buf, count); break;
case _DIO_WORD: phys_outsw(port, phys_buf, count); break;
default: return(EINVAL);
}
}

View file

@ -44,7 +44,7 @@ register message *m_ptr; /* pointer to request message */
}
if (index < 0) return(ENOSPC);
if (! machine.protected) {
if (! machine.prot) {
selector = phys / HCLICK_SIZE;
offset = phys % HCLICK_SIZE;
result = OK;

View file

@ -46,6 +46,9 @@ register message *m_ptr; /* pointer to request message */
case BIOS_SEG:
phys_addr = umap_bios(proc_addr(proc_nr), offset, count);
break;
case GRANT_SEG:
phys_addr = umap_grant(proc_addr(proc_nr), offset, count);
break;
default:
return(EINVAL);
}

View file

@ -38,16 +38,19 @@ register message *m_ptr; /* pointer to request message */
vir_bytes caller_vir; /* virtual address at caller */
phys_bytes caller_phys; /* physical address at caller */
int i;
int io_dir, io_type;
/* Get the request, size of the request vector, and check the values. */
if (m_ptr->DIO_REQUEST == DIO_INPUT) io_in = TRUE;
else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) io_in = FALSE;
io_dir = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
io_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
if (io_dir == _DIO_INPUT) io_in = TRUE;
else if (io_dir == _DIO_OUTPUT) io_in = FALSE;
else return(EINVAL);
if ((vec_size = m_ptr->DIO_VEC_SIZE) <= 0) return(EINVAL);
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: bytes = vec_size * sizeof(pvb_pair_t); break;
case DIO_WORD: bytes = vec_size * sizeof(pvw_pair_t); break;
case DIO_LONG: bytes = vec_size * sizeof(pvl_pair_t); break;
switch (io_type) {
case _DIO_BYTE: bytes = vec_size * sizeof(pvb_pair_t); break;
case _DIO_WORD: bytes = vec_size * sizeof(pvw_pair_t); break;
case _DIO_LONG: bytes = vec_size * sizeof(pvl_pair_t); break;
default: return(EINVAL); /* check type once and for all */
}
if (bytes > sizeof(vdevio_buf)) return(E2BIG);
@ -63,12 +66,12 @@ register message *m_ptr; /* pointer to request message */
* batch from being interrupted.
*/
lock(13, "do_vdevio");
switch (m_ptr->DIO_TYPE) {
case DIO_BYTE: /* byte values */
switch (io_type) {
case _DIO_BYTE: /* byte values */
if (io_in) for (i=0; i<vec_size; i++) pvb[i].value = inb(pvb[i].port);
else for (i=0; i<vec_size; i++) outb(pvb[i].port, pvb[i].value);
break;
case DIO_WORD: /* word values */
case _DIO_WORD: /* word values */
if (io_in) for (i=0; i<vec_size; i++) pvw[i].value = inw(pvw[i].port);
else for (i=0; i<vec_size; i++) outw(pvw[i].port, pvw[i].value);
break;