From ec970f2dff9a0feb57e361ca8e25aa666600cf2c Mon Sep 17 00:00:00 2001 From: Liu Hua Date: Tue, 19 Feb 2019 18:51:25 +0800 Subject: [PATCH] vm:isolate max_map_count by pid namespace Signed-off-by: Zhiguang Peng Signed-off-by: Liu Hua --- include/linux/pid_namespace.h | 1 + kernel/pid.c | 2 ++ kernel/pid_namespace.c | 2 ++ kernel/sysctl.c | 14 +++++++++++++- mm/madvise.c | 14 ++++++++++++-- mm/mmap.c | 16 ++++++++++++++++ mm/mremap.c | 4 ++++ mm/nommu.c | 4 ++++ 8 files changed, 54 insertions(+), 3 deletions(-) diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index c78af6061..5eb7413f9 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -53,6 +53,7 @@ struct pid_namespace { int hide_pid; int reboot; /* group exit code if this pidns was rebooted */ struct ns_common ns; + int max_map_count; } __randomize_layout; extern struct pid_namespace init_pid_ns; diff --git a/kernel/pid.c b/kernel/pid.c index 020dedbdf..72f77a021 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -39,6 +39,7 @@ #include #include #include +#include #define pid_hashfn(nr, ns) \ hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift) @@ -78,6 +79,7 @@ struct pid_namespace init_pid_ns = { .level = 0, .child_reaper = &init_task, .user_ns = &init_user_ns, + .max_map_count = DEFAULT_MAX_MAP_COUNT, .ns.inum = PROC_PID_INIT_INO, #ifdef CONFIG_PID_NS .ns.ops = &pidns_operations, diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index 491831489..01b2d3644 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -21,6 +21,7 @@ #include #include #include +#include struct pid_cache { int nr_ids; @@ -136,6 +137,7 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; ns->nr_hashed = PIDNS_HASH_ADDING; + ns->max_map_count = DEFAULT_MAX_MAP_COUNT; INIT_WORK(&ns->proc_work, proc_cleanup_work); set_bit(0, ns->pidmap[0].page); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 11116de08..0a9ae6c2f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -208,6 +208,9 @@ static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +static int proc_dointvec_max_map_count(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); + #ifdef CONFIG_PRINTK static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -1509,7 +1512,7 @@ static struct ctl_table vm_table[] = { .data = &sysctl_max_map_count, .maxlen = sizeof(sysctl_max_map_count), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dointvec_max_map_count, .extra1 = &zero, }, #else @@ -2523,6 +2526,15 @@ int proc_douintvec(struct ctl_table *table, int write, do_proc_douintvec_conv, NULL); } +static int proc_dointvec_max_map_count(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ +#ifdef CONFIG_PID_NS + table->data = &task_active_pid_ns(current)->max_map_count; +#endif + return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL); +} + /* * Taint values can only be increased * This means we can safely use a temporary. diff --git a/mm/madvise.c b/mm/madvise.c index 576b753be..ffb77f924 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -147,7 +147,12 @@ static long madvise_behavior(struct vm_area_struct *vma, *prev = vma; if (start != vma->vm_start) { - if (unlikely(mm->map_count >= sysctl_max_map_count)) { +#ifdef CONFIG_PID_NS + if (unlikely(mm->map_count >= task_active_pid_ns(current)->max_map_count)) +#else + if (unlikely(mm->map_count >= sysctl_max_map_count)) +#endif + { error = -ENOMEM; goto out; } @@ -164,7 +169,12 @@ static long madvise_behavior(struct vm_area_struct *vma, } if (end != vma->vm_end) { - if (unlikely(mm->map_count >= sysctl_max_map_count)) { +#ifdef CONFIG_PID_NS + if (unlikely(mm->map_count >= task_active_pid_ns(current)->max_map_count)) +#else + if (unlikely(mm->map_count >= sysctl_max_map_count)) +#endif + { error = -ENOMEM; goto out; } diff --git a/mm/mmap.c b/mm/mmap.c index 00dab291e..6515645d1 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1384,7 +1384,11 @@ unsigned long do_mmap(struct file *file, unsigned long addr, return -EOVERFLOW; /* Too many mappings? */ +#ifdef CONFIG_PID_NS + if (mm->map_count > task_active_pid_ns(current)->max_map_count) +#else if (mm->map_count > sysctl_max_map_count) +#endif return -ENOMEM; /* Obtain the address to map to. we verify (or select) it and ensure @@ -2637,7 +2641,11 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, int split_vma(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, int new_below) { +#ifdef CONFIG_PID_NS + if (mm->map_count >= task_active_pid_ns(current)->max_map_count) +#else if (mm->map_count >= sysctl_max_map_count) +#endif return -ENOMEM; return __split_vma(mm, vma, addr, new_below); @@ -2688,7 +2696,11 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, * not exceed its limit; but let map_count go just above * its limit temporarily, to help free resources as expected. */ +#ifdef CONFIG_PID_NS + if (end < vma->vm_end && mm->map_count >= task_active_pid_ns(current)->max_map_count) +#else if (end < vma->vm_end && mm->map_count >= sysctl_max_map_count) +#endif return -ENOMEM; error = __split_vma(mm, vma, start, 0); @@ -2926,7 +2938,11 @@ static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long fla if (!may_expand_vm(mm, flags, len >> PAGE_SHIFT)) return -ENOMEM; +#ifdef CONFIG_PID_NS + if (mm->map_count > task_active_pid_ns(current)->max_map_count) +#else if (mm->map_count > sysctl_max_map_count) +#endif return -ENOMEM; if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT)) diff --git a/mm/mremap.c b/mm/mremap.c index 88ceeb4ef..ccc0e173a 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -277,7 +277,11 @@ static unsigned long move_vma(struct vm_area_struct *vma, * We'd prefer to avoid failure later on in do_munmap: * which may split one vma into three before unmapping. */ +#ifdef CONFIG_PID_NS + if (mm->map_count >= task_active_pid_ns(current)->max_map_count - 3) +#else if (mm->map_count >= sysctl_max_map_count - 3) +#endif return -ENOMEM; /* diff --git a/mm/nommu.c b/mm/nommu.c index 17c00d93d..33f987f57 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1487,7 +1487,11 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (vma->vm_file) return -ENOMEM; +#ifdef CONFIG_PID_NS + if (mm->map_count >= task_active_pid_ns(current)->max_map_count) +#else if (mm->map_count >= sysctl_max_map_count) +#endif return -ENOMEM; region = kmem_cache_alloc(vm_region_jar, GFP_KERNEL);