3061d7b17a
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.
269 lines
7.8 KiB
C
269 lines
7.8 KiB
C
/* 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);
|
|
}
|
|
|