pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312)
This commit is contained in:
parent
3180443250
commit
ef736f8440
|
@ -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)
|
||||
|
|
237
debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch
vendored
Normal file
237
debian/patches/bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
From: Willy Tarreau <w@1wt.eu>
|
||||
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 <penguin-kernel@I-love.SAKURA.ne.jp>
|
||||
Mitigates: CVE-2013-4312 (Linux 2.0+)
|
||||
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
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,
|
||||
+ },
|
||||
{ }
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue