[PATCH RFC 2/2] rlimits: report resource limits violations

From: Yauheni Kaliuta
Date: Wed Sep 07 2016 - 06:27:45 EST


The patch instrument different places of resource limits checks with
reporting using the infrastructure from the previous patch.

Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@xxxxxxxxxx>
---
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(&current->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(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
- retval = -EAGAIN;
- goto out_ret;
+ if (current->flags & PF_NPROC_EXCEEDED) {
+ int nproc;
+
+ nproc = atomic_read(&current_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(&current->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(&current->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