From c8a9900b0c9747f0204da0b3ece358573974b606 Mon Sep 17 00:00:00 2001 From: Cristiano Giuffrida Date: Wed, 12 Mar 2014 19:34:52 +0100 Subject: [PATCH] kernel: Add support for IPC filters. Edited by David van Moolenbroek. Change-Id: Ia0052c42a0f218d011dd2da1e3db6c5b2107adc7 --- distrib/sets/lists/minix/mi | 1 + minix/include/minix/Makefile | 2 +- minix/include/minix/com.h | 3 + minix/include/minix/ipc_filter.h | 30 ++++++ minix/kernel/debug.c | 4 +- minix/kernel/debug.h | 4 + minix/kernel/ipc.h | 12 ++- minix/kernel/ipc_filter.h | 73 +++++++++++++ minix/kernel/main.c | 7 +- minix/kernel/priv.h | 3 + minix/kernel/proc.c | 63 +++++++---- minix/kernel/proto.h | 9 ++ minix/kernel/system.c | 167 ++++++++++++++++++++++++++++++ minix/kernel/system/do_privctl.c | 3 +- minix/kernel/system/do_statectl.c | 14 +++ 15 files changed, 367 insertions(+), 28 deletions(-) create mode 100644 minix/include/minix/ipc_filter.h create mode 100644 minix/kernel/ipc_filter.h diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index ea23bc671..573647fc4 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -1414,6 +1414,7 @@ ./usr/include/minix/ioctl.h minix-sys ./usr/include/minix/ipcconst.h minix-sys ./usr/include/minix/ipc.h minix-sys +./usr/include/minix/ipc_filter.h minix-sys ./usr/include/minix/keymap.h minix-sys ./usr/include/minix/libminixfs.h minix-sys ./usr/include/minix/log.h minix-sys diff --git a/minix/include/minix/Makefile b/minix/include/minix/Makefile index bb255599f..65406db33 100644 --- a/minix/include/minix/Makefile +++ b/minix/include/minix/Makefile @@ -12,7 +12,7 @@ INCS+= acpi.h audio_fw.h bitmap.h \ driver.h drivers.h drvlib.h ds.h \ endpoint.h fb.h fsdriver.h fslib.h gpio.h gcov.h hash.h \ hgfs.h i2c.h i2cdriver.h ioctl.h input.h \ - inputdriver.h ipc.h ipcconst.h \ + inputdriver.h ipc.h ipc_filter.h ipcconst.h \ keymap.h log.h mmio.h mthread.h minlib.h \ netdriver.h optset.h padconf.h partition.h portio.h \ priv.h procfs.h profile.h queryparam.h \ diff --git a/minix/include/minix/com.h b/minix/include/minix/com.h index f2474f926..5010cb3af 100644 --- a/minix/include/minix/com.h +++ b/minix/include/minix/com.h @@ -437,6 +437,9 @@ /* Subfunctions for SYS_STATECTL */ #define SYS_STATE_CLEAR_IPC_REFS 1 /* clear IPC references */ #define SYS_STATE_SET_STATE_TABLE 2 /* set state map */ +#define SYS_STATE_ADD_IPC_BL_FILTER 3 /* set IPC blacklist filter */ +#define SYS_STATE_ADD_IPC_WL_FILTER 4 /* set IPC whitelist filter */ +#define SYS_STATE_CLEAR_IPC_FILTERS 5 /* clear IPC filters */ /* Subfunctions for SYS_SCHEDCTL */ # define SCHEDCTL_FLAG_KERNEL 1 /* mark kernel scheduler and remove diff --git a/minix/include/minix/ipc_filter.h b/minix/include/minix/ipc_filter.h new file mode 100644 index 000000000..62d787a2d --- /dev/null +++ b/minix/include/minix/ipc_filter.h @@ -0,0 +1,30 @@ +/* IPC filter definitions. */ + +#ifndef _MINIX_IPC_FILTER_H +#define _MINIX_IPC_FILTER_H + +#include +#include + +/* Special message sources, allowed in IPC filters only. */ +#define ANY_USR _ENDPOINT(1, _ENDPOINT_P(ANY)) +#define ANY_SYS _ENDPOINT(2, _ENDPOINT_P(ANY)) +#define ANY_TSK _ENDPOINT(3, _ENDPOINT_P(ANY)) + +/* IPC filter constants. */ +#define IPCF_MAX_ELEMENTS NR_SYS_PROCS + +/* IPC filter flags. */ +#define IPCF_MATCH_M_SOURCE 0x1 +#define IPCF_MATCH_M_TYPE 0x2 +#define IPCF_EL_BLACKLIST 0x4 +#define IPCF_EL_WHITELIST 0x8 + +struct ipc_filter_el_s { + int flags; + endpoint_t m_source; + int m_type; +}; +typedef struct ipc_filter_el_s ipc_filter_el_t; + +#endif /* _MINIX_IPC_FILTER_H */ diff --git a/minix/kernel/debug.c b/minix/kernel/debug.c index 793749e9e..4463512e6 100644 --- a/minix/kernel/debug.c +++ b/minix/kernel/debug.c @@ -312,7 +312,7 @@ void print_proc_recursive(struct proc *pp) print_proc_depends(pp, 0); } -#if DEBUG_DUMPIPC +#if DEBUG_DUMPIPC || DEBUG_DUMPIPCF static const char *mtypename(int mtype, int *possible_callname) { char *callname = NULL, *errname = NULL; @@ -384,7 +384,7 @@ static int namematch(char **names, int nnames, char *name) } #endif -static void printmsg(message *msg, struct proc *src, struct proc *dst, +void printmsg(message *msg, struct proc *src, struct proc *dst, char operation, int printparams) { const char *name; diff --git a/minix/kernel/debug.h b/minix/kernel/debug.h index a41dd77f0..446904102 100644 --- a/minix/kernel/debug.h +++ b/minix/kernel/debug.h @@ -46,6 +46,10 @@ */ #define DEBUG_DUMPIPC 0 +/* DEBUG_DUMPIPCF dumps filtered IPC to serial. + */ +#define DEBUG_DUMPIPCF 0 + /* If defined, restrict DEBUG_DUMPIPC to particular process names */ /* #define DEBUG_DUMPIPC_NAMES { "tty", "inet" } */ diff --git a/minix/kernel/ipc.h b/minix/kernel/ipc.h index a62919eb9..fe00ac16d 100644 --- a/minix/kernel/ipc.h +++ b/minix/kernel/ipc.h @@ -11,9 +11,15 @@ #define NON_BLOCKING 0x0080 /* do not block if target not ready */ #define FROM_KERNEL 0x0100 /* message from kernel on behalf of a process */ -#define WILLRECEIVE(target, source_ep) \ - ((RTS_ISSET(target, RTS_RECEIVING) && !RTS_ISSET(target, RTS_SENDING)) && \ - (target->p_getfrom_e == ANY || target->p_getfrom_e == source_ep)) +#define WILLRECEIVE(src_e,dst_ptr,m_src_v,m_src_p) \ + ((RTS_ISSET(dst_ptr, RTS_RECEIVING) && \ + !RTS_ISSET(dst_ptr, RTS_SENDING)) && \ + CANRECEIVE(dst_ptr->p_getfrom_e,src_e,dst_ptr,m_src_v,m_src_p)) + +#define CANRECEIVE(receive_e,src_e,dst_ptr,m_src_v,m_src_p) \ + (((receive_e) == ANY || (receive_e) == (src_e)) && \ + (priv(dst_ptr)->s_ipcf == NULL || \ + allow_ipc_filtered_msg(dst_ptr,src_e,m_src_v,m_src_p))) /* IPC status code macros. */ #define IPC_STATUS_GET(p) ((p)->p_reg.IPC_STATUS_REG) diff --git a/minix/kernel/ipc_filter.h b/minix/kernel/ipc_filter.h new file mode 100644 index 000000000..a8b290da5 --- /dev/null +++ b/minix/kernel/ipc_filter.h @@ -0,0 +1,73 @@ +#ifndef IPC_FILTER_H +#define IPC_FILTER_H + +/* Declaration of the ipc filter structure. It provides a framework to + * selectively allow/disallow ipc messages a process agrees to receive. To this + * end, a single ipc filter can be specified at a given time for any recipient + * to blacklist/whitelist a set of ipc messages identified by sender or message + * type. + */ +#include + +/* IPC filter types. */ +#define IPCF_NONE 0 /* no ipc filter */ +#define IPCF_BLACKLIST 1 /* blacklist filter type */ +#define IPCF_WHITELIST 2 /* whitelist filter type */ + +/* IPC filter element macros. */ +EXTERN int _ipcf_nr; +#define IPCF_EL_CHECK(E) \ + ((((E)->flags & IPCF_MATCH_M_TYPE) || \ + ((E)->flags & IPCF_MATCH_M_SOURCE)) && \ + (!((E)->flags & IPCF_MATCH_M_SOURCE) || \ + IPCF_IS_ANY_EP((E)->m_source) || isokendpt((E)->m_source, &_ipcf_nr))) +#define IPCF_IS_USR_EP(EP) \ + (!(priv(proc_addr(_ENDPOINT_P((EP))))->s_flags & SYS_PROC)) +#define IPCF_IS_TSK_EP(EP) (iskerneln(_ENDPOINT_P((EP)))) +#define IPCF_IS_SYS_EP(EP) (!IPCF_IS_USR_EP(EP) && !IPCF_IS_TSK_EP(EP)) +#define IPCF_IS_ANY_EP(EP) \ + ((EP) == ANY_USR || (EP) == ANY_SYS || (EP) == ANY_TSK) +#define IPCF_EL_MATCH_M_TYPE(E,M) \ + (!((E)->flags & IPCF_MATCH_M_TYPE) || (E)->m_type == (M)->m_type) +#define IPCF_EL_MATCH_M_SOURCE(E,M) \ + (!((E)->flags & IPCF_MATCH_M_SOURCE) || \ + (E)->m_source == (M)->m_source || \ + IPCF_EL_MATCH_M_SOURCE_ANY_EP((E)->m_source,(M)->m_source)) +#define IPCF_EL_MATCH_M_SOURCE_ANY_EP(ES,MS) \ + (((ES) == ANY_USR && IPCF_IS_USR_EP(MS)) || \ + ((ES) == ANY_SYS && IPCF_IS_SYS_EP(MS)) || \ + ((ES) == ANY_TSK && IPCF_IS_TSK_EP(MS))) +#define IPCF_EL_MATCH(E,M) \ + (IPCF_EL_MATCH_M_TYPE(E,M) && IPCF_EL_MATCH_M_SOURCE(E,M)) + +struct ipc_filter_s { + int type; + int num_elements; + int flags; + struct ipc_filter_s *next; + ipc_filter_el_t elements[IPCF_MAX_ELEMENTS]; +}; +typedef struct ipc_filter_s ipc_filter_t; + +/* IPC filter pool. */ +#define IPCF_POOL_SIZE (2*NR_SYS_PROCS) +EXTERN ipc_filter_t ipc_filter_pool[IPCF_POOL_SIZE]; + +/* IPC filter pool macros. */ +#define IPCF_POOL_FREE_SLOT(S) ((S)->type = IPCF_NONE) +#define IPCF_POOL_IS_FREE_SLOT(S) ((S)->type == IPCF_NONE) +#define IPCF_POOL_ALLOCATE_SLOT(T,S) \ + do { \ + int i; \ + *(S) = NULL; \ + for (i = 0; i < IPCF_POOL_SIZE; i++) { \ + if (IPCF_POOL_IS_FREE_SLOT(&ipc_filter_pool[i])) { \ + *(S) = &ipc_filter_pool[i]; \ + (*(S))->type = T; \ + break; \ + } \ + } \ + } while(0) +#define IPCF_POOL_INIT(S) memset(&ipc_filter_pool,0,sizeof(ipc_filter_pool)) + +#endif /* !IPC_FILTER_H */ diff --git a/minix/kernel/main.c b/minix/kernel/main.c index 8786f48ec..f351a06a2 100644 --- a/minix/kernel/main.c +++ b/minix/kernel/main.c @@ -154,7 +154,12 @@ void kmain(kinfo_t *local_cbi) DEBUGEXTRA(("main()\n")); - proc_init(); + /* Clear the process table. Anounce each slot as empty and set up mappings + * for proc_addr() and proc_nr() macros. Do the same for the table with + * privilege structures for the system processes and the ipc filter pool. + */ + proc_init(); + IPCF_POOL_INIT(); if(NR_BOOT_MODULES != kinfo.mbi.mi_mods_count) panic("expecting %d boot processes/modules, found %d", diff --git a/minix/kernel/priv.h b/minix/kernel/priv.h index 31cf215d6..06e4c091e 100644 --- a/minix/kernel/priv.h +++ b/minix/kernel/priv.h @@ -17,6 +17,7 @@ #include #include "kernel/const.h" #include "kernel/type.h" +#include "kernel/ipc_filter.h" struct priv { proc_nr_t s_proc_nr; /* number of associated process */ @@ -42,6 +43,7 @@ struct priv { sys_map_t s_asyn_pending; /* bit map with pending asyn messages */ irq_id_t s_int_pending; /* pending hardware interrupts */ sigset_t s_sig_pending; /* pending signals */ + ipc_filter_t *s_ipcf; /* ipc filter (NULL when no filter is set) */ minix_timer_t s_alarm_timer; /* synchronous alarm timer */ reg_t *s_stack_guard; /* stack guard word for kernel tasks */ @@ -81,6 +83,7 @@ struct priv { #define nr_to_id(nr) priv(proc_addr(nr))->s_id #define may_send_to(rp, nr) (get_sys_bit(priv(rp)->s_ipc_to, nr_to_id(nr))) +#define may_asynsend_to(rp, nr) (may_send_to(rp, nr) || (rp)->p_nr == nr) /* The system structures table and pointers to individual table slots. The * pointers allow faster access because now a process entry can be found by diff --git a/minix/kernel/proc.c b/minix/kernel/proc.c index 51920648c..1629b3ca6 100644 --- a/minix/kernel/proc.c +++ b/minix/kernel/proc.c @@ -52,13 +52,14 @@ static int mini_send(struct proc *caller_ptr, endpoint_t dst_e, message *m_ptr, int flags); */ static int mini_receive(struct proc *caller_ptr, endpoint_t src, - message *m_ptr, int flags); + message *m_buff_usr, int flags); static int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size); static int deadlock(int function, register struct proc *caller, endpoint_t src_dst_e); static int try_async(struct proc *caller_ptr); -static int try_one(struct proc *src_ptr, struct proc *dst_ptr); +static int try_one(endpoint_t receive_e, struct proc *src_ptr, + struct proc *dst_ptr); static struct proc * pick_proc(void); static void enqueue_head(struct proc *rp); @@ -116,6 +117,8 @@ static void set_idle_name(char * name, int n) break; \ } +static message m_notify_buff = { 0, NOTIFY_MESSAGE }; + void proc_init(void) { struct proc * rp; @@ -826,7 +829,7 @@ int mini_send( /* Check if 'dst' is blocked waiting for this message. The destination's * RTS_SENDING flag may be set when its SENDREC call blocked while sending. */ - if (WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint)) { + if (WILLRECEIVE(caller_ptr->p_endpoint, dst_ptr, (vir_bytes)m_ptr, NULL)) { int call; /* Destination is indeed waiting for this message. */ assert(!(dst_ptr->p_misc_flags & MF_DELIVERMSG)); @@ -908,7 +911,8 @@ static int mini_receive(struct proc * caller_ptr, * is available block the caller. */ register struct proc **xpp; - int r, src_id, src_proc_nr, src_p; + int r, src_id, found, src_proc_nr, src_p; + endpoint_t sender_e; assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); @@ -936,10 +940,16 @@ static int mini_receive(struct proc * caller_ptr, if (! (caller_ptr->p_misc_flags & MF_REPLY_PEND)) { /* Check for pending notifications */ - if ((src_id = has_pending_notify(caller_ptr, src_p)) != NULL_PRIV_ID) { - endpoint_t hisep; - + src_id = has_pending_notify(caller_ptr, src_p); + found = src_id != NULL_PRIV_ID; + if(found) { src_proc_nr = id_to_nr(src_id); /* get source proc */ + sender_e = proc_addr(src_proc_nr)->p_endpoint; + } + + if (found && CANRECEIVE(src_e, sender_e, caller_ptr, 0, + &m_notify_buff)) { + #if DEBUG_ENABLE_IPC_WARNINGS if(src_proc_nr == NONE) { printf("mini_receive: sending notify from NONE\n"); @@ -949,13 +959,12 @@ static int mini_receive(struct proc * caller_ptr, unset_notify_pending(caller_ptr, src_id); /* no longer pending */ /* Found a suitable source, deliver the notification message. */ - hisep = proc_addr(src_proc_nr)->p_endpoint; assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); - assert(src_e == ANY || hisep == src_e); + assert(src_e == ANY || sender_e == src_e); /* assemble message */ BuildNotifyMessage(&caller_ptr->p_delivermsg, src_proc_nr, caller_ptr); - caller_ptr->p_delivermsg.m_source = hisep; + caller_ptr->p_delivermsg.m_source = sender_e; caller_ptr->p_misc_flags |= MF_DELIVERMSG; IPC_STATUS_ADD_CALL(caller_ptr, NOTIFY); @@ -967,7 +976,7 @@ static int mini_receive(struct proc * caller_ptr, /* Check for pending asynchronous messages */ if (has_pending_asend(caller_ptr, src_p) != NULL_PRIV_ID) { if (src_p != ANY) - r = try_one(proc_addr(src_p), caller_ptr); + r = try_one(src_e, proc_addr(src_p), caller_ptr); else r = try_async(caller_ptr); @@ -981,8 +990,9 @@ static int mini_receive(struct proc * caller_ptr, xpp = &caller_ptr->p_caller_q; while (*xpp) { struct proc * sender = *xpp; + endpoint_t sender_e = sender->p_endpoint; - if (src_e == ANY || src_p == proc_nr(sender)) { + if (CANRECEIVE(src_e, sender_e, caller_ptr, 0, &sender->p_sendmsg)) { int call; assert(!RTS_ISSET(sender, RTS_SLOT_FREE)); assert(!RTS_ISSET(sender, RTS_NO_ENDPOINT)); @@ -1066,8 +1076,8 @@ int mini_notify( /* Check to see if target is blocked waiting for this message. A process * can be both sending and receiving during a SENDREC system call. */ - if (WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) && - ! (dst_ptr->p_misc_flags & MF_REPLY_PEND)) { + if (WILLRECEIVE(caller_ptr->p_endpoint, dst_ptr, 0, &m_notify_buff) && + !(dst_ptr->p_misc_flags & MF_REPLY_PEND)) { /* Destination is indeed waiting for a message. Assemble a notification * message and deliver it. Copy from pseudo-source HARDWARE, since the * message is in the kernel's address space. @@ -1107,6 +1117,9 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) r = EFAULT; \ goto asyn_error; \ } \ + else if(tabent.dst == SELF) { \ + tabent.dst = caller_ptr->p_endpoint; \ + } \ } while(0) #define A_INSRT(entry) do { \ @@ -1133,6 +1146,7 @@ int try_deliver_senda(struct proc *caller_ptr, struct priv *privp; asynmsg_t tabent; const vir_bytes table_v = (vir_bytes) table; + message *m_ptr = NULL; privp = priv(caller_ptr); @@ -1185,7 +1199,7 @@ int try_deliver_senda(struct proc *caller_ptr, r = EDEADSRCDST; /* Bad destination, report the error */ else if (iskerneln(dst_p)) r = ECALLDENIED; /* Asyn sends to the kernel are not allowed */ - else if (!may_send_to(caller_ptr, dst_p)) + else if (!may_asynsend_to(caller_ptr, dst_p)) r = ECALLDENIED; /* Send denied by IPC mask */ else /* r == OK */ dst_ptr = proc_addr(dst_p); @@ -1199,7 +1213,8 @@ int try_deliver_senda(struct proc *caller_ptr, * If AMF_NOREPLY is set, do not satisfy the receiving part of * a SENDREC. */ - if (r == OK && WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) && + if (r == OK && WILLRECEIVE(caller_ptr->p_endpoint, dst_ptr, + (vir_bytes)&table[i].msg, NULL) && (!(flags&AMF_NOREPLY) || !(dst_ptr->p_misc_flags&MF_REPLY_PEND))) { /* Destination is indeed waiting for this message. */ dst_ptr->p_delivermsg = tabent.msg; @@ -1298,7 +1313,7 @@ struct proc *caller_ptr; #endif assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); - if ((r = try_one(src_ptr, caller_ptr)) == OK) + if ((r = try_one(ANY, src_ptr, caller_ptr)) == OK) return(r); } @@ -1309,13 +1324,14 @@ struct proc *caller_ptr; /*===========================================================================* * try_one * *===========================================================================*/ -static int try_one(struct proc *src_ptr, struct proc *dst_ptr) +static int try_one(endpoint_t receive_e, struct proc *src_ptr, + struct proc *dst_ptr) { /* Try to receive an asynchronous message from 'src_ptr' */ int r = EAGAIN, done, do_notify; unsigned int flags, i; size_t size; - endpoint_t dst; + endpoint_t dst, src_e; struct proc *caller_ptr; struct priv *privp; asynmsg_t tabent; @@ -1330,9 +1346,10 @@ static int try_one(struct proc *src_ptr, struct proc *dst_ptr) unset_sys_bit(priv(dst_ptr)->s_asyn_pending, privp->s_id); if (size == 0) return(EAGAIN); - if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return(ECALLDENIED); + if (!may_asynsend_to(src_ptr, proc_nr(dst_ptr))) return (ECALLDENIED); caller_ptr = src_ptr; /* Needed for A_ macros later on */ + src_e = src_ptr->p_endpoint; /* Scan the table */ do_notify = FALSE; @@ -1373,6 +1390,12 @@ static int try_one(struct proc *src_ptr, struct proc *dst_ptr) /* Message must be directed at receiving end */ if (dst != dst_ptr->p_endpoint) continue; + if (!CANRECEIVE(receive_e, src_e, dst_ptr, + table_v + i*sizeof(asynmsg_t) + offsetof(struct asynmsg,msg), + NULL)) { + continue; + } + /* If AMF_NOREPLY is set, then this message is not a reply to a * SENDREC and thus should not satisfy the receiving part of the * SENDREC. This message is to be delivered later. diff --git a/minix/kernel/proto.h b/minix/kernel/proto.h index 0400d6772..fd1f6f081 100644 --- a/minix/kernel/proto.h +++ b/minix/kernel/proto.h @@ -13,6 +13,7 @@ /* Struct declarations. */ struct proc; +struct ipc_filter_s; /* clock.c */ clock_t get_realtime(void); @@ -100,6 +101,12 @@ void clear_endpoint(struct proc *rc); void clear_ipc_refs(struct proc *rc, int caller_ret); void kernel_call_resume(struct proc *p); int sched_proc(struct proc *rp, int priority, int quantum, int cpu); +int add_ipc_filter(struct proc *rp, int type, + vir_bytes address, size_t length); +void clear_ipc_filters(struct proc *rp); +int check_ipc_filter(struct ipc_filter_s *ipcf, int fill_flags); +int allow_ipc_filtered_msg(struct proc *rp, endpoint_t src_e, + vir_bytes m_src_v, message *m_src_p); /* system/do_vtimer.c */ void vtimer_check(struct proc *rp); @@ -128,6 +135,8 @@ char *schedulerstr(struct proc *scheduler); void print_proc(struct proc *pp); /* prints the given process and recursively all processes it depends on */ void print_proc_recursive(struct proc *pp); +void printmsg(message *msg, struct proc *src, struct proc *dst, + char operation, int printparams); #if DEBUG_IPC_HOOK void hook_ipc_msgrecv(message *msg, struct proc *src, struct proc *dst); void hook_ipc_msgsend(message *msg, struct proc *src, struct proc *dst); diff --git a/minix/kernel/system.c b/minix/kernel/system.c index ea9c92456..4cf9fdcea 100644 --- a/minix/kernel/system.c +++ b/minix/kernel/system.c @@ -37,6 +37,7 @@ #include "kernel/vm.h" #include "kernel/clock.h" #include +#include #include #include #include @@ -674,3 +675,169 @@ int sched_proc(struct proc *p, return OK; } +/*===========================================================================* + * add_ipc_filter * + *===========================================================================*/ +int add_ipc_filter(struct proc *rp, int type, vir_bytes address, + size_t length) +{ + int num_elements, r; + ipc_filter_t *ipcf, **ipcfp; + + /* Validate arguments. */ + if (type != IPCF_BLACKLIST && type != IPCF_WHITELIST) + return EINVAL; + + if (length % sizeof(ipc_filter_el_t) != 0) + return EINVAL; + + num_elements = length / sizeof(ipc_filter_el_t); + if (num_elements <= 0 || num_elements > IPCF_MAX_ELEMENTS) + return E2BIG; + + /* Allocate a new IPC filter slot. */ + IPCF_POOL_ALLOCATE_SLOT(type, &ipcf); + if (ipcf == NULL) + return ENOMEM; + + /* Fill details. */ + ipcf->num_elements = num_elements; + ipcf->next = NULL; + r = data_copy(rp->p_endpoint, address, + KERNEL, (vir_bytes)ipcf->elements, length); + if (r == OK) + r = check_ipc_filter(ipcf, TRUE /*fill_flags*/); + if (r != OK) { + IPCF_POOL_FREE_SLOT(ipcf); + return r; + } + + /* Add the new filter at the end of the IPC filter chain. */ + for (ipcfp = &priv(rp)->s_ipcf; *ipcfp != NULL; + ipcfp = &(*ipcfp)->next) + ; + *ipcfp = ipcf; + + return OK; +} + +/*===========================================================================* + * clear_ipc_filters * + *===========================================================================*/ +void clear_ipc_filters(struct proc *rp) +{ + ipc_filter_t *curr_ipcf, *ipcf; + + ipcf = priv(rp)->s_ipcf; + while (ipcf != NULL) { + curr_ipcf = ipcf; + ipcf = ipcf->next; + IPCF_POOL_FREE_SLOT(curr_ipcf); + } + + priv(rp)->s_ipcf = NULL; +} + +/*===========================================================================* + * check_ipc_filter * + *===========================================================================*/ +int check_ipc_filter(ipc_filter_t *ipcf, int fill_flags) +{ + ipc_filter_el_t *ipcf_el; + int i, num_elements, flags; + + if (ipcf == NULL) + return OK; + + num_elements = ipcf->num_elements; + flags = 0; + for (i = 0; i < num_elements; i++) { + ipcf_el = &ipcf->elements[i]; + if (!IPCF_EL_CHECK(ipcf_el)) + return EINVAL; + flags |= ipcf_el->flags; + } + + if (fill_flags) + ipcf->flags = flags; + else if (ipcf->flags != flags) + return EINVAL; + return OK; +} + +/*===========================================================================* + * allow_ipc_filtered_msg * + *===========================================================================*/ +int allow_ipc_filtered_msg(struct proc *rp, endpoint_t src_e, + vir_bytes m_src_v, message *m_src_p) +{ + int i, r, num_elements, get_mtype, allow; + ipc_filter_t *ipcf; + ipc_filter_el_t *ipcf_el; + message m_buff; + + ipcf = priv(rp)->s_ipcf; + if (ipcf == NULL) + return TRUE; /* no IPC filters, always allow */ + + if (m_src_p == NULL) { + assert(m_src_v != 0); + + /* Should we copy in the message type? */ + get_mtype = FALSE; + do { +#if DEBUG_DUMPIPCF + if (TRUE) { +#else + if (ipcf->flags & IPCF_MATCH_M_TYPE) { +#endif + get_mtype = TRUE; + break; + } + ipcf = ipcf->next; + } while (ipcf); + ipcf = priv(rp)->s_ipcf; /* reset to start */ + + /* If so, copy it in from the process. */ + if (get_mtype) { + r = data_copy(src_e, + m_src_v + offsetof(message, m_type), KERNEL, + (vir_bytes)&m_buff.m_type, sizeof(m_buff.m_type)); + if (r != OK) { + /* allow for now, this will fail later anyway */ +#if DEBUG_DUMPIPCF + printf("KERNEL: allow_ipc_filtered_msg: data " + "copy error %d, allowing message...\n", r); +#endif + return TRUE; + } + } + m_src_p = &m_buff; + } + + m_src_p->m_source = src_e; + + /* See if the message is allowed. */ + allow = (ipcf->type == IPCF_BLACKLIST); + do { + if (allow != (ipcf->type == IPCF_WHITELIST)) { + num_elements = ipcf->num_elements; + for (i = 0; i < num_elements; i++) { + ipcf_el = &ipcf->elements[i]; + if (IPCF_EL_MATCH(ipcf_el, m_src_p)) { + allow = (ipcf->type == IPCF_WHITELIST); + break; + } + } + } + ipcf = ipcf->next; + } while (ipcf); + +#if DEBUG_DUMPIPCF + printmsg(m_src_p, proc_addr(_ENDPOINT_P(src_e)), rp, allow ? '+' : '-', + TRUE /*printparams*/); +#endif + + return allow; +} + diff --git a/minix/kernel/system/do_privctl.c b/minix/kernel/system/do_privctl.c index ef2b9ffae..41eee88c7 100644 --- a/minix/kernel/system/do_privctl.c +++ b/minix/kernel/system/do_privctl.c @@ -149,7 +149,7 @@ int do_privctl(struct proc * caller, message * m_ptr) priv(rp)->s_bak_sig_mgr = NONE; /* Set defaults for resources: no I/O resources, no memory resources, - * no IRQs, no grant table + * no IRQs, no grant table, no ipc filter */ priv(rp)->s_nr_io_range= 0; priv(rp)->s_nr_mem_range= 0; @@ -158,6 +158,7 @@ int do_privctl(struct proc * caller, message * m_ptr) priv(rp)->s_grant_entries= 0; priv(rp)->s_state_table= 0; priv(rp)->s_state_entries= 0; + priv(rp)->s_ipcf= 0; /* Override defaults if the caller has supplied a privilege structure. */ if (m_ptr->m_lsys_krn_sys_privctl.arg_ptr) diff --git a/minix/kernel/system/do_statectl.c b/minix/kernel/system/do_statectl.c index d9c9d5296..e2cdc85dc 100644 --- a/minix/kernel/system/do_statectl.c +++ b/minix/kernel/system/do_statectl.c @@ -29,6 +29,20 @@ int do_statectl(struct proc * caller, message * m_ptr) priv(caller)->s_state_table = (vir_bytes) m_ptr->m_lsys_krn_sys_statectl.address; priv(caller)->s_state_entries = m_ptr->m_lsys_krn_sys_statectl.length; return(OK); + case SYS_STATE_ADD_IPC_BL_FILTER: + /* Add an IPC blacklist filter for the caller. */ + return add_ipc_filter(caller, IPCF_BLACKLIST, + (vir_bytes) m_ptr->m_lsys_krn_sys_statectl.address, + m_ptr->m_lsys_krn_sys_statectl.length); + case SYS_STATE_ADD_IPC_WL_FILTER: + /* Add an IPC whitelist filter for the caller. */ + return add_ipc_filter(caller, IPCF_WHITELIST, + (vir_bytes) m_ptr->m_lsys_krn_sys_statectl.address, + m_ptr->m_lsys_krn_sys_statectl.length); + case SYS_STATE_CLEAR_IPC_FILTERS: + /* Clear any IPC filter for the caller. */ + clear_ipc_filters(caller); + return OK; default: printf("do_statectl: bad request %d\n", m_ptr->m_lsys_krn_sys_statectl.request);