Kernel: IPC filter support for VM memory requests
The filtering also exposed the risk that a process be killed or swapped while on the list of VM memory requests. These cases are now handled properly as well. Change-Id: Ibd3897b34abdf33bce19d37b8e5f65fbd0fd9316
This commit is contained in:
parent
8b0f8559ee
commit
3779ed93c3
4 changed files with 136 additions and 18 deletions
|
@ -110,6 +110,7 @@ void clear_ipc_filters(struct proc *rp);
|
||||||
int check_ipc_filter(struct ipc_filter_s *ipcf, int fill_flags);
|
int check_ipc_filter(struct ipc_filter_s *ipcf, int fill_flags);
|
||||||
int allow_ipc_filtered_msg(struct proc *rp, endpoint_t src_e,
|
int allow_ipc_filtered_msg(struct proc *rp, endpoint_t src_e,
|
||||||
vir_bytes m_src_v, message *m_src_p);
|
vir_bytes m_src_v, message *m_src_p);
|
||||||
|
int allow_ipc_filtered_memreq(struct proc *src_rp, struct proc *dst_rp);
|
||||||
int priv_add_irq(struct proc *rp, int irq);
|
int priv_add_irq(struct proc *rp, int irq);
|
||||||
int priv_add_io(struct proc *rp, struct io_range *ior);
|
int priv_add_io(struct proc *rp, struct io_range *ior);
|
||||||
int priv_add_mem(struct proc *rp, struct minix_mem_range *memr);
|
int priv_add_mem(struct proc *rp, struct minix_mem_range *memr);
|
||||||
|
|
|
@ -486,6 +486,27 @@ void send_diag_sig(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===========================================================================*
|
||||||
|
* clear_memreq *
|
||||||
|
*===========================================================================*/
|
||||||
|
static void clear_memreq(struct proc *rp)
|
||||||
|
{
|
||||||
|
struct proc **rpp;
|
||||||
|
|
||||||
|
if (!RTS_ISSET(rp, RTS_VMREQUEST))
|
||||||
|
return; /* nothing to do */
|
||||||
|
|
||||||
|
for (rpp = &vmrequest; *rpp != NULL;
|
||||||
|
rpp = &(*rpp)->p_vmrequest.nextrequestor) {
|
||||||
|
if (*rpp == rp) {
|
||||||
|
*rpp = rp->p_vmrequest.nextrequestor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RTS_UNSET(rp, RTS_VMREQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* clear_ipc *
|
* clear_ipc *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
@ -548,6 +569,10 @@ register struct proc *rc; /* slot of process to clean up */
|
||||||
*/
|
*/
|
||||||
clear_ipc_refs(rc, EDEADSRCDST);
|
clear_ipc_refs(rc, EDEADSRCDST);
|
||||||
|
|
||||||
|
/* Finally, if the process was blocked on a VM request, remove it from the
|
||||||
|
* queue of processes waiting to be processed by VM.
|
||||||
|
*/
|
||||||
|
clear_memreq(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
|
@ -736,6 +761,14 @@ void clear_ipc_filters(struct proc *rp)
|
||||||
}
|
}
|
||||||
|
|
||||||
priv(rp)->s_ipcf = NULL;
|
priv(rp)->s_ipcf = NULL;
|
||||||
|
|
||||||
|
/* VM is a special case here: since the cleared IPC filter may have
|
||||||
|
* blocked memory handling requests, we may now have to tell VM that
|
||||||
|
* there are "new" requests pending.
|
||||||
|
*/
|
||||||
|
if (rp->p_endpoint == VM_PROC_NR && vmrequest != NULL)
|
||||||
|
if (send_sig(VM_PROC_NR, SIGKMEM) != OK)
|
||||||
|
panic("send_sig failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
|
@ -841,6 +874,46 @@ int allow_ipc_filtered_msg(struct proc *rp, endpoint_t src_e,
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===========================================================================*
|
||||||
|
* allow_ipc_filtered_memreq *
|
||||||
|
*===========================================================================*/
|
||||||
|
int allow_ipc_filtered_memreq(struct proc *src_rp, struct proc *dst_rp)
|
||||||
|
{
|
||||||
|
/* Determine whether VM should receive a request to handle memory
|
||||||
|
* that is the result of process 'src_rp' trying to access currently
|
||||||
|
* unavailable memory in process 'dst_rp'. Return TRUE if VM should
|
||||||
|
* be given the request, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct proc *vmp;
|
||||||
|
message m_buf;
|
||||||
|
int allow_src, allow_dst;
|
||||||
|
|
||||||
|
vmp = proc_addr(VM_PROC_NR);
|
||||||
|
|
||||||
|
/* If VM has no filter in place, all requests should go through. */
|
||||||
|
if (priv(vmp)->s_ipcf == NULL)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* VM obtains memory requests in response to a SIGKMEM signal, which
|
||||||
|
* is a notification sent from SYSTEM. Thus, if VM blocks such
|
||||||
|
* notifications, it also should not get any memory requests. Of
|
||||||
|
* course, VM should not be asking for requests in that case either,
|
||||||
|
* but the extra check doesn't hurt.
|
||||||
|
*/
|
||||||
|
m_buf.m_type = NOTIFY_MESSAGE;
|
||||||
|
if (!allow_ipc_filtered_msg(vmp, SYSTEM, 0, &m_buf))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* A more refined policy may be implemented here, for example to
|
||||||
|
* ensure that both the source and the destination (if different)
|
||||||
|
* are in the group of processes that VM wants to talk to. Since VM
|
||||||
|
* is basically not able to handle any memory requests during an
|
||||||
|
* update, we will not get here, and none of that is needed.
|
||||||
|
*/
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* priv_add_irq *
|
* priv_add_irq *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
|
|
@ -30,6 +30,7 @@ static void adjust_priv_slot(struct priv *privp, struct priv
|
||||||
static void adjust_asyn_table(struct priv *src_privp, struct priv *dst_privp);
|
static void adjust_asyn_table(struct priv *src_privp, struct priv *dst_privp);
|
||||||
static void swap_proc_slot_pointer(struct proc **rpp, struct proc
|
static void swap_proc_slot_pointer(struct proc **rpp, struct proc
|
||||||
*src_rp, struct proc *dst_rp);
|
*src_rp, struct proc *dst_rp);
|
||||||
|
static void swap_memreq(struct proc *src_rp, struct proc *dst_rp);
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* do_update *
|
* do_update *
|
||||||
|
@ -143,6 +144,9 @@ int do_update(struct proc * caller, message * m_ptr)
|
||||||
/* Swap global process slot addresses. */
|
/* Swap global process slot addresses. */
|
||||||
swap_proc_slot_pointer(get_cpulocal_var_ptr(ptproc), src_rp, dst_rp);
|
swap_proc_slot_pointer(get_cpulocal_var_ptr(ptproc), src_rp, dst_rp);
|
||||||
|
|
||||||
|
/* Swap VM request entries. */
|
||||||
|
swap_memreq(src_rp, dst_rp);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
printf("do_update: updated %d (%s, %d, %d) into %d (%s, %d, %d)\n",
|
printf("do_update: updated %d (%s, %d, %d) into %d (%s, %d, %d)\n",
|
||||||
src_rp->p_endpoint, src_rp->p_name, src_rp->p_nr, priv(src_rp)->s_proc_nr,
|
src_rp->p_endpoint, src_rp->p_name, src_rp->p_nr, priv(src_rp)->s_proc_nr,
|
||||||
|
@ -303,5 +307,34 @@ static void swap_proc_slot_pointer(struct proc **rpp, struct proc *src_rp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===========================================================================*
|
||||||
|
* swap_memreq *
|
||||||
|
*===========================================================================*/
|
||||||
|
static void swap_memreq(struct proc *src_rp, struct proc *dst_rp)
|
||||||
|
{
|
||||||
|
/* If either the source or the destination process is part of the VM request
|
||||||
|
* chain, but not both, then swap the process pointers in the chain.
|
||||||
|
*/
|
||||||
|
struct proc **rpp;
|
||||||
|
|
||||||
|
if (RTS_ISSET(src_rp, RTS_VMREQUEST) == RTS_ISSET(dst_rp, RTS_VMREQUEST))
|
||||||
|
return; /* nothing to do */
|
||||||
|
|
||||||
|
for (rpp = &vmrequest; *rpp != NULL;
|
||||||
|
rpp = &(*rpp)->p_vmrequest.nextrequestor) {
|
||||||
|
if (*rpp == src_rp) {
|
||||||
|
dst_rp->p_vmrequest.nextrequestor =
|
||||||
|
src_rp->p_vmrequest.nextrequestor;
|
||||||
|
*rpp = dst_rp;
|
||||||
|
break;
|
||||||
|
} else if (*rpp == dst_rp) {
|
||||||
|
src_rp->p_vmrequest.nextrequestor =
|
||||||
|
dst_rp->p_vmrequest.nextrequestor;
|
||||||
|
*rpp = src_rp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* USE_UPDATE */
|
#endif /* USE_UPDATE */
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ int do_vmctl(struct proc * caller, message * m_ptr)
|
||||||
{
|
{
|
||||||
int proc_nr;
|
int proc_nr;
|
||||||
endpoint_t ep = m_ptr->SVMCTL_WHO;
|
endpoint_t ep = m_ptr->SVMCTL_WHO;
|
||||||
struct proc *p, *rp, *target;
|
struct proc *p, *rp, **rpp, *target;
|
||||||
|
|
||||||
if(ep == SELF) { ep = caller->p_endpoint; }
|
if(ep == SELF) { ep = caller->p_endpoint; }
|
||||||
|
|
||||||
|
@ -37,17 +37,28 @@ int do_vmctl(struct proc * caller, message * m_ptr)
|
||||||
RTS_UNSET(p, RTS_PAGEFAULT);
|
RTS_UNSET(p, RTS_PAGEFAULT);
|
||||||
return OK;
|
return OK;
|
||||||
case VMCTL_MEMREQ_GET:
|
case VMCTL_MEMREQ_GET:
|
||||||
/* Send VM the information about the memory request. */
|
/* Send VM the information about the memory request. We can
|
||||||
if(!(rp = vmrequest))
|
* not simply send the first request on the list, because IPC
|
||||||
return ESRCH;
|
* filters may forbid VM from getting requests for particular
|
||||||
assert(RTS_ISSET(rp, RTS_VMREQUEST));
|
* sources. However, IPC filters are used only in rare cases.
|
||||||
|
*/
|
||||||
|
for (rpp = &vmrequest; *rpp != NULL;
|
||||||
|
rpp = &(*rpp)->p_vmrequest.nextrequestor) {
|
||||||
|
rp = *rpp;
|
||||||
|
|
||||||
okendpt(rp->p_vmrequest.target, &proc_nr);
|
assert(RTS_ISSET(rp, RTS_VMREQUEST));
|
||||||
target = proc_addr(proc_nr);
|
|
||||||
|
okendpt(rp->p_vmrequest.target, &proc_nr);
|
||||||
|
target = proc_addr(proc_nr);
|
||||||
|
|
||||||
|
/* Check against IPC filters. */
|
||||||
|
if (!allow_ipc_filtered_memreq(rp, target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Reply with request fields. */
|
||||||
|
if (rp->p_vmrequest.req_type != VMPTYPE_CHECK)
|
||||||
|
panic("VMREQUEST wrong type");
|
||||||
|
|
||||||
/* Reply with request fields. */
|
|
||||||
switch(rp->p_vmrequest.req_type) {
|
|
||||||
case VMPTYPE_CHECK:
|
|
||||||
m_ptr->SVMCTL_MRG_TARGET =
|
m_ptr->SVMCTL_MRG_TARGET =
|
||||||
rp->p_vmrequest.target;
|
rp->p_vmrequest.target;
|
||||||
m_ptr->SVMCTL_MRG_ADDR =
|
m_ptr->SVMCTL_MRG_ADDR =
|
||||||
|
@ -58,17 +69,17 @@ int do_vmctl(struct proc * caller, message * m_ptr)
|
||||||
rp->p_vmrequest.params.check.writeflag;
|
rp->p_vmrequest.params.check.writeflag;
|
||||||
m_ptr->SVMCTL_MRG_REQUESTOR =
|
m_ptr->SVMCTL_MRG_REQUESTOR =
|
||||||
(void *) rp->p_endpoint;
|
(void *) rp->p_endpoint;
|
||||||
break;
|
|
||||||
default:
|
rp->p_vmrequest.vmresult = VMSUSPEND;
|
||||||
panic("VMREQUEST wrong type");
|
|
||||||
|
/* Remove from request chain. */
|
||||||
|
*rpp = rp->p_vmrequest.nextrequestor;
|
||||||
|
|
||||||
|
return rp->p_vmrequest.req_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
rp->p_vmrequest.vmresult = VMSUSPEND;
|
return ENOENT;
|
||||||
|
|
||||||
/* Remove from request chain. */
|
|
||||||
vmrequest = vmrequest->p_vmrequest.nextrequestor;
|
|
||||||
|
|
||||||
return rp->p_vmrequest.req_type;
|
|
||||||
case VMCTL_MEMREQ_REPLY:
|
case VMCTL_MEMREQ_REPLY:
|
||||||
assert(RTS_ISSET(p, RTS_VMREQUEST));
|
assert(RTS_ISSET(p, RTS_VMREQUEST));
|
||||||
assert(p->p_vmrequest.vmresult == VMSUSPEND);
|
assert(p->p_vmrequest.vmresult == VMSUSPEND);
|
||||||
|
|
Loading…
Reference in a new issue