From ef736f84407a14adafb095c41b5c80286216a31e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 4 Feb 2016 20:31:06 +0000 Subject: [PATCH] pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312) --- debian/changelog | 1 + ...er-user-amount-of-pages-allocated-in.patch | 237 ++++++++++++++++++ ...ix-abi-change-for-cve-2013-4312-fix.patch} | 10 +- debian/patches/series | 3 +- 4 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch rename debian/patches/{debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch => bugfix/all/user_struct-fix-abi-change-for-cve-2013-4312-fix.patch} (70%) diff --git a/debian/changelog b/debian/changelog index f7ec1b505..7b5e47a32 100644 --- a/debian/changelog +++ b/debian/changelog @@ -212,6 +212,7 @@ linux (4.3.5-1) UNRELEASED; urgency=medium * fuse: break infinite loop in fuse_fill_write_pages() (CVE-2015-8785) * SCSI: fix crashes in sd and sr runtime PM (Closes: #801925) * rt2x00: fix monitor mode regression (regression in 4.2) + * pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312) [ Salvatore Bonaccorso ] * netfilter: nf_nat_redirect: add missing NULL pointer check (CVE-2015-8787) diff --git a/debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch b/debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch new file mode 100644 index 000000000..149818968 --- /dev/null +++ b/debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch @@ -0,0 +1,237 @@ +From: Willy Tarreau +Date: Mon, 18 Jan 2016 16:36:09 +0100 +Subject: pipe: limit the per-user amount of pages allocated in pipes +Origin: https://git.kernel.org/linus/759c01142a5d0f364a462346168a56de28a80f52 + +On no-so-small systems, it is possible for a single process to cause an +OOM condition by filling large pipes with data that are never read. A +typical process filling 4000 pipes with 1 MB of data will use 4 GB of +memory. On small systems it may be tricky to set the pipe max size to +prevent this from happening. + +This patch makes it possible to enforce a per-user soft limit above +which new pipes will be limited to a single page, effectively limiting +them to 4 kB each, as well as a hard limit above which no new pipes may +be created for this user. This has the effect of protecting the system +against memory abuse without hurting other users, and still allowing +pipes to work correctly though with less data at once. + +The limit are controlled by two new sysctls : pipe-user-pages-soft, and +pipe-user-pages-hard. Both may be disabled by setting them to zero. The +default soft limit allows the default number of FDs per process (1024) +to create pipes of the default size (64kB), thus reaching a limit of 64MB +before starting to create only smaller pipes. With 256 processes limited +to 1024 FDs each, this results in 1024*64kB + (256*1024 - 1024) * 4kB = +1084 MB of memory allocated for a user. The hard limit is disabled by +default to avoid breaking existing applications that make intensive use +of pipes (eg: for splicing). + +Reported-by: socketpair@gmail.com +Reported-by: Tetsuo Handa +Mitigates: CVE-2013-4312 (Linux 2.0+) +Suggested-by: Linus Torvalds +Signed-off-by: Willy Tarreau +Signed-off-by: Al Viro +--- + Documentation/sysctl/fs.txt | 23 ++++++++++++++++++++++ + fs/pipe.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- + include/linux/pipe_fs_i.h | 4 ++++ + include/linux/sched.h | 1 + + kernel/sysctl.c | 14 ++++++++++++++ + 5 files changed, 87 insertions(+), 2 deletions(-) + +--- a/Documentation/sysctl/fs.txt ++++ b/Documentation/sysctl/fs.txt +@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/ + - nr_open + - overflowuid + - overflowgid ++- pipe-user-pages-hard ++- pipe-user-pages-soft + - protected_hardlinks + - protected_symlinks + - suid_dumpable +@@ -159,6 +161,27 @@ The default is 65534. + + ============================================================== + ++pipe-user-pages-hard: ++ ++Maximum total number of pages a non-privileged user may allocate for pipes. ++Once this limit is reached, no new pipes may be allocated until usage goes ++below the limit again. When set to 0, no limit is applied, which is the default ++setting. ++ ++============================================================== ++ ++pipe-user-pages-soft: ++ ++Maximum total number of pages a non-privileged user may allocate for pipes ++before the pipe size gets limited to a single page. Once this limit is reached, ++new pipes will be limited to a single page in size for this user in order to ++limit total memory usage, and trying to increase them using fcntl() will be ++denied until usage goes below the limit again. The default value allows to ++allocate up to 1024 pipes at their default size. When set to 0, no limit is ++applied. ++ ++============================================================== ++ + protected_hardlinks: + + A long-standing class of security issues is the hardlink-based +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576; + */ + unsigned int pipe_min_size = PAGE_SIZE; + ++/* Maximum allocatable pages per user. Hard limit is unset by default, soft ++ * matches default values. ++ */ ++unsigned long pipe_user_pages_hard; ++unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; ++ + /* + * We use a start+len construction, which provides full use of the + * allocated memory. +@@ -584,20 +590,49 @@ pipe_fasync(int fd, struct file *filp, i + return retval; + } + ++static void account_pipe_buffers(struct pipe_inode_info *pipe, ++ unsigned long old, unsigned long new) ++{ ++ atomic_long_add(new - old, &pipe->user->pipe_bufs); ++} ++ ++static bool too_many_pipe_buffers_soft(struct user_struct *user) ++{ ++ return pipe_user_pages_soft && ++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; ++} ++ ++static bool too_many_pipe_buffers_hard(struct user_struct *user) ++{ ++ return pipe_user_pages_hard && ++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; ++} ++ + struct pipe_inode_info *alloc_pipe_info(void) + { + struct pipe_inode_info *pipe; + + pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); + if (pipe) { +- pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); ++ unsigned long pipe_bufs = PIPE_DEF_BUFFERS; ++ struct user_struct *user = get_current_user(); ++ ++ if (!too_many_pipe_buffers_hard(user)) { ++ if (too_many_pipe_buffers_soft(user)) ++ pipe_bufs = 1; ++ pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); ++ } ++ + if (pipe->bufs) { + init_waitqueue_head(&pipe->wait); + pipe->r_counter = pipe->w_counter = 1; +- pipe->buffers = PIPE_DEF_BUFFERS; ++ pipe->buffers = pipe_bufs; ++ pipe->user = user; ++ account_pipe_buffers(pipe, 0, pipe_bufs); + mutex_init(&pipe->mutex); + return pipe; + } ++ free_uid(user); + kfree(pipe); + } + +@@ -608,6 +643,8 @@ void free_pipe_info(struct pipe_inode_in + { + int i; + ++ account_pipe_buffers(pipe, pipe->buffers, 0); ++ free_uid(pipe->user); + for (i = 0; i < pipe->buffers; i++) { + struct pipe_buffer *buf = pipe->bufs + i; + if (buf->ops) +@@ -996,6 +1033,7 @@ static long pipe_set_size(struct pipe_in + memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); + } + ++ account_pipe_buffers(pipe, pipe->buffers, nr_pages); + pipe->curbuf = 0; + kfree(pipe->bufs); + pipe->bufs = bufs; +@@ -1067,6 +1105,11 @@ long pipe_fcntl(struct file *file, unsig + if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { + ret = -EPERM; + goto out; ++ } else if ((too_many_pipe_buffers_hard(pipe->user) || ++ too_many_pipe_buffers_soft(pipe->user)) && ++ !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { ++ ret = -EPERM; ++ goto out; + } + ret = pipe_set_size(pipe, nr_pages); + break; +--- a/include/linux/pipe_fs_i.h ++++ b/include/linux/pipe_fs_i.h +@@ -42,6 +42,7 @@ struct pipe_buffer { + * @fasync_readers: reader side fasync + * @fasync_writers: writer side fasync + * @bufs: the circular array of pipe buffers ++ * @user: the user who created this pipe + **/ + struct pipe_inode_info { + struct mutex mutex; +@@ -57,6 +58,7 @@ struct pipe_inode_info { + struct fasync_struct *fasync_readers; + struct fasync_struct *fasync_writers; + struct pipe_buffer *bufs; ++ struct user_struct *user; + }; + + /* +@@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info + void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); + + extern unsigned int pipe_max_size, pipe_min_size; ++extern unsigned long pipe_user_pages_hard; ++extern unsigned long pipe_user_pages_soft; + int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); + + +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -831,6 +831,7 @@ struct user_struct { + #endif + unsigned long locked_shm; /* How many pages of mlocked shm ? */ + unsigned long unix_inflight; /* How many files in flight in unix sockets */ ++ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ + + #ifdef CONFIG_KEYS + struct key *uid_keyring; /* UID specific keyring */ +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -1714,6 +1714,20 @@ static struct ctl_table fs_table[] = { + .proc_handler = &pipe_proc_fn, + .extra1 = &pipe_min_size, + }, ++ { ++ .procname = "pipe-user-pages-hard", ++ .data = &pipe_user_pages_hard, ++ .maxlen = sizeof(pipe_user_pages_hard), ++ .mode = 0644, ++ .proc_handler = proc_doulongvec_minmax, ++ }, ++ { ++ .procname = "pipe-user-pages-soft", ++ .data = &pipe_user_pages_soft, ++ .maxlen = sizeof(pipe_user_pages_soft), ++ .mode = 0644, ++ .proc_handler = proc_doulongvec_minmax, ++ }, + { } + }; + diff --git a/debian/patches/debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch b/debian/patches/bugfix/all/user_struct-fix-abi-change-for-cve-2013-4312-fix.patch similarity index 70% rename from debian/patches/debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch rename to debian/patches/bugfix/all/user_struct-fix-abi-change-for-cve-2013-4312-fix.patch index a4a829398..cc7176852 100644 --- a/debian/patches/debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch +++ b/debian/patches/bugfix/all/user_struct-fix-abi-change-for-cve-2013-4312-fix.patch @@ -3,8 +3,8 @@ Date: Sun, 17 Jan 2016 15:55:02 +0000 Subject: unix: Fix ABI change for CVE-2013-4312 Forwarded: not-needed -The fix for CVE-2013-4312 added a new structure member, -user_struct::unix_inflight. As this is always allocated in +The fixes for CVE-2013-4312 added new structure members, +user_struct::{unix_inflight,pipe_bufs}. As this is always allocated in kernel/user.c and the new member is only used by af_unix which is also built-in, we can safely add new members at the end. So move it to the end and hide it from genksyms. @@ -12,21 +12,23 @@ end and hide it from genksyms. --- --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -830,7 +830,6 @@ struct user_struct { +@@ -830,8 +830,6 @@ struct user_struct { unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ #endif unsigned long locked_shm; /* How many pages of mlocked shm ? */ - unsigned long unix_inflight; /* How many files in flight in unix sockets */ +- atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ #ifdef CONFIG_KEYS struct key *uid_keyring; /* UID specific keyring */ -@@ -844,6 +843,10 @@ struct user_struct { +@@ -845,6 +843,11 @@ struct user_struct { #ifdef CONFIG_PERF_EVENTS atomic_long_t locked_vm; #endif + +#ifndef __GENKSYMS__ + unsigned long unix_inflight; /* How many files in flight in unix sockets */ ++ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ +#endif }; diff --git a/debian/patches/series b/debian/patches/series index c62983f1b..a0c223a45 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -126,7 +126,6 @@ bugfix/all/revert-xhci-don-t-finish-a-td-if-we-get-a-short-transfer.patch bugfix/all/usb-serial-visor-fix-crash-on-detecting-device-without-write_urbs.patch bugfix/all/tty-fix-unsafe-ldisc-reference-via-ioctl-tiocgetd.patch bugfix/x86/drm-vmwgfx-fix-a-width-pitch-mismatch-on-framebuffer.patch -debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch bugfix/all/bcache-fix-a-livelock-when-we-cause-a-huge-number-of.patch bugfix/all/bcache-add-a-cond_resched-call-to-gc.patch bugfix/all/bcache-clear-bcache_dev_unlink_done-flag-when-attach.patch @@ -140,3 +139,5 @@ bugfix/all/scsi-fix-crashes-in-sd-and-sr-runtime-pm.patch bugfix/all/netfilter-nf_nat_redirect-add-missing-NULL-pointer-c.patch debian/usb-fix-abi-change-in-4.3.5.patch bugfix/all/rt2x00-fix-monitor-mode-regression.patch +bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch +bugfix/all/user_struct-fix-abi-change-for-cve-2013-4312-fix.patch