From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757179AbcIGK1l (ORCPT ); Wed, 7 Sep 2016 06:27:41 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46024 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757006AbcIGK1U (ORCPT ); Wed, 7 Sep 2016 06:27:20 -0400 From: Yauheni Kaliuta To: linux-kernel@vger.kernel.org Cc: aris@redhat.com, jolsa@redhat.com Subject: [PATCH RFC 2/2] rlimits: report resource limits violations Date: Wed, 7 Sep 2016 13:27:35 +0300 Message-Id: <1473244055-25240-3-git-send-email-yauheni.kaliuta@redhat.com> In-Reply-To: <1473244055-25240-1-git-send-email-yauheni.kaliuta@redhat.com> References: <20160824112428.GA15743@krava> <1473244055-25240-1-git-send-email-yauheni.kaliuta@redhat.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Wed, 07 Sep 2016 10:27:19 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The patch instrument different places of resource limits checks with reporting using the infrastructure from the previous patch. Signed-off-by: Yauheni Kaliuta --- arch/ia64/kernel/perfmon.c | 4 +++- arch/powerpc/kvm/book3s_64_vio.c | 6 ++++-- arch/powerpc/mm/mmu_context_iommu.c | 6 ++++-- drivers/android/binder.c | 7 ++++++- drivers/infiniband/core/umem.c | 1 + drivers/infiniband/hw/hfi1/user_pages.c | 5 ++++- drivers/infiniband/hw/qib/qib_user_pages.c | 1 + drivers/infiniband/hw/usnic/usnic_uiom.c | 1 + drivers/misc/mic/scif/scif_rma.c | 1 + drivers/vfio/vfio_iommu_spapr_tce.c | 6 ++++-- drivers/vfio/vfio_iommu_type1.c | 4 ++++ fs/attr.c | 4 +++- fs/binfmt_aout.c | 4 +++- fs/binfmt_flat.c | 1 + fs/coredump.c | 4 +++- fs/exec.c | 14 ++++++++++---- fs/file.c | 26 +++++++++++++++++++++----- fs/select.c | 4 +++- include/linux/mm.h | 7 ++++++- ipc/mqueue.c | 10 ++++++++-- ipc/shm.c | 1 + kernel/bpf/syscall.c | 15 ++++++++++++--- kernel/events/core.c | 1 + kernel/fork.c | 9 ++++++--- kernel/sched/core.c | 17 +++++++++++++---- kernel/signal.c | 7 ++++--- kernel/sys.c | 9 ++++++--- kernel/time/posix-cpu-timers.c | 8 ++++++++ mm/mlock.c | 14 +++++++++++++- mm/mmap.c | 19 +++++++++++++++---- mm/mremap.c | 4 +++- net/unix/af_unix.c | 9 ++++++--- 32 files changed, 179 insertions(+), 50 deletions(-) diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 2436ad5f92c1..c765e94a7089 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2259,8 +2259,10 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t * if ((mm->total_vm << PAGE_SHIFT) + len> task->rlim[RLIMIT_AS].rlim_cur) * return -ENOMEM; */ - if (size > task_rlimit(task, RLIMIT_MEMLOCK)) + if (size > task_rlimit(task, RLIMIT_MEMLOCK)) { + rlimit_exceeded_task(RLIMIT_MEMLOCK, size, task); return -ENOMEM; + } /* * We do the easy to undo allocations first. diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c index c379ff5a4438..a0477260d398 100644 --- a/arch/powerpc/kvm/book3s_64_vio.c +++ b/arch/powerpc/kvm/book3s_64_vio.c @@ -67,10 +67,12 @@ static long kvmppc_account_memlimit(unsigned long stt_pages, bool inc) locked = current->mm->locked_vm + stt_pages; lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -ENOMEM; - else + } else { current->mm->locked_vm += stt_pages; + } } else { if (WARN_ON_ONCE(stt_pages > current->mm->locked_vm)) stt_pages = current->mm->locked_vm; diff --git a/arch/powerpc/mm/mmu_context_iommu.c b/arch/powerpc/mm/mmu_context_iommu.c index da6a2168ae9e..421890d325df 100644 --- a/arch/powerpc/mm/mmu_context_iommu.c +++ b/arch/powerpc/mm/mmu_context_iommu.c @@ -42,10 +42,12 @@ static long mm_iommu_adjust_locked_vm(struct mm_struct *mm, if (incr) { locked = mm->locked_vm + npages; lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -ENOMEM; - else + } else { mm->locked_vm += npages; + } } else { if (WARN_ON_ONCE(npages > mm->locked_vm)) npages = mm->locked_vm; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 16288e777ec3..a44021051a02 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -379,6 +379,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) struct files_struct *files = proc->files; unsigned long rlim_cur; unsigned long irqs; + int ret; if (files == NULL) return -ESRCH; @@ -389,7 +390,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); unlock_task_sighand(proc->tsk, &irqs); - return __alloc_fd(files, 0, rlim_cur, flags); + ret = __alloc_fd(files, 0, rlim_cur, flags); + if (ret == -EMFILE) + rlimit_exceeded_task(RLIMIT_NOFILE, (u64)-1, proc->tsk); + + return ret; } /* diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index c68746ce6624..8d7746b3a5c9 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -168,6 +168,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -ENOMEM; goto out; } diff --git a/drivers/infiniband/hw/hfi1/user_pages.c b/drivers/infiniband/hw/hfi1/user_pages.c index 20f4ddcac3b0..1f510a13ed3b 100644 --- a/drivers/infiniband/hw/hfi1/user_pages.c +++ b/drivers/infiniband/hw/hfi1/user_pages.c @@ -95,8 +95,11 @@ bool hfi1_can_pin_pages(struct hfi1_devdata *dd, struct mm_struct *mm, up_read(&mm->mmap_sem); /* First, check the absolute limit against all pinned pages. */ - if (pinned + npages >= ulimit && !can_lock) + if (pinned + npages >= ulimit && !can_lock) { + /* if it's in pages, should be converted to bytes? */ + rlimit_exceeded(RLIMIT_MEMLOCK, pinned + npages); return false; + } return ((nlocked + npages) <= size) || can_lock; } diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index 2d2b94fd3633..649a0a1317bb 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -61,6 +61,7 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if (num_pages > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, num_pages << PAGE_SHIFT); ret = -ENOMEM; goto bail; } diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index a0b6ebee4d8a..11367fcc238b 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -129,6 +129,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -ENOMEM; goto out; } diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index e0203b1a20fd..0e83d14cda06 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -303,6 +303,7 @@ static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, dev_err(scif_info.mdev.this_device, "locked(%lu) > lock_limit(%lu)\n", locked, lock_limit); + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); return -ENOMEM; } mm->pinned_vm = locked; diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 80378ddadc5c..5ff1773b01f4 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -44,10 +44,12 @@ static long try_increment_locked_vm(long npages) down_write(¤t->mm->mmap_sem); locked = current->mm->locked_vm + npages; lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -ENOMEM; - else + } else { current->mm->locked_vm += npages; + } pr_debug("[%d] RLIMIT_MEMLOCK +%ld %ld/%ld%s\n", current->pid, npages << PAGE_SHIFT, diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 2ba19424e4a1..6929c0eaac9d 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -280,6 +280,8 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, put_pfn(*pfn_base, prot); pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, limit << PAGE_SHIFT); + rlimit_exceeded(RLIMIT_MEMLOCK, + (current->mm->locked_vm + 1) << PAGE_SHIFT); return -ENOMEM; } @@ -308,6 +310,8 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, put_pfn(pfn, prot); pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, limit << PAGE_SHIFT); + rlimit_exceeded(RLIMIT_MEMLOCK, + (current->mm->locked_vm + i + 1) << PAGE_SHIFT); break; } } diff --git a/fs/attr.c b/fs/attr.c index 42bb42bb3c72..62d3de88ab42 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -102,8 +102,10 @@ int inode_newsize_ok(const struct inode *inode, loff_t offset) unsigned long limit; limit = rlimit(RLIMIT_FSIZE); - if (limit != RLIM_INFINITY && offset > limit) + if (limit != RLIM_INFINITY && offset > limit) { + rlimit_exceeded(RLIMIT_FSIZE, offset); goto out_sig; + } if (offset > inode->i_sb->s_maxbytes) goto out_big; } else { diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index ae1b5404fced..9041ef2d419a 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -228,8 +228,10 @@ static int load_aout_binary(struct linux_binprm * bprm) rlim = rlimit(RLIMIT_DATA); if (rlim >= RLIM_INFINITY) rlim = ~0; - if (ex.a_data + ex.a_bss > rlim) + if (ex.a_data + ex.a_bss > rlim) { + rlimit_exceeded(RLIMIT_DATA, data_len + bss_len); return -ENOMEM; + } /* Flush all traces of the currently running executable */ retval = flush_old_exec(bprm); diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 9b2917a30294..042864d44dff 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -512,6 +512,7 @@ static int load_flat_file(struct linux_binprm *bprm, if (rlim >= RLIM_INFINITY) rlim = ~0; if (data_len + bss_len > rlim) { + rlimit_exceeded(RLIMIT_DATA, data_len + bss_len); ret = -ENOMEM; goto err; } diff --git a/fs/coredump.c b/fs/coredump.c index 281b768000e6..8c7b6cadf262 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -784,8 +784,10 @@ int dump_emit(struct coredump_params *cprm, const void *addr, int nr) struct file *file = cprm->file; loff_t pos = file->f_pos; ssize_t n; - if (cprm->written + nr > cprm->limit) + if (cprm->written + nr > cprm->limit) { + rlimit_exceeded(RLIMIT_CORE, cprm->written + nr); return 0; + } while (nr) { if (dump_interrupted()) return 0; diff --git a/fs/exec.c b/fs/exec.c index 6fcfb3f7b137..6edc0eeeece0 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -230,6 +230,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, */ rlim = current->signal->rlim; if (size > ACCESS_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) { + /* should it be reported somehow? */ put_page(page); return NULL; } @@ -1650,10 +1651,15 @@ static int do_execveat_common(int fd, struct filename *filename, * don't check setuid() return code. Here we additionally recheck * whether NPROC limit is still exceeded. */ - if ((current->flags & PF_NPROC_EXCEEDED) && - atomic_read(¤t_user()->processes) > rlimit(RLIMIT_NPROC)) { - retval = -EAGAIN; - goto out_ret; + if (current->flags & PF_NPROC_EXCEEDED) { + int nproc; + + nproc = atomic_read(¤t_user()->processes); + if (nproc > rlimit(RLIMIT_NPROC)) { + rlimit_exceeded(RLIMIT_NPROC, nproc); + retval = -EAGAIN; + goto out_ret; + } } /* We're below the limit (still or again), so we don't want to make diff --git a/fs/file.c b/fs/file.c index 6b1acdfe59da..d76fbb15e4ec 100644 --- a/fs/file.c +++ b/fs/file.c @@ -554,12 +554,22 @@ out: static int alloc_fd(unsigned start, unsigned flags) { - return __alloc_fd(current->files, start, rlimit(RLIMIT_NOFILE), flags); + int ret; + + ret = __alloc_fd(current->files, start, rlimit(RLIMIT_NOFILE), flags); + if (ret == -EMFILE) + rlimit_exceeded(RLIMIT_NOFILE, (u64)-1); + return ret; } int get_unused_fd_flags(unsigned flags) { - return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags); + int ret; + + ret = __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags); + if (ret == -EMFILE) + rlimit_exceeded(RLIMIT_NOFILE, (u64)-1); + return ret; } EXPORT_SYMBOL(get_unused_fd_flags); @@ -872,8 +882,10 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags) if (!file) return __close_fd(files, fd); - if (fd >= rlimit(RLIMIT_NOFILE)) + if (fd >= rlimit(RLIMIT_NOFILE)) { + rlimit_exceeded(RLIMIT_NOFILE, fd); return -EBADF; + } spin_lock(&files->file_lock); err = expand_files(files, fd); @@ -898,8 +910,10 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) if (unlikely(oldfd == newfd)) return -EINVAL; - if (newfd >= rlimit(RLIMIT_NOFILE)) + if (newfd >= rlimit(RLIMIT_NOFILE)) { + rlimit_exceeded(RLIMIT_NOFILE, newfd); return -EBADF; + } spin_lock(&files->file_lock); err = expand_files(files, newfd); @@ -953,8 +967,10 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes) int f_dupfd(unsigned int from, struct file *file, unsigned flags) { int err; - if (from >= rlimit(RLIMIT_NOFILE)) + if (from >= rlimit(RLIMIT_NOFILE)) { + rlimit_exceeded(RLIMIT_NOFILE, from); return -EINVAL; + } err = alloc_fd(from, flags); if (err >= 0) { get_file(file); diff --git a/fs/select.c b/fs/select.c index 8ed9da50896a..adb057ce7897 100644 --- a/fs/select.c +++ b/fs/select.c @@ -886,8 +886,10 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, struct poll_list *walk = head; unsigned long todo = nfds; - if (nfds > rlimit(RLIMIT_NOFILE)) + if (nfds > rlimit(RLIMIT_NOFILE)) { + rlimit_exceeded(RLIMIT_NOFILE, nfds); return -EINVAL; + } len = min_t(unsigned int, nfds, N_STACK_PPS); for (;;) { diff --git a/include/linux/mm.h b/include/linux/mm.h index ef815b9cd426..1ef5ed878895 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2002,8 +2002,13 @@ static inline int check_data_rlimit(unsigned long rlim, unsigned long start_data) { if (rlim < RLIM_INFINITY) { - if (((new - start) + (end_data - start_data)) > rlim) + unsigned long data_size; + + data_size = (new - start) + (end_data - start_data); + if (data_size > rlim) { + rlimit_exceeded(RLIMIT_DATA, data_size); return -ENOSPC; + } } return 0; diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 0b13ace266f2..85ac1b643522 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -275,13 +275,19 @@ static struct inode *mqueue_get_inode(struct super_block *sb, info->attr.mq_msgsize); spin_lock(&mq_lock); - if (u->mq_bytes + mq_bytes < u->mq_bytes || - u->mq_bytes + mq_bytes > rlimit(RLIMIT_MSGQUEUE)) { + if (u->mq_bytes + mq_bytes < u->mq_bytes) { spin_unlock(&mq_lock); /* mqueue_evict_inode() releases info->messages */ ret = -EMFILE; goto out_inode; } + if (u->mq_bytes + mq_bytes > rlimit(RLIMIT_MSGQUEUE)) { + spin_unlock(&mq_lock); + rlimit_exceeded(RLIMIT_MSGQUEUE, u->mq_bytes + mq_bytes); + /* mqueue_evict_inode() releases info->messages */ + ret = -EMFILE; + goto out_inode; + } u->mq_bytes += mq_bytes; spin_unlock(&mq_lock); diff --git a/ipc/shm.c b/ipc/shm.c index dbac8860c721..640f17ae6094 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1034,6 +1034,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) goto out_unlock0; } if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, (u64)-1); err = -EPERM; goto out_unlock0; } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 228f962447a5..8494c1fe921e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -56,8 +56,11 @@ int bpf_map_precharge_memlock(u32 pages) memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur = atomic_long_read(&user->locked_vm); free_uid(user); - if (cur + pages > memlock_limit) + if (cur + pages > memlock_limit) { + rlimit_exceeded(RLIMIT_MEMLOCK, + (cur + pages) << PAGE_SHIFT); return -EPERM; + } return 0; } @@ -65,14 +68,17 @@ static int bpf_map_charge_memlock(struct bpf_map *map) { struct user_struct *user = get_current_user(); unsigned long memlock_limit; + int npages; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; atomic_long_add(map->pages, &user->locked_vm); - if (atomic_long_read(&user->locked_vm) > memlock_limit) { + npages = atomic_long_read(&user->locked_vm); + if (npages > memlock_limit) { atomic_long_sub(map->pages, &user->locked_vm); free_uid(user); + rlimit_exceeded(RLIMIT_MEMLOCK, npages << PAGE_SHIFT); return -EPERM; } map->user = user; @@ -603,13 +609,16 @@ static int bpf_prog_charge_memlock(struct bpf_prog *prog) { struct user_struct *user = get_current_user(); unsigned long memlock_limit; + int npages; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; atomic_long_add(prog->pages, &user->locked_vm); - if (atomic_long_read(&user->locked_vm) > memlock_limit) { + npages = atomic_long_read(&user->locked_vm); + if (npages > memlock_limit) { atomic_long_sub(prog->pages, &user->locked_vm); free_uid(user); + rlimit_exceeded(RLIMIT_MEMLOCK, npages << PAGE_SHIFT); return -EPERM; } prog->aux->user = user; diff --git a/kernel/events/core.c b/kernel/events/core.c index 3cfabdf7b942..b74bf90d1fd4 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5166,6 +5166,7 @@ accounting: if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); ret = -EPERM; goto unlock; } diff --git a/kernel/fork.c b/kernel/fork.c index beb31725f7e2..a80f2e11788d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1307,6 +1307,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, { int retval; struct task_struct *p; + int nproc; if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) return ERR_PTR(-EINVAL); @@ -1368,11 +1369,13 @@ static struct task_struct *copy_process(unsigned long clone_flags, DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); #endif retval = -EAGAIN; - if (atomic_read(&p->real_cred->user->processes) >= - task_rlimit(p, RLIMIT_NPROC)) { + nproc = atomic_read(&p->real_cred->user->processes); + if (nproc >= task_rlimit(p, RLIMIT_NPROC)) { if (p->real_cred->user != INIT_USER && - !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) + !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { + rlimit_exceeded_task(RLIMIT_NPROC, nproc, p); goto bad_fork_free; + } } current->flags &= ~PF_NPROC_EXCEEDED; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2a906f20fba7..1c66b3088684 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3736,9 +3736,13 @@ int can_nice(const struct task_struct *p, const int nice) { /* convert nice value [19,-20] to rlimit style value [1,40] */ int nice_rlim = nice_to_rlimit(nice); + int ret; - return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) || - capable(CAP_SYS_NICE)); + ret = (nice_rlim <= task_rlimit(p, RLIMIT_NICE) || + capable(CAP_SYS_NICE)); + if (!ret) + rlimit_exceeded(RLIMIT_NICE, nice_rlim); + return ret; } #ifdef __ARCH_WANT_SYS_NICE @@ -4070,13 +4074,18 @@ recheck: task_rlimit(p, RLIMIT_RTPRIO); /* can't set/change the rt policy */ - if (policy != p->policy && !rlim_rtprio) + if (policy != p->policy && !rlim_rtprio) { + rlimit_exceeded(RLIMIT_RTPRIO, (u64)-1); return -EPERM; + } /* can't increase priority */ if (attr->sched_priority > p->rt_priority && - attr->sched_priority > rlim_rtprio) + attr->sched_priority > rlim_rtprio) { + rlimit_exceeded(RLIMIT_RTPRIO, + attr->sched_priority); return -EPERM; + } } /* diff --git a/kernel/signal.c b/kernel/signal.c index af21afc00d08..1c03ca7484f7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -362,6 +362,7 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi { struct sigqueue *q = NULL; struct user_struct *user; + int nsigs; /* * Protect access to @t credentials. This can go away when all @@ -372,11 +373,11 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi atomic_inc(&user->sigpending); rcu_read_unlock(); - if (override_rlimit || - atomic_read(&user->sigpending) <= - task_rlimit(t, RLIMIT_SIGPENDING)) { + nsigs = atomic_read(&user->sigpending); + if (override_rlimit || nsigs <= task_rlimit(t, RLIMIT_SIGPENDING)) { q = kmem_cache_alloc(sigqueue_cachep, flags); } else { + rlimit_exceeded_task(RLIMIT_SIGPENDING, nsigs, t); print_dropped_signal(sig); } diff --git a/kernel/sys.c b/kernel/sys.c index 89d5be418157..28b718ac1fb1 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -421,6 +421,7 @@ error: static int set_user(struct cred *new) { struct user_struct *new_user; + int nproc; new_user = alloc_uid(new->uid); if (!new_user) @@ -433,11 +434,13 @@ static int set_user(struct cred *new) * for programs doing set*uid()+execve() by harmlessly deferring the * failure to the execve() stage. */ - if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) && - new_user != INIT_USER) + nproc = atomic_read(&new_user->processes); + if (nproc >= rlimit(RLIMIT_NPROC) && new_user != INIT_USER) { + rlimit_exceeded(RLIMIT_NPROC, nproc); current->flags |= PF_NPROC_EXCEEDED; - else + } else { current->flags &= ~PF_NPROC_EXCEEDED; + } free_uid(new->user); new->user = new_user; diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 39008d78927a..ce50f2166776 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -860,6 +860,9 @@ static void check_thread_timers(struct task_struct *tsk, if (hard != RLIM_INFINITY && tsk->rt.timeout > DIV_ROUND_UP(hard, USEC_PER_SEC/HZ)) { + rlimit_hard_exceeded_task(RLIMIT_RTTIME, + tsk->rt.timeout, + tsk); /* * At the hard limit, we just die. * No need to calculate anything else now. @@ -875,6 +878,9 @@ static void check_thread_timers(struct task_struct *tsk, soft += USEC_PER_SEC; sig->rlim[RLIMIT_RTTIME].rlim_cur = soft; } + rlimit_exceeded_task(RLIMIT_RTTIME, + tsk->rt.timeout, + tsk); printk(KERN_INFO "RT Watchdog Timeout: %s[%d]\n", tsk->comm, task_pid_nr(tsk)); @@ -980,6 +986,7 @@ static void check_process_timers(struct task_struct *tsk, READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_max); cputime_t x; if (psecs >= hard) { + rlimit_hard_exceeded(RLIMIT_CPU, psecs); /* * At the hard limit, we just die. * No need to calculate anything else now. @@ -988,6 +995,7 @@ static void check_process_timers(struct task_struct *tsk, return; } if (psecs >= soft) { + rlimit_exceeded(RLIMIT_CPU, psecs); /* * At the soft limit, send a SIGXCPU every second. */ diff --git a/mm/mlock.c b/mm/mlock.c index 14645be06e30..016c7089db04 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -28,6 +28,9 @@ bool can_do_mlock(void) { if (rlimit(RLIMIT_MEMLOCK) != 0) return true; + else + rlimit_exceeded(RLIMIT_MEMLOCK, (u64)-1); + if (capable(CAP_IPC_LOCK)) return true; return false; @@ -643,6 +646,8 @@ static __must_check int do_mlock(unsigned long start, size_t len, vm_flags_t fla /* check against resource limits */ if ((locked <= lock_limit) || capable(CAP_IPC_LOCK)) error = apply_vma_lock_flags(start, len, flags); + else + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); up_write(¤t->mm->mmap_sem); if (error) @@ -757,6 +762,10 @@ SYSCALL_DEFINE1(mlockall, int, flags) if (!(flags & MCL_CURRENT) || (current->mm->total_vm <= lock_limit) || capable(CAP_IPC_LOCK)) ret = apply_mlockall_flags(flags); + else + rlimit_exceeded(RLIMIT_MEMLOCK, + current->mm->total_vm << PAGE_SHIFT); + up_write(¤t->mm->mmap_sem); if (!ret && (flags & MCL_CURRENT)) mm_populate(0, TASK_SIZE); @@ -793,8 +802,11 @@ int user_shm_lock(size_t size, struct user_struct *user) lock_limit >>= PAGE_SHIFT; spin_lock(&shmlock_user_lock); if (!allowed && - locked + user->locked_shm > lock_limit && !capable(CAP_IPC_LOCK)) + locked + user->locked_shm > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, + (locked + user->locked_shm) << PAGE_SHIFT); goto out; + } get_uid(user); user->locked_shm += locked; allowed = 1; diff --git a/mm/mmap.c b/mm/mmap.c index ca9d91bca0d6..500a247f1759 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1139,8 +1139,10 @@ static inline int mlock_future_check(struct mm_struct *mm, locked += mm->locked_vm; lock_limit = rlimit(RLIMIT_MEMLOCK); lock_limit >>= PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); return -EAGAIN; + } } return 0; } @@ -2012,8 +2014,10 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns actual_size = size; if (size && (vma->vm_flags & (VM_GROWSUP | VM_GROWSDOWN))) actual_size -= PAGE_SIZE; - if (actual_size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur)) + if (actual_size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur)) { + rlimit_exceeded(RLIMIT_STACK, actual_size); return -ENOMEM; + } /* mlock limit tests */ if (vma->vm_flags & VM_LOCKED) { @@ -2022,8 +2026,10 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns locked = mm->locked_vm + grow; limit = READ_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur); limit >>= PAGE_SHIFT; - if (locked > limit && !capable(CAP_IPC_LOCK)) + if (locked > limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); return -ENOMEM; + } } /* Check to ensure the stack will not grow into a hugetlb-only region */ @@ -2925,8 +2931,11 @@ out: */ bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags, unsigned long npages) { - if (mm->total_vm + npages > rlimit(RLIMIT_AS) >> PAGE_SHIFT) + if (mm->total_vm + npages > rlimit(RLIMIT_AS) >> PAGE_SHIFT) { + rlimit_exceeded(RLIMIT_AS, + (mm->total_vm + npages) << PAGE_SHIFT); return false; + } if (is_data_mapping(flags) && mm->data_vm + npages > rlimit(RLIMIT_DATA) >> PAGE_SHIFT) { @@ -2935,6 +2944,8 @@ bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags, unsigned long npages) mm->data_vm + npages <= rlimit_max(RLIMIT_DATA) >> PAGE_SHIFT) return true; if (!ignore_rlimit_data) { + rlimit_exceeded(RLIMIT_DATA, + (mm->data_vm + npages) << PAGE_SHIFT); pr_warn_once("%s (%d): VmData %lu exceed data ulimit %lu. Update limits or use boot option ignore_rlimit_data.\n", current->comm, current->pid, (mm->data_vm + npages) << PAGE_SHIFT, diff --git a/mm/mremap.c b/mm/mremap.c index da22ad2a5678..8755433ec79c 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -378,8 +378,10 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, locked = mm->locked_vm << PAGE_SHIFT; lock_limit = rlimit(RLIMIT_MEMLOCK); locked += new_len - old_len; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + rlimit_exceeded(RLIMIT_MEMLOCK, locked << PAGE_SHIFT); return ERR_PTR(-EAGAIN); + } } if (!may_expand_vm(mm, vma->vm_flags, diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index f1dffe84f0d5..c365e5ab9ace 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1523,10 +1523,13 @@ static void unix_destruct_scm(struct sk_buff *skb) static inline bool too_many_unix_fds(struct task_struct *p) { struct user_struct *user = current_user(); + bool ret = false; - if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE))) - return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); - return false; + if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE))) { + rlimit_exceeded_task(RLIMIT_NOFILE, user->unix_inflight, p); + ret = !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); + } + return ret; } #define MAX_RECURSION_LEVEL 4 -- 2.7.4