aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 22:14:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 22:14:20 -0400
commit191a712090bb8a10e6f129360eeed2d68f3d4c9a (patch)
tree17e2d6c27fb8a7c3a61828fbcc7c343a4966a0a9 /security
parent46d9be3e5eb01f71fc02653755d970247174b400 (diff)
parent2a0010af17b1739ef8ea8cf02647a127241ee674 (diff)
Merge branch 'for-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup updates from Tejun Heo: - Fixes and a lot of cleanups. Locking cleanup is finally complete. cgroup_mutex is no longer exposed to individual controlelrs which used to cause nasty deadlock issues. Li fixed and cleaned up quite a bit including long standing ones like racy cgroup_path(). - device cgroup now supports proper hierarchy thanks to Aristeu. - perf_event cgroup now supports proper hierarchy. - A new mount option "__DEVEL__sane_behavior" is added. As indicated by the name, this option is to be used for development only at this point and generates a warning message when used. Unfortunately, cgroup interface currently has too many brekages and inconsistencies to implement a consistent and unified hierarchy on top. The new flag is used to collect the behavior changes which are necessary to implement consistent unified hierarchy. It's likely that this flag won't be used verbatim when it becomes ready but will be enabled implicitly along with unified hierarchy. The option currently disables some of broken behaviors in cgroup core and also .use_hierarchy switch in memcg (will be routed through -mm), which can be used to make very unusual hierarchy where nesting is partially honored. It will also be used to implement hierarchy support for blk-throttle which would be impossible otherwise without introducing a full separate set of control knobs. This is essentially versioning of interface which isn't very nice but at this point I can't see any other options which would allow keeping the interface the same while moving towards hierarchy behavior which is at least somewhat sane. The planned unified hierarchy is likely to require some level of adaptation from userland anyway, so I think it'd be best to take the chance and update the interface such that it's supportable in the long term. Maintaining the existing interface does complicate cgroup core but shouldn't put too much strain on individual controllers and I think it'd be manageable for the foreseeable future. Maybe we'll be able to drop it in a decade. Fix up conflicts (including a semantic one adding a new #include to ppc that was uncovered by header the file changes) as per Tejun. * 'for-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: (45 commits) cpuset: fix compile warning when CONFIG_SMP=n cpuset: fix cpu hotplug vs rebuild_sched_domains() race cpuset: use rebuild_sched_domains() in cpuset_hotplug_workfn() cgroup: restore the call to eventfd->poll() cgroup: fix use-after-free when umounting cgroupfs cgroup: fix broken file xattrs devcg: remove parent_cgroup. memcg: force use_hierarchy if sane_behavior cgroup: remove cgrp->top_cgroup cgroup: introduce sane_behavior mount option move cgroupfs_root to include/linux/cgroup.h cgroup: convert cgroupfs_root flag bits to masks and add CGRP_ prefix cgroup: make cgroup_path() not print double slashes Revert "cgroup: remove bind() method from cgroup_subsys." perf: make perf_event cgroup hierarchical cgroup: implement cgroup_is_descendant() cgroup: make sure parent won't be destroyed before its children cgroup: remove bind() method from cgroup_subsys. devcg: remove broken_hierarchy tag cgroup: remove cgroup_lock_is_held() ...
Diffstat (limited to 'security')
-rw-r--r--security/device_cgroup.c267
1 files changed, 211 insertions, 56 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 1c69e38e3a2c..dd0dc574d78d 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -25,6 +25,12 @@
25 25
26static DEFINE_MUTEX(devcgroup_mutex); 26static DEFINE_MUTEX(devcgroup_mutex);
27 27
28enum devcg_behavior {
29 DEVCG_DEFAULT_NONE,
30 DEVCG_DEFAULT_ALLOW,
31 DEVCG_DEFAULT_DENY,
32};
33
28/* 34/*
29 * exception list locking rules: 35 * exception list locking rules:
30 * hold devcgroup_mutex for update/read. 36 * hold devcgroup_mutex for update/read.
@@ -42,10 +48,9 @@ struct dev_exception_item {
42struct dev_cgroup { 48struct dev_cgroup {
43 struct cgroup_subsys_state css; 49 struct cgroup_subsys_state css;
44 struct list_head exceptions; 50 struct list_head exceptions;
45 enum { 51 enum devcg_behavior behavior;
46 DEVCG_DEFAULT_ALLOW, 52 /* temporary list for pending propagation operations */
47 DEVCG_DEFAULT_DENY, 53 struct list_head propagate_pending;
48 } behavior;
49}; 54};
50 55
51static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) 56static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
@@ -182,35 +187,62 @@ static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
182 __dev_exception_clean(dev_cgroup); 187 __dev_exception_clean(dev_cgroup);
183} 188}
184 189
190static inline bool is_devcg_online(const struct dev_cgroup *devcg)
191{
192 return (devcg->behavior != DEVCG_DEFAULT_NONE);
193}
194
195/**
196 * devcgroup_online - initializes devcgroup's behavior and exceptions based on
197 * parent's
198 * @cgroup: cgroup getting online
199 * returns 0 in case of success, error code otherwise
200 */
201static int devcgroup_online(struct cgroup *cgroup)
202{
203 struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL;
204 int ret = 0;
205
206 mutex_lock(&devcgroup_mutex);
207 dev_cgroup = cgroup_to_devcgroup(cgroup);
208 if (cgroup->parent)
209 parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent);
210
211 if (parent_dev_cgroup == NULL)
212 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
213 else {
214 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
215 &parent_dev_cgroup->exceptions);
216 if (!ret)
217 dev_cgroup->behavior = parent_dev_cgroup->behavior;
218 }
219 mutex_unlock(&devcgroup_mutex);
220
221 return ret;
222}
223
224static void devcgroup_offline(struct cgroup *cgroup)
225{
226 struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup);
227
228 mutex_lock(&devcgroup_mutex);
229 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
230 mutex_unlock(&devcgroup_mutex);
231}
232
185/* 233/*
186 * called from kernel/cgroup.c with cgroup_lock() held. 234 * called from kernel/cgroup.c with cgroup_lock() held.
187 */ 235 */
188static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) 236static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup)
189{ 237{
190 struct dev_cgroup *dev_cgroup, *parent_dev_cgroup; 238 struct dev_cgroup *dev_cgroup;
191 struct cgroup *parent_cgroup;
192 int ret;
193 239
194 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL); 240 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
195 if (!dev_cgroup) 241 if (!dev_cgroup)
196 return ERR_PTR(-ENOMEM); 242 return ERR_PTR(-ENOMEM);
197 INIT_LIST_HEAD(&dev_cgroup->exceptions); 243 INIT_LIST_HEAD(&dev_cgroup->exceptions);
198 parent_cgroup = cgroup->parent; 244 INIT_LIST_HEAD(&dev_cgroup->propagate_pending);
199 245 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
200 if (parent_cgroup == NULL)
201 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
202 else {
203 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
204 mutex_lock(&devcgroup_mutex);
205 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
206 &parent_dev_cgroup->exceptions);
207 dev_cgroup->behavior = parent_dev_cgroup->behavior;
208 mutex_unlock(&devcgroup_mutex);
209 if (ret) {
210 kfree(dev_cgroup);
211 return ERR_PTR(ret);
212 }
213 }
214 246
215 return &dev_cgroup->css; 247 return &dev_cgroup->css;
216} 248}
@@ -304,9 +336,11 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
304 * verify if a certain access is allowed. 336 * verify if a certain access is allowed.
305 * @dev_cgroup: dev cgroup to be tested against 337 * @dev_cgroup: dev cgroup to be tested against
306 * @refex: new exception 338 * @refex: new exception
339 * @behavior: behavior of the exception
307 */ 340 */
308static int may_access(struct dev_cgroup *dev_cgroup, 341static bool may_access(struct dev_cgroup *dev_cgroup,
309 struct dev_exception_item *refex) 342 struct dev_exception_item *refex,
343 enum devcg_behavior behavior)
310{ 344{
311 struct dev_exception_item *ex; 345 struct dev_exception_item *ex;
312 bool match = false; 346 bool match = false;
@@ -330,18 +364,29 @@ static int may_access(struct dev_cgroup *dev_cgroup,
330 break; 364 break;
331 } 365 }
332 366
333 /* 367 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
334 * In two cases we'll consider this new exception valid: 368 if (behavior == DEVCG_DEFAULT_ALLOW) {
335 * - the dev cgroup has its default policy to allow + exception list: 369 /* the exception will deny access to certain devices */
336 * the new exception should *not* match any of the exceptions 370 return true;
337 * (behavior == DEVCG_DEFAULT_ALLOW, !match) 371 } else {
338 * - the dev cgroup has its default policy to deny + exception list: 372 /* the exception will allow access to certain devices */
339 * the new exception *should* match the exceptions 373 if (match)
340 * (behavior == DEVCG_DEFAULT_DENY, match) 374 /*
341 */ 375 * a new exception allowing access shouldn't
342 if ((dev_cgroup->behavior == DEVCG_DEFAULT_DENY) == match) 376 * match an parent's exception
343 return 1; 377 */
344 return 0; 378 return false;
379 return true;
380 }
381 } else {
382 /* only behavior == DEVCG_DEFAULT_DENY allowed here */
383 if (match)
384 /* parent has an exception that matches the proposed */
385 return true;
386 else
387 return false;
388 }
389 return false;
345} 390}
346 391
347/* 392/*
@@ -358,7 +403,7 @@ static int parent_has_perm(struct dev_cgroup *childcg,
358 if (!pcg) 403 if (!pcg)
359 return 1; 404 return 1;
360 parent = cgroup_to_devcgroup(pcg); 405 parent = cgroup_to_devcgroup(pcg);
361 return may_access(parent, ex); 406 return may_access(parent, ex, childcg->behavior);
362} 407}
363 408
364/** 409/**
@@ -374,6 +419,111 @@ static inline int may_allow_all(struct dev_cgroup *parent)
374 return parent->behavior == DEVCG_DEFAULT_ALLOW; 419 return parent->behavior == DEVCG_DEFAULT_ALLOW;
375} 420}
376 421
422/**
423 * revalidate_active_exceptions - walks through the active exception list and
424 * revalidates the exceptions based on parent's
425 * behavior and exceptions. The exceptions that
426 * are no longer valid will be removed.
427 * Called with devcgroup_mutex held.
428 * @devcg: cgroup which exceptions will be checked
429 *
430 * This is one of the three key functions for hierarchy implementation.
431 * This function is responsible for re-evaluating all the cgroup's active
432 * exceptions due to a parent's exception change.
433 * Refer to Documentation/cgroups/devices.txt for more details.
434 */
435static void revalidate_active_exceptions(struct dev_cgroup *devcg)
436{
437 struct dev_exception_item *ex;
438 struct list_head *this, *tmp;
439
440 list_for_each_safe(this, tmp, &devcg->exceptions) {
441 ex = container_of(this, struct dev_exception_item, list);
442 if (!parent_has_perm(devcg, ex))
443 dev_exception_rm(devcg, ex);
444 }
445}
446
447/**
448 * get_online_devcg - walks the cgroup tree and fills a list with the online
449 * groups
450 * @root: cgroup used as starting point
451 * @online: list that will be filled with online groups
452 *
453 * Must be called with devcgroup_mutex held. Grabs RCU lock.
454 * Because devcgroup_mutex is held, no devcg will become online or offline
455 * during the tree walk (see devcgroup_online, devcgroup_offline)
456 * A separated list is needed because propagate_behavior() and
457 * propagate_exception() need to allocate memory and can block.
458 */
459static void get_online_devcg(struct cgroup *root, struct list_head *online)
460{
461 struct cgroup *pos;
462 struct dev_cgroup *devcg;
463
464 lockdep_assert_held(&devcgroup_mutex);
465
466 rcu_read_lock();
467 cgroup_for_each_descendant_pre(pos, root) {
468 devcg = cgroup_to_devcgroup(pos);
469 if (is_devcg_online(devcg))
470 list_add_tail(&devcg->propagate_pending, online);
471 }
472 rcu_read_unlock();
473}
474
475/**
476 * propagate_exception - propagates a new exception to the children
477 * @devcg_root: device cgroup that added a new exception
478 * @ex: new exception to be propagated
479 *
480 * returns: 0 in case of success, != 0 in case of error
481 */
482static int propagate_exception(struct dev_cgroup *devcg_root,
483 struct dev_exception_item *ex)
484{
485 struct cgroup *root = devcg_root->css.cgroup;
486 struct dev_cgroup *devcg, *parent, *tmp;
487 int rc = 0;
488 LIST_HEAD(pending);
489
490 get_online_devcg(root, &pending);
491
492 list_for_each_entry_safe(devcg, tmp, &pending, propagate_pending) {
493 parent = cgroup_to_devcgroup(devcg->css.cgroup->parent);
494
495 /*
496 * in case both root's behavior and devcg is allow, a new
497 * restriction means adding to the exception list
498 */
499 if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
500 devcg->behavior == DEVCG_DEFAULT_ALLOW) {
501 rc = dev_exception_add(devcg, ex);
502 if (rc)
503 break;
504 } else {
505 /*
506 * in the other possible cases:
507 * root's behavior: allow, devcg's: deny
508 * root's behavior: deny, devcg's: deny
509 * the exception will be removed
510 */
511 dev_exception_rm(devcg, ex);
512 }
513 revalidate_active_exceptions(devcg);
514
515 list_del_init(&devcg->propagate_pending);
516 }
517 return rc;
518}
519
520static inline bool has_children(struct dev_cgroup *devcgroup)
521{
522 struct cgroup *cgrp = devcgroup->css.cgroup;
523
524 return !list_empty(&cgrp->children);
525}
526
377/* 527/*
378 * Modify the exception list using allow/deny rules. 528 * Modify the exception list using allow/deny rules.
379 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD 529 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
@@ -392,7 +542,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
392{ 542{
393 const char *b; 543 const char *b;
394 char temp[12]; /* 11 + 1 characters needed for a u32 */ 544 char temp[12]; /* 11 + 1 characters needed for a u32 */
395 int count, rc; 545 int count, rc = 0;
396 struct dev_exception_item ex; 546 struct dev_exception_item ex;
397 struct cgroup *p = devcgroup->css.cgroup; 547 struct cgroup *p = devcgroup->css.cgroup;
398 struct dev_cgroup *parent = NULL; 548 struct dev_cgroup *parent = NULL;
@@ -410,6 +560,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
410 case 'a': 560 case 'a':
411 switch (filetype) { 561 switch (filetype) {
412 case DEVCG_ALLOW: 562 case DEVCG_ALLOW:
563 if (has_children(devcgroup))
564 return -EINVAL;
565
413 if (!may_allow_all(parent)) 566 if (!may_allow_all(parent))
414 return -EPERM; 567 return -EPERM;
415 dev_exception_clean(devcgroup); 568 dev_exception_clean(devcgroup);
@@ -423,6 +576,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
423 return rc; 576 return rc;
424 break; 577 break;
425 case DEVCG_DENY: 578 case DEVCG_DENY:
579 if (has_children(devcgroup))
580 return -EINVAL;
581
426 dev_exception_clean(devcgroup); 582 dev_exception_clean(devcgroup);
427 devcgroup->behavior = DEVCG_DEFAULT_DENY; 583 devcgroup->behavior = DEVCG_DEFAULT_DENY;
428 break; 584 break;
@@ -517,22 +673,28 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
517 dev_exception_rm(devcgroup, &ex); 673 dev_exception_rm(devcgroup, &ex);
518 return 0; 674 return 0;
519 } 675 }
520 return dev_exception_add(devcgroup, &ex); 676 rc = dev_exception_add(devcgroup, &ex);
677 break;
521 case DEVCG_DENY: 678 case DEVCG_DENY:
522 /* 679 /*
523 * If the default policy is to deny by default, try to remove 680 * If the default policy is to deny by default, try to remove
524 * an matching exception instead. And be silent about it: we 681 * an matching exception instead. And be silent about it: we
525 * don't want to break compatibility 682 * don't want to break compatibility
526 */ 683 */
527 if (devcgroup->behavior == DEVCG_DEFAULT_DENY) { 684 if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
528 dev_exception_rm(devcgroup, &ex); 685 dev_exception_rm(devcgroup, &ex);
529 return 0; 686 else
530 } 687 rc = dev_exception_add(devcgroup, &ex);
531 return dev_exception_add(devcgroup, &ex); 688
689 if (rc)
690 break;
691 /* we only propagate new restrictions */
692 rc = propagate_exception(devcgroup, &ex);
693 break;
532 default: 694 default:
533 return -EINVAL; 695 rc = -EINVAL;
534 } 696 }
535 return 0; 697 return rc;
536} 698}
537 699
538static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, 700static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
@@ -571,17 +733,10 @@ struct cgroup_subsys devices_subsys = {
571 .can_attach = devcgroup_can_attach, 733 .can_attach = devcgroup_can_attach,
572 .css_alloc = devcgroup_css_alloc, 734 .css_alloc = devcgroup_css_alloc,
573 .css_free = devcgroup_css_free, 735 .css_free = devcgroup_css_free,
736 .css_online = devcgroup_online,
737 .css_offline = devcgroup_offline,
574 .subsys_id = devices_subsys_id, 738 .subsys_id = devices_subsys_id,
575 .base_cftypes = dev_cgroup_files, 739 .base_cftypes = dev_cgroup_files,
576
577 /*
578 * While devices cgroup has the rudimentary hierarchy support which
579 * checks the parent's restriction, it doesn't properly propagates
580 * config changes in ancestors to their descendents. A child
581 * should only be allowed to add more restrictions to the parent's
582 * configuration. Fix it and remove the following.
583 */
584 .broken_hierarchy = true,
585}; 740};
586 741
587/** 742/**
@@ -609,7 +764,7 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
609 764
610 rcu_read_lock(); 765 rcu_read_lock();
611 dev_cgroup = task_devcgroup(current); 766 dev_cgroup = task_devcgroup(current);
612 rc = may_access(dev_cgroup, &ex); 767 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
613 rcu_read_unlock(); 768 rcu_read_unlock();
614 769
615 if (!rc) 770 if (!rc)