diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2012-12-28 01:27:29 -0500 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2013-01-27 01:12:04 -0500 |
commit | 0bd14b4fd72afd5df41e9fd59f356740f22fceba (patch) | |
tree | bf1bc8dfa507ca40970927efb2dd87b4f5bdd416 /kernel/user_namespace.c | |
parent | c61a2810a2161986353705b44d9503e6bb079f4f (diff) |
userns: Allow any uid or gid mappings that don't overlap.
When I initially wrote the code for /proc/<pid>/uid_map. I was lazy
and avoided duplicate mappings by the simple expedient of ensuring the
first number in a new extent was greater than any number in the
previous extent.
Unfortunately that precludes a number of valid mappings, and someone
noticed and complained. So use a simple check to ensure that ranges
in the mapping extents don't overlap.
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'kernel/user_namespace.c')
-rw-r--r-- | kernel/user_namespace.c | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 24f8ec3b64d8..8b650837083e 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -520,6 +520,42 @@ struct seq_operations proc_projid_seq_operations = { | |||
520 | .show = projid_m_show, | 520 | .show = projid_m_show, |
521 | }; | 521 | }; |
522 | 522 | ||
523 | static bool mappings_overlap(struct uid_gid_map *new_map, struct uid_gid_extent *extent) | ||
524 | { | ||
525 | u32 upper_first, lower_first, upper_last, lower_last; | ||
526 | unsigned idx; | ||
527 | |||
528 | upper_first = extent->first; | ||
529 | lower_first = extent->lower_first; | ||
530 | upper_last = upper_first + extent->count - 1; | ||
531 | lower_last = lower_first + extent->count - 1; | ||
532 | |||
533 | for (idx = 0; idx < new_map->nr_extents; idx++) { | ||
534 | u32 prev_upper_first, prev_lower_first; | ||
535 | u32 prev_upper_last, prev_lower_last; | ||
536 | struct uid_gid_extent *prev; | ||
537 | |||
538 | prev = &new_map->extent[idx]; | ||
539 | |||
540 | prev_upper_first = prev->first; | ||
541 | prev_lower_first = prev->lower_first; | ||
542 | prev_upper_last = prev_upper_first + prev->count - 1; | ||
543 | prev_lower_last = prev_lower_first + prev->count - 1; | ||
544 | |||
545 | /* Does the upper range intersect a previous extent? */ | ||
546 | if ((prev_upper_first <= upper_last) && | ||
547 | (prev_upper_last >= upper_first)) | ||
548 | return true; | ||
549 | |||
550 | /* Does the lower range intersect a previous extent? */ | ||
551 | if ((prev_lower_first <= lower_last) && | ||
552 | (prev_lower_last >= lower_first)) | ||
553 | return true; | ||
554 | } | ||
555 | return false; | ||
556 | } | ||
557 | |||
558 | |||
523 | static DEFINE_MUTEX(id_map_mutex); | 559 | static DEFINE_MUTEX(id_map_mutex); |
524 | 560 | ||
525 | static ssize_t map_write(struct file *file, const char __user *buf, | 561 | static ssize_t map_write(struct file *file, const char __user *buf, |
@@ -532,7 +568,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, | |||
532 | struct user_namespace *ns = seq->private; | 568 | struct user_namespace *ns = seq->private; |
533 | struct uid_gid_map new_map; | 569 | struct uid_gid_map new_map; |
534 | unsigned idx; | 570 | unsigned idx; |
535 | struct uid_gid_extent *extent, *last = NULL; | 571 | struct uid_gid_extent *extent = NULL; |
536 | unsigned long page = 0; | 572 | unsigned long page = 0; |
537 | char *kbuf, *pos, *next_line; | 573 | char *kbuf, *pos, *next_line; |
538 | ssize_t ret = -EINVAL; | 574 | ssize_t ret = -EINVAL; |
@@ -635,14 +671,11 @@ static ssize_t map_write(struct file *file, const char __user *buf, | |||
635 | if ((extent->lower_first + extent->count) <= extent->lower_first) | 671 | if ((extent->lower_first + extent->count) <= extent->lower_first) |
636 | goto out; | 672 | goto out; |
637 | 673 | ||
638 | /* For now only accept extents that are strictly in order */ | 674 | /* Do the ranges in extent overlap any previous extents? */ |
639 | if (last && | 675 | if (mappings_overlap(&new_map, extent)) |
640 | (((last->first + last->count) > extent->first) || | ||
641 | ((last->lower_first + last->count) > extent->lower_first))) | ||
642 | goto out; | 676 | goto out; |
643 | 677 | ||
644 | new_map.nr_extents++; | 678 | new_map.nr_extents++; |
645 | last = extent; | ||
646 | 679 | ||
647 | /* Fail if the file contains too many extents */ | 680 | /* Fail if the file contains too many extents */ |
648 | if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && | 681 | if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && |