diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2009-09-23 18:56:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-24 10:21:00 -0400 |
commit | a293980c2e261bd5b0d2a77340dd04f684caff58 (patch) | |
tree | 69f2b2747548deafb146c18953ebce21ae1f02d4 | |
parent | 725eae32df7754044809973034429a47e6035158 (diff) |
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>
-rw-r--r-- | Documentation/sysctl/kernel.txt | 22 | ||||
-rw-r--r-- | fs/exec.c | 23 | ||||
-rw-r--r-- | kernel/sysctl.c | 9 |
3 files changed, 49 insertions, 5 deletions
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index b3d8b4922740..a028b92001ed 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt | |||
@@ -22,6 +22,7 @@ show up in /proc/sys/kernel: | |||
22 | - callhome [ S390 only ] | 22 | - callhome [ S390 only ] |
23 | - auto_msgmni | 23 | - auto_msgmni |
24 | - core_pattern | 24 | - core_pattern |
25 | - core_pipe_limit | ||
25 | - core_uses_pid | 26 | - core_uses_pid |
26 | - ctrl-alt-del | 27 | - ctrl-alt-del |
27 | - dentry-state | 28 | - dentry-state |
@@ -135,6 +136,27 @@ core_pattern is used to specify a core dumpfile pattern name. | |||
135 | 136 | ||
136 | ============================================================== | 137 | ============================================================== |
137 | 138 | ||
139 | core_pipe_limit: | ||
140 | |||
141 | This sysctl is only applicable when core_pattern is configured to pipe core | ||
142 | files to user space helper a (when the first character of core_pattern is a '|', | ||
143 | see above). When collecting cores via a pipe to an application, it is | ||
144 | occasionally usefull for the collecting application to gather data about the | ||
145 | crashing process from its /proc/pid directory. In order to do this safely, the | ||
146 | kernel must wait for the collecting process to exit, so as not to remove the | ||
147 | crashing processes proc files prematurely. This in turn creates the possibility | ||
148 | that a misbehaving userspace collecting process can block the reaping of a | ||
149 | crashed process simply by never exiting. This sysctl defends against that. It | ||
150 | defines how many concurrent crashing processes may be piped to user space | ||
151 | applications in parallel. If this value is exceeded, then those crashing | ||
152 | processes above that value are noted via the kernel log and their cores are | ||
153 | skipped. 0 is a special value, indicating that unlimited processes may be | ||
154 | captured in parallel, but that no waiting will take place (i.e. the collecting | ||
155 | process is not guaranteed access to /proc/<crahing pid>/). This value defaults | ||
156 | to 0. | ||
157 | |||
158 | ============================================================== | ||
159 | |||
138 | core_uses_pid: | 160 | core_uses_pid: |
139 | 161 | ||
140 | The default coredump filename is "core". By setting | 162 | The default coredump filename is "core". By setting |
@@ -63,6 +63,7 @@ | |||
63 | 63 | ||
64 | int core_uses_pid; | 64 | int core_uses_pid; |
65 | char core_pattern[CORENAME_MAX_SIZE] = "core"; | 65 | char core_pattern[CORENAME_MAX_SIZE] = "core"; |
66 | unsigned int core_pipe_limit; | ||
66 | int suid_dumpable = 0; | 67 | int suid_dumpable = 0; |
67 | 68 | ||
68 | /* The maximal length of core_pattern is also specified in sysctl.c */ | 69 | /* 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 | unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; | 1745 | unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; |
1745 | char **helper_argv = NULL; | 1746 | char **helper_argv = NULL; |
1746 | int helper_argc = 0; | 1747 | int helper_argc = 0; |
1747 | char *delimit; | 1748 | int dump_count = 0; |
1749 | static atomic_t core_dump_count = ATOMIC_INIT(0); | ||
1748 | 1750 | ||
1749 | audit_core_dumps(signr); | 1751 | audit_core_dumps(signr); |
1750 | 1752 | ||
@@ -1826,28 +1828,36 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
1826 | goto fail_unlock; | 1828 | goto fail_unlock; |
1827 | } | 1829 | } |
1828 | 1830 | ||
1831 | dump_count = atomic_inc_return(&core_dump_count); | ||
1832 | if (core_pipe_limit && (core_pipe_limit < dump_count)) { | ||
1833 | printk(KERN_WARNING "Pid %d(%s) over core_pipe_limit\n", | ||
1834 | task_tgid_vnr(current), current->comm); | ||
1835 | printk(KERN_WARNING "Skipping core dump\n"); | ||
1836 | goto fail_dropcount; | ||
1837 | } | ||
1838 | |||
1829 | helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc); | 1839 | helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc); |
1830 | if (!helper_argv) { | 1840 | if (!helper_argv) { |
1831 | printk(KERN_WARNING "%s failed to allocate memory\n", | 1841 | printk(KERN_WARNING "%s failed to allocate memory\n", |
1832 | __func__); | 1842 | __func__); |
1833 | goto fail_unlock; | 1843 | goto fail_dropcount; |
1834 | } | 1844 | } |
1835 | 1845 | ||
1836 | core_limit = RLIM_INFINITY; | 1846 | core_limit = RLIM_INFINITY; |
1837 | 1847 | ||
1838 | /* SIGPIPE can happen, but it's just never processed */ | 1848 | /* SIGPIPE can happen, but it's just never processed */ |
1839 | if (call_usermodehelper_pipe(corename+1, helper_argv, NULL, | 1849 | if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL, |
1840 | &file)) { | 1850 | &file)) { |
1841 | printk(KERN_INFO "Core dump to %s pipe failed\n", | 1851 | printk(KERN_INFO "Core dump to %s pipe failed\n", |
1842 | corename); | 1852 | corename); |
1843 | goto fail_unlock; | 1853 | goto fail_dropcount; |
1844 | } | 1854 | } |
1845 | } else | 1855 | } else |
1846 | file = filp_open(corename, | 1856 | file = filp_open(corename, |
1847 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, | 1857 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, |
1848 | 0600); | 1858 | 0600); |
1849 | if (IS_ERR(file)) | 1859 | if (IS_ERR(file)) |
1850 | goto fail_unlock; | 1860 | goto fail_dropcount; |
1851 | inode = file->f_path.dentry->d_inode; | 1861 | inode = file->f_path.dentry->d_inode; |
1852 | if (inode->i_nlink > 1) | 1862 | if (inode->i_nlink > 1) |
1853 | goto close_fail; /* multiple links - don't dump */ | 1863 | 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 | current->signal->group_exit_code |= 0x80; | 1887 | current->signal->group_exit_code |= 0x80; |
1878 | close_fail: | 1888 | close_fail: |
1879 | filp_close(file, NULL); | 1889 | filp_close(file, NULL); |
1890 | fail_dropcount: | ||
1891 | if (dump_count) | ||
1892 | atomic_dec(&core_dump_count); | ||
1880 | fail_unlock: | 1893 | fail_unlock: |
1881 | if (helper_argv) | 1894 | if (helper_argv) |
1882 | argv_free(helper_argv); | 1895 | argv_free(helper_argv); |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 7f4f57bea4ce..37abb8c3995b 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -76,6 +76,7 @@ extern int max_threads; | |||
76 | extern int core_uses_pid; | 76 | extern int core_uses_pid; |
77 | extern int suid_dumpable; | 77 | extern int suid_dumpable; |
78 | extern char core_pattern[]; | 78 | extern char core_pattern[]; |
79 | extern unsigned int core_pipe_limit; | ||
79 | extern int pid_max; | 80 | extern int pid_max; |
80 | extern int min_free_kbytes; | 81 | extern int min_free_kbytes; |
81 | extern int pid_max_min, pid_max_max; | 82 | extern int pid_max_min, pid_max_max; |
@@ -423,6 +424,14 @@ static struct ctl_table kern_table[] = { | |||
423 | .proc_handler = &proc_dostring, | 424 | .proc_handler = &proc_dostring, |
424 | .strategy = &sysctl_string, | 425 | .strategy = &sysctl_string, |
425 | }, | 426 | }, |
427 | { | ||
428 | .ctl_name = CTL_UNNUMBERED, | ||
429 | .procname = "core_pipe_limit", | ||
430 | .data = &core_pipe_limit, | ||
431 | .maxlen = sizeof(unsigned int), | ||
432 | .mode = 0644, | ||
433 | .proc_handler = &proc_dointvec, | ||
434 | }, | ||
426 | #ifdef CONFIG_PROC_SYSCTL | 435 | #ifdef CONFIG_PROC_SYSCTL |
427 | { | 436 | { |
428 | .procname = "tainted", | 437 | .procname = "tainted", |