Commit a293980c authored by Neil Horman's avatar Neil Horman Committed by Linus Torvalds

exec: let do_coredump() limit the number of concurrent dumps to pipes

Introduce core pipe limiting sysctl.

Since we can dump cores to pipe, rather than directly to the filesystem,
we create a condition in which a user can create a very high load on the
system simply by running bad applications.

If the pipe reader specified in core_pattern is poorly written, we can
have lots of ourstandig resources and processes in the system.

This sysctl introduces an ability to limit that resource consumption.
core_pipe_limit defines how many in-flight dumps may be run in parallel,
dumps beyond this value are skipped and a note is made in the kernel log.
A special value of 0 in core_pipe_limit denotes unlimited core dumps may
be handled (this is the default value).

[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Reported-by: default avatarEarl Chew <earl_chew@agilent.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 725eae32
...@@ -22,6 +22,7 @@ show up in /proc/sys/kernel: ...@@ -22,6 +22,7 @@ show up in /proc/sys/kernel:
- callhome [ S390 only ] - callhome [ S390 only ]
- auto_msgmni - auto_msgmni
- core_pattern - core_pattern
- core_pipe_limit
- core_uses_pid - core_uses_pid
- ctrl-alt-del - ctrl-alt-del
- dentry-state - dentry-state
...@@ -135,6 +136,27 @@ core_pattern is used to specify a core dumpfile pattern name. ...@@ -135,6 +136,27 @@ core_pattern is used to specify a core dumpfile pattern name.
============================================================== ==============================================================
core_pipe_limit:
This sysctl is only applicable when core_pattern is configured to pipe core
files to user space helper a (when the first character of core_pattern is a '|',
see above). When collecting cores via a pipe to an application, it is
occasionally usefull for the collecting application to gather data about the
crashing process from its /proc/pid directory. In order to do this safely, the
kernel must wait for the collecting process to exit, so as not to remove the
crashing processes proc files prematurely. This in turn creates the possibility
that a misbehaving userspace collecting process can block the reaping of a
crashed process simply by never exiting. This sysctl defends against that. It
defines how many concurrent crashing processes may be piped to user space
applications in parallel. If this value is exceeded, then those crashing
processes above that value are noted via the kernel log and their cores are
skipped. 0 is a special value, indicating that unlimited processes may be
captured in parallel, but that no waiting will take place (i.e. the collecting
process is not guaranteed access to /proc/<crahing pid>/). This value defaults
to 0.
==============================================================
core_uses_pid: core_uses_pid:
The default coredump filename is "core". By setting The default coredump filename is "core". By setting
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
int core_uses_pid; int core_uses_pid;
char core_pattern[CORENAME_MAX_SIZE] = "core"; char core_pattern[CORENAME_MAX_SIZE] = "core";
unsigned int core_pipe_limit;
int suid_dumpable = 0; int suid_dumpable = 0;
/* The maximal length of core_pattern is also specified in sysctl.c */ /* The maximal length of core_pattern is also specified in sysctl.c */
...@@ -1744,7 +1745,8 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) ...@@ -1744,7 +1745,8 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
char **helper_argv = NULL; char **helper_argv = NULL;
int helper_argc = 0; int helper_argc = 0;
char *delimit; int dump_count = 0;
static atomic_t core_dump_count = ATOMIC_INIT(0);
audit_core_dumps(signr); audit_core_dumps(signr);
...@@ -1826,28 +1828,36 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) ...@@ -1826,28 +1828,36 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
goto fail_unlock; goto fail_unlock;
} }
dump_count = atomic_inc_return(&core_dump_count);
if (core_pipe_limit && (core_pipe_limit < dump_count)) {
printk(KERN_WARNING "Pid %d(%s) over core_pipe_limit\n",
task_tgid_vnr(current), current->comm);
printk(KERN_WARNING "Skipping core dump\n");
goto fail_dropcount;
}
helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc); helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
if (!helper_argv) { if (!helper_argv) {
printk(KERN_WARNING "%s failed to allocate memory\n", printk(KERN_WARNING "%s failed to allocate memory\n",
__func__); __func__);
goto fail_unlock; goto fail_dropcount;
} }
core_limit = RLIM_INFINITY; core_limit = RLIM_INFINITY;
/* SIGPIPE can happen, but it's just never processed */ /* SIGPIPE can happen, but it's just never processed */
if (call_usermodehelper_pipe(corename+1, helper_argv, NULL, if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
&file)) { &file)) {
printk(KERN_INFO "Core dump to %s pipe failed\n", printk(KERN_INFO "Core dump to %s pipe failed\n",
corename); corename);
goto fail_unlock; goto fail_dropcount;
} }
} else } else
file = filp_open(corename, file = filp_open(corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600); 0600);
if (IS_ERR(file)) if (IS_ERR(file))
goto fail_unlock; goto fail_dropcount;
inode = file->f_path.dentry->d_inode; inode = file->f_path.dentry->d_inode;
if (inode->i_nlink > 1) if (inode->i_nlink > 1)
goto close_fail; /* multiple links - don't dump */ goto close_fail; /* multiple links - don't dump */
...@@ -1877,6 +1887,9 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) ...@@ -1877,6 +1887,9 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
current->signal->group_exit_code |= 0x80; current->signal->group_exit_code |= 0x80;
close_fail: close_fail:
filp_close(file, NULL); filp_close(file, NULL);
fail_dropcount:
if (dump_count)
atomic_dec(&core_dump_count);
fail_unlock: fail_unlock:
if (helper_argv) if (helper_argv)
argv_free(helper_argv); argv_free(helper_argv);
......
...@@ -76,6 +76,7 @@ extern int max_threads; ...@@ -76,6 +76,7 @@ extern int max_threads;
extern int core_uses_pid; extern int core_uses_pid;
extern int suid_dumpable; extern int suid_dumpable;
extern char core_pattern[]; extern char core_pattern[];
extern unsigned int core_pipe_limit;
extern int pid_max; extern int pid_max;
extern int min_free_kbytes; extern int min_free_kbytes;
extern int pid_max_min, pid_max_max; extern int pid_max_min, pid_max_max;
...@@ -423,6 +424,14 @@ static struct ctl_table kern_table[] = { ...@@ -423,6 +424,14 @@ static struct ctl_table kern_table[] = {
.proc_handler = &proc_dostring, .proc_handler = &proc_dostring,
.strategy = &sysctl_string, .strategy = &sysctl_string,
}, },
{
.ctl_name = CTL_UNNUMBERED,
.procname = "core_pipe_limit",
.data = &core_pipe_limit,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
#ifdef CONFIG_PROC_SYSCTL #ifdef CONFIG_PROC_SYSCTL
{ {
.procname = "tainted", .procname = "tainted",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment