From 3061d7b17a38dc875cc0aff8a36251de2217e359 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Tue, 20 Jun 2006 10:03:10 +0000 Subject: [PATCH] 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. --- kernel/system/Makefile | 4 + kernel/system/do_devio.c | 31 +++-- kernel/system/do_privctl.c | 12 +- kernel/system/do_safecopy.c | 269 ++++++++++++++++++++++++++++++++++++ kernel/system/do_sdevio.c | 40 ++++-- kernel/system/do_segctl.c | 2 +- kernel/system/do_umap.c | 3 + kernel/system/do_vdevio.c | 21 +-- 8 files changed, 345 insertions(+), 37 deletions(-) create mode 100644 kernel/system/do_safecopy.c diff --git a/kernel/system/Makefile b/kernel/system/Makefile index 8d18fbefa..2d8271650 100644 --- a/kernel/system/Makefile +++ b/kernel/system/Makefile @@ -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 diff --git a/kernel/system/do_devio.c b/kernel/system/do_devio.c index a1da1a274..9c98bba00 100644 --- a/kernel/system/do_devio.c +++ b/kernel/system/do_devio.c @@ -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); } } diff --git a/kernel/system/do_privctl.c b/kernel/system/do_privctl.c index e01ff2333..c567c8a26 100644 --- a/kernel/system/do_privctl.c +++ b/kernel/system/do_privctl.c @@ -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); diff --git a/kernel/system/do_safecopy.c b/kernel/system/do_safecopy.c new file mode 100644 index 000000000..154192809 --- /dev/null +++ b/kernel/system/do_safecopy.c @@ -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 +#include + +#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); +} + diff --git a/kernel/system/do_sdevio.c b/kernel/system/do_sdevio.c index 82a707f8e..dad932a23 100644 --- a/kernel/system/do_sdevio.c +++ b/kernel/system/do_sdevio.c @@ -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); } } diff --git a/kernel/system/do_segctl.c b/kernel/system/do_segctl.c index e1b5b5988..97e56937f 100644 --- a/kernel/system/do_segctl.c +++ b/kernel/system/do_segctl.c @@ -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; diff --git a/kernel/system/do_umap.c b/kernel/system/do_umap.c index 613549169..93331a7dd 100644 --- a/kernel/system/do_umap.c +++ b/kernel/system/do_umap.c @@ -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); } diff --git a/kernel/system/do_vdevio.c b/kernel/system/do_vdevio.c index 94792bf7b..ae3cb78c5 100644 --- a/kernel/system/do_vdevio.c +++ b/kernel/system/do_vdevio.c @@ -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