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: Neil Horman <nhorman@tuxdriver.com>
Reported-by: Earl 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Neil Horman 2009-09-23 15:56:56 -07:00 committed by Linus Torvalds
parent 725eae32df
commit a293980c2e
3 changed files with 49 additions and 5 deletions

View file

@ -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.
============================================================== ==============================================================
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

View file

@ -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)
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)
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)
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);

View file

@ -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[] = {
.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",