diff --git a/config/cml/arm.ruleset b/config/cml/arm.ruleset index 2e0ec73..c0a0255 100644 --- a/config/cml/arm.ruleset +++ b/config/cml/arm.ruleset @@ -39,6 +39,7 @@ containers_menu 'Container Setup' arch_type 'Main architecture' CONTAINERS 'Number of containers' +CAPABILITIES 'Enable capability checking' ############# # CHOICES # @@ -97,9 +98,11 @@ menu main_menu CONTAINERS% containers_menu -############# +#############` # RULES # ############# +#Capability rules: +default CAPABILITIES from y #Platform rules: unless SUBARCH_V5 suppress PLATFORM_PB926 diff --git a/conts/posix/mm0/mm/init.c b/conts/posix/mm0/mm/init.c index 69c12f9..171f10a 100644 --- a/conts/posix/mm0/mm/init.c +++ b/conts/posix/mm0/mm/init.c @@ -210,7 +210,7 @@ int read_pager_capabilities() /* Share all of them with paged children */ if ((err = l4_capability_control(CAP_CONTROL_SHARE, - CAP_SHARE_CHILD, + CAP_SHARE_CONTAINER, 0)) < 0) { printf("l4_capability_control() sharing of " "capabilities failed.\n Could not " diff --git a/conts/test/src/capability.c b/conts/test/src/capability.c index 0d363f0..3e9488f 100644 --- a/conts/test/src/capability.c +++ b/conts/test/src/capability.c @@ -9,6 +9,7 @@ static struct capability cap_array[30]; +#if 0 struct cap_group { struct cap_list virtmem; struct cap_list physmem; @@ -73,6 +74,7 @@ void cap_grant_single(struct capability *orig, struct capability *share, l4id_t { } +#endif void cap_print(struct capability *cap) { @@ -151,7 +153,7 @@ void cap_print(struct capability *cap) printf("\n"); } -int cap_read_all(void) +int caps_read_all(void) { int ncaps; int err; diff --git a/conts/test/src/example.c b/conts/test/src/example.c index 69502eb..6321f2d 100644 --- a/conts/test/src/example.c +++ b/conts/test/src/example.c @@ -1,4 +1,5 @@ +#if 0 int mutex_user_thread(void *arg) { @@ -215,5 +216,5 @@ out_err: - +#endif diff --git a/include/l4/api/thread.h b/include/l4/api/thread.h index 0338b4c..095c713 100644 --- a/include/l4/api/thread.h +++ b/include/l4/api/thread.h @@ -5,9 +5,9 @@ #define THREAD_CREATE 0x0000 #define THREAD_RUN 0x0001 #define THREAD_SUSPEND 0x0002 -#define THREAD_RESUME 0x0003 -#define THREAD_DESTROY 0x0004 -#define THREAD_RECYCLE 0x0005 +#define THREAD_DESTROY 0x0003 +#define THREAD_RECYCLE 0x0004 +#define THREAD_WAIT 0x0005 #define THREAD_CREATE_MASK 0x0FF0 #define TC_SHARE_CAPS 0x0010 /* Share all thread capabilities */ diff --git a/include/l4/generic/cap-types.h b/include/l4/generic/cap-types.h index 0947d62..d818acb 100644 --- a/include/l4/generic/cap-types.h +++ b/include/l4/generic/cap-types.h @@ -28,7 +28,7 @@ #define CAP_RTYPE_TGROUP (1 << 17) #define CAP_RTYPE_SPACE (1 << 18) #define CAP_RTYPE_CONTAINER (1 << 19) -#define CAP_RTYPE_UMUTEX (1 << 20) /* Don't mix with pool version */ +#define CAP_RTYPE_PGGROUP (1 << 20) /* Group of paged threads */ #define CAP_RTYPE_VIRTMEM (1 << 21) #define CAP_RTYPE_PHYSMEM (1 << 22) #define CAP_RTYPE_CPUPOOL (1 << 23) @@ -37,7 +37,6 @@ #define CAP_RTYPE_MUTEXPOOL (1 << 26) #define CAP_RTYPE_MAPPOOL (1 << 27) /* For pmd spending */ #define CAP_RTYPE_CAPPOOL (1 << 28) /* For new cap generation */ -#define CAP_RTYPE_PGGROUP (1 << 29) /* Group of paged threads */ #define cap_rtype(c) ((c)->type & CAP_RTYPE_MASK) @@ -48,9 +47,10 @@ /* Thread control capability */ #define CAP_TCTRL_CREATE (1 << 0) #define CAP_TCTRL_DESTROY (1 << 1) -#define CAP_TCTRL_SUSPEND (1 << 2) -#define CAP_TCTRL_RESUME (1 << 3) +#define CAP_TCTRL_RUN (1 << 2) +#define CAP_TCTRL_SUSPEND (1 << 3) #define CAP_TCTRL_RECYCLE (1 << 4) +#define CAP_TCTRL_WAIT (1 << 5) /* Exchange registers capability */ #define CAP_EXREGS_RW_PAGER (1 << 0) diff --git a/include/l4/generic/capability.h b/include/l4/generic/capability.h index 95f2c6f..61b2da5 100644 --- a/include/l4/generic/capability.h +++ b/include/l4/generic/capability.h @@ -142,7 +142,7 @@ struct capability *cap_list_find_by_rtype(struct cap_list *clist, /* Capability checking on system calls */ int cap_map_check(struct ktcb *task, unsigned long phys, unsigned long virt, - unsigned long npages, unsigned int flags, l4id_t tid); + unsigned long npages, unsigned int flags); int cap_thread_check(struct ktcb *task, unsigned int flags, struct task_ids *ids); int cap_exregs_check(struct ktcb *task, struct exregs_data *exregs); diff --git a/scripts/cml/generate_container_cml.py b/scripts/cml/generate_container_cml.py index 00e0848..9113870 100755 --- a/scripts/cml/generate_container_cml.py +++ b/scripts/cml/generate_container_cml.py @@ -19,6 +19,7 @@ from config.configuration import * containers_menu = \ ''' menu containers_menu + CAPABILITIES ''' containers_constraint = \ diff --git a/scripts/kernel/generate_kernel_cinfo.py b/scripts/kernel/generate_kernel_cinfo.py index bb0df3b..12c7918 100755 --- a/scripts/kernel/generate_kernel_cinfo.py +++ b/scripts/kernel/generate_kernel_cinfo.py @@ -109,7 +109,7 @@ cap_all_others = \ \t\t\t[%d] = { \t\t\t\t.type = CAP_TYPE_TCTRL | CAP_RTYPE_CONTAINER, \t\t\t\t.access = CAP_TCTRL_CREATE | CAP_TCTRL_DESTROY -\t\t\t\t | CAP_TCTRL_SUSPEND | CAP_TCTRL_RESUME +\t\t\t\t | CAP_TCTRL_SUSPEND | CAP_TCTRL_RUN \t\t\t\t | CAP_TCTRL_RECYCLE, \t\t\t\t.start = 0, .end = 0, .size = 0, \t\t\t}, @@ -121,6 +121,17 @@ cap_all_others = \ \t\t\t\t.start = 0, .end = 0, .size = 0, \t\t\t}, \t\t\t[%d] = { +\t\t\t\t.type = CAP_TYPE_CAP | CAP_RTYPE_CONTAINER, +\t\t\t\t.access = CAP_CAP_MODIFY | CAP_CAP_GRANT +\t\t\t\t| CAP_CAP_READ | CAP_CAP_SHARE, +\t\t\t\t.start = 0, .end = 0, .size = 0, +\t\t\t}, +\t\t\t[%d] = { +\t\t\t\t.type = CAP_TYPE_UMUTEX | CAP_RTYPE_CONTAINER, +\t\t\t\t.access = CAP_UMUTEX_LOCK | CAP_UMUTEX_UNLOCK, +\t\t\t\t.start = 0, .end = 0, .size = 0, +\t\t\t}, +\t\t\t[%d] = { \t\t\t\t.type = CAP_TYPE_QUANTITY \t\t\t\t | CAP_RTYPE_THREADPOOL, \t\t\t\t.access = 0, .start = 0, .end = 0, @@ -203,7 +214,7 @@ def generate_kernel_cinfo(config, cinfo_path): with open(cinfo_path, 'w+') as cinfo_file: fbody = cinfo_file_start % pager_ifdefs - total_other_caps = 9 + total_other_caps = 11 for c in containers: # Currently only these are considered as capabilities total_caps = c.virt_regions + c.phys_regions + total_other_caps @@ -216,7 +227,7 @@ def generate_kernel_cinfo(config, cinfo_path): for mem_index in range(c.phys_regions): fbody += cap_physmem % { 'capidx' : cap_index, 'cn' : c.id, 'pn' : mem_index } cap_index += 1 - fbody += cap_all_others % (tuple(range(cap_index, total_caps))) + fbody += cap_all_others % tuple(range(cap_index, total_caps)) fbody += pager_end fbody += cinfo_end fbody += cinfo_file_end diff --git a/src/api/map.c b/src/api/map.c index 45faf03..d1c6932 100644 --- a/src/api/map.c +++ b/src/api/map.c @@ -18,7 +18,7 @@ int sys_map(unsigned long phys, unsigned long virt, unsigned long npages, if (!(target = tcb_find(tid))) return -ESRCH; - if ((err = cap_map_check(target, phys, virt, npages, flags, tid)) < 0) + if ((err = cap_map_check(target, phys, virt, npages, flags)) < 0) return err; add_mapping_pgd(phys, virt, npages << PAGE_BITS, flags, TASK_PGD(target)); diff --git a/src/api/thread.c b/src/api/thread.c index a02a867..2a72125 100644 --- a/src/api/thread.c +++ b/src/api/thread.c @@ -203,20 +203,6 @@ void thread_destroy_current(void) sched_suspend_sync(); } -int thread_resume(struct ktcb *task) -{ - if (!mutex_trylock(&task->thread_control_lock)) - return -EAGAIN; - - /* Put task into runqueue as runnable */ - sched_resume_async(task); - - /* Release lock and return */ - mutex_unlock(&task->thread_control_lock); - - return 0; -} - /* Runs a thread for the first time */ int thread_start(struct ktcb *task) { @@ -443,9 +429,6 @@ int sys_thread_control(unsigned int flags, struct task_ids *ids) case THREAD_SUSPEND: ret = thread_suspend(task, flags); break; - case THREAD_RESUME: - ret = thread_resume(task); - break; case THREAD_DESTROY: ret = thread_destroy(task); break; diff --git a/src/generic/capability.c b/src/generic/capability.c index 6b3b7d4..493ce09 100644 --- a/src/generic/capability.c +++ b/src/generic/capability.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ struct capability *capability_create(void) return cap; } +#if defined(CONFIG_CAPABILITIES) int capability_consume(struct capability *cap, int quantity) { if (cap->size < cap->used + quantity) @@ -60,6 +62,18 @@ int capability_free(struct capability *cap, int quantity) return 0; } +#else +int capability_consume(struct capability *cap, int quantity) +{ + return 0; +} + +int capability_free(struct capability *cap, int quantity) +{ + return 0; +} +#endif + struct capability *cap_list_find_by_rtype(struct cap_list *cap_list, unsigned int rtype) { @@ -77,10 +91,12 @@ struct capability *cap_list_find_by_rtype(struct cap_list *cap_list, * Search all capability lists that task is allowed. * * FIXME: - * Tasks won't always search for a capability randomly. Consider + * Tasks should not always search for a capability randomly. Consider * mutexes, if a mutex is freed, it needs to be accounted to private * pool first if that is not full, because freeing it into shared - * pool may lose the mutex right to another task. + * pool may lose the mutex right to another task. In other words, + * when you're freeing a mutex, we should know which capability pool + * to free it to. * * In conclusion freeing of pool-type capabilities need to be done * in order of privacy. -> It may get confusing as a space, thread @@ -116,25 +132,25 @@ typedef struct capability *(*cap_match_func_t) \ struct capability *cap_find(struct ktcb *task, cap_match_func_t cap_match_func, void *match_args, unsigned int cap_type) { - struct capability *cap; + struct capability *cap, *found; /* Search task's own list */ list_foreach_struct(cap, &task->cap_list.caps, list) if (cap_type(cap) == cap_type && - ((cap = cap_match_func(cap, match_args)))) - return cap; + ((found = cap_match_func(cap, match_args)))) + return found; /* Search space list */ list_foreach_struct(cap, &task->space->cap_list.caps, list) if (cap_type(cap) == cap_type && - ((cap = cap_match_func(cap, match_args)))) - return cap; + ((found = cap_match_func(cap, match_args)))) + return found; /* Search container list */ list_foreach_struct(cap, &task->container->cap_list.caps, list) if (cap_type(cap) == cap_type && - ((cap = cap_match_func(cap, match_args)))) - return cap; + ((found = cap_match_func(cap, match_args)))) + return found; return 0; } @@ -144,6 +160,24 @@ struct sys_mutex_args { unsigned int op; }; +/* + * Check broadly the ability to do mutex ops. Check it by + * the thread, space or container, (i.e. the group that can + * do this operation broadly) + * + * Note, that we check mutex_address elsewhere as a quick, + * per-task virt_to_phys translation that would not get + * easily/quickly satisfied by a memory capability checking. + * + * While this is not %100 right from a capability checking + * point-of-view, it is a shortcut that works and makes sense. + * + * For sake of completion, the right way to do it would be to + * add MUTEX_LOCKABLE, MUTEX_UNLOCKABLE attributes to both + * virtual and physical memory caps of a task, search those + * to validate the address. But we would have to translate + * from the page tables either ways. + */ struct capability * cap_match_mutex(struct capability *cap, void *args) { @@ -174,20 +208,6 @@ cap_match_mutex(struct capability *cap, void *args) return cap; } -int cap_mutex_check(unsigned long mutex_address, int mutex_op) -{ - struct sys_mutex_args args = { - .address = mutex_address, - .op = mutex_op, - }; - - if (!(cap_find(current, cap_match_mutex, - &args, CAP_TYPE_UMUTEX))) - return -ENOCAP; - - return 0; -} - struct sys_capctrl_args { unsigned int req; unsigned int flags; @@ -237,21 +257,6 @@ cap_match_capctrl(struct capability *cap, void *args_ptr) return cap; } -int cap_cap_check(struct ktcb *task, unsigned int req, unsigned int flags) -{ - struct sys_capctrl_args args = { - .req = req, - .flags = flags, - .task = task, - }; - - if (!(cap_find(task, cap_match_capctrl, - &args, CAP_TYPE_CAP))) - return -ENOCAP; - - return 0; -} - struct sys_ipc_args { struct ktcb *task; unsigned int ipc_type; @@ -307,40 +312,6 @@ cap_match_ipc(struct capability *cap, void *args_ptr) return cap; } -/* - * Limitation: We currently only check from sender's - * perspective. Sender always targets a real thread. - * Does sender have the right to do this ipc? - */ -int cap_ipc_check(l4id_t to, l4id_t from, - unsigned int flags, unsigned int ipc_type) -{ - struct ktcb *target; - struct sys_ipc_args args; - - /* Receivers can get away from us (for now) */ - if (ipc_type != IPC_SEND && ipc_type != IPC_SENDRECV) - return 0; - - /* - * We're the sender, meaning we have - * a real target - */ - if (!(target = tcb_find(to))) - return -ESRCH; - - /* Set up other args */ - args.flags = flags; - args.ipc_type = ipc_type; - args.task = target; - - if (!(cap_find(target, cap_match_ipc, - &args, CAP_TYPE_IPC))) - return -ENOCAP; - - return 0; -} - struct sys_exregs_args { struct exregs_data *exregs; struct ktcb *task; @@ -397,20 +368,6 @@ cap_match_exregs(struct capability *cap, void *args_ptr) return cap; } -int cap_exregs_check(struct ktcb *task, struct exregs_data *exregs) -{ - struct sys_exregs_args args = { - .exregs = exregs, - .task = task, - }; - - if (!(cap_find(task, cap_match_exregs, - &args, CAP_TYPE_EXREGS))) - return -ENOCAP; - - return 0; -} - /* * FIXME: Issues on capabilities: * @@ -456,26 +413,44 @@ struct capability *cap_match_thread(struct capability *cap, { struct sys_tctrl_args *args = args_ptr; struct ktcb *target = args->task; + unsigned int action_flags = args->flags & THREAD_ACTION_MASK; /* Check operation privileges */ - if (args->flags & THREAD_CREATE) + switch (action_flags) { + case THREAD_CREATE: if (!(cap->access & CAP_TCTRL_CREATE)) return 0; - if (args->flags & THREAD_DESTROY) + break; + case THREAD_DESTROY: if (!(cap->access & CAP_TCTRL_DESTROY)) return 0; - if (args->flags & THREAD_SUSPEND) + break; + case THREAD_SUSPEND: if (!(cap->access & CAP_TCTRL_SUSPEND)) return 0; - if (args->flags & THREAD_RESUME) - if (!(cap->access & CAP_TCTRL_RESUME)) + break; + case THREAD_RUN: + if (!(cap->access & CAP_TCTRL_RUN)) return 0; + break; + case THREAD_RECYCLE: + if (!(cap->access & CAP_TCTRL_RECYCLE)) + return 0; + break; + case THREAD_WAIT: + if (!(cap->access & CAP_TCTRL_WAIT)) + return 0; + break; + default: + /* We refuse to accept anything else */ + return 0; + } /* If no target and create, or vice versa, it really is a bug */ - BUG_ON(!target && !(args->flags & THREAD_CREATE)); - BUG_ON(target && (args->flags & THREAD_CREATE)); + BUG_ON(!target && action_flags != THREAD_CREATE); + BUG_ON(target && action_flags == THREAD_CREATE); - if (args->flags & THREAD_CREATE) { + if (action_flags == THREAD_CREATE) { /* * FIXME: Add cid to task_ids arg. * @@ -490,7 +465,7 @@ struct capability *cap_match_thread(struct capability *cap, return 0; if (cap->resid != current->container->cid) return 0; - /* Resource type and it match, success */ + /* Resource type and id match, success */ return cap; } @@ -517,23 +492,6 @@ struct capability *cap_match_thread(struct capability *cap, return cap; } -int cap_thread_check(struct ktcb *task, - unsigned int flags, - struct task_ids *ids) -{ - struct sys_tctrl_args args = { - .task = task, - .flags = flags, - .ids = ids, - }; - - if (!(cap_find(task, cap_match_thread, - &args, CAP_TYPE_TCTRL))) - return -ENOCAP; - - return 0; -} - struct sys_map_args { struct ktcb *task; unsigned long phys; @@ -541,7 +499,6 @@ struct sys_map_args { unsigned long npages; unsigned int flags; unsigned int rtype; - l4id_t tid; }; /* @@ -589,7 +546,9 @@ struct capability *cap_match_mem(struct capability *cap, return cap; /* - * TODO: Does it make sense to have a meaningful resid field + * FIXME: + * + * Does it make sense to have a meaningful resid field * in a memory resource? E.g. Which resources may I map it to? * It might, as I can map an arbitrary mapping to an arbitrary * thread in my container and break it's memory integrity. @@ -601,32 +560,163 @@ struct capability *cap_match_mem(struct capability *cap, */ } -int cap_map_check(struct ktcb *task, unsigned long phys, unsigned long virt, - unsigned long npages, unsigned int flags, l4id_t tid) +#if defined(CONFIG_CAPABILITIES) +int cap_mutex_check(unsigned long mutex_address, int mutex_op) +{ + struct sys_mutex_args args = { + .address = mutex_address, + .op = mutex_op, + }; + + if (!(cap_find(current, cap_match_mutex, + &args, CAP_TYPE_UMUTEX))) + return -ENOCAP; + + return 0; +} + +int cap_cap_check(struct ktcb *task, unsigned int req, unsigned int flags) +{ + struct sys_capctrl_args args = { + .req = req, + .flags = flags, + .task = task, + }; + + if (!(cap_find(current, cap_match_capctrl, + &args, CAP_TYPE_CAP))) + return -ENOCAP; + + return 0; +} + +int cap_map_check(struct ktcb *target, unsigned long phys, unsigned long virt, + unsigned long npages, unsigned int flags) { struct capability *physmem, *virtmem; struct sys_map_args args = { - .task = task, + .task = target, .phys = phys, .virt = virt, .npages = npages, .flags = flags, - .tid = tid, }; args.rtype = CAP_RTYPE_PHYSMEM; - if (!(physmem = cap_find(task, cap_match_mem, + if (!(physmem = cap_find(current, cap_match_mem, &args, CAP_TYPE_MAP))) return -ENOCAP; args.rtype = CAP_RTYPE_VIRTMEM; - if (!(virtmem = cap_find(task, cap_match_mem, + if (!(virtmem = cap_find(current, cap_match_mem, &args, CAP_TYPE_MAP))) return -ENOCAP; return 0; } +/* + * Limitation: We currently only check from sender's + * perspective. Sender always targets a real thread. + * Does sender have the right to do this ipc? + */ +int cap_ipc_check(l4id_t to, l4id_t from, + unsigned int flags, unsigned int ipc_type) +{ + struct ktcb *target; + struct sys_ipc_args args; + + /* Receivers can get away from us (for now) */ + if (ipc_type != IPC_SEND && ipc_type != IPC_SENDRECV) + return 0; + + /* + * We're the sender, meaning we have + * a real target + */ + if (!(target = tcb_find(to))) + return -ESRCH; + + /* Set up other args */ + args.flags = flags; + args.ipc_type = ipc_type; + args.task = target; + + if (!(cap_find(current, cap_match_ipc, + &args, CAP_TYPE_IPC))) + return -ENOCAP; + + return 0; +} + +int cap_exregs_check(struct ktcb *task, struct exregs_data *exregs) +{ + struct sys_exregs_args args = { + .exregs = exregs, + .task = task, + }; + + /* We always search for current's caps */ + if (!(cap_find(current, cap_match_exregs, + &args, CAP_TYPE_EXREGS))) + return -ENOCAP; + + return 0; +} + +int cap_thread_check(struct ktcb *task, + unsigned int flags, + struct task_ids *ids) +{ + struct sys_tctrl_args args = { + .task = task, + .flags = flags, + .ids = ids, + }; + + if (!(cap_find(current, cap_match_thread, + &args, CAP_TYPE_TCTRL))) + return -ENOCAP; + + return 0; +} + +#else /* Meaning !CONFIG_CAPABILITIES */ +int cap_mutex_check(unsigned long mutex_address, int mutex_op) +{ + return 0; +} + +int cap_cap_check(struct ktcb *task, unsigned int req, unsigned int flags) +{ + return 0; +} + +int cap_ipc_check(l4id_t to, l4id_t from, + unsigned int flags, unsigned int ipc_type) +{ + return 0; +} + +int cap_map_check(struct ktcb *task, unsigned long phys, unsigned long virt, + unsigned long npages, unsigned int flags) +{ + return 0; +} + +int cap_exregs_check(struct ktcb *task, struct exregs_data *exregs) +{ + return 0; +} + +int cap_thread_check(struct ktcb *task, + unsigned int flags, + struct task_ids *ids) +{ + return 0; +} +#endif + /* * FIXME: * @@ -667,7 +757,6 @@ int capability_set_resource_id(struct capability *cap) case CAP_RTYPE_TGROUP: case CAP_RTYPE_SPACE: case CAP_RTYPE_CONTAINER: - case CAP_RTYPE_UMUTEX: break; } return 0;