[linux] 01/02: pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312)

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Fri Feb 12 23:45:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch jessie-security
in repository linux.

commit ff312a4d092af899822419b276b0a76ab83d4b85
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Fri Feb 12 23:23:40 2016 +0000

    pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312)
---
 debian/changelog                                   |   1 +
 ...the-per-user-amount-of-pages-allocated-in.patch | 237 +++++++++++++++++++++
 ...=> fix-abi-changes-for-cve-2013-4312-fix.patch} |  26 ++-
 debian/patches/series                              |   3 +-
 4 files changed, 261 insertions(+), 6 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 1e48edf..391ee8f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -8,6 +8,7 @@ linux (3.16.7-ckt20-1+deb8u4) UNRELEASED; urgency=medium
   * [x86] mm: Add barriers and document switch_mm()-vs-flush synchronization
     (CVE-2016-2069)
   * [x86] mm: Improve switch_mm() barrier comments
+  * pipe: limit the per-user amount of pages allocated in pipes (CVE-2013-4312)
 
  -- Ben Hutchings <ben at decadent.org.uk>  Sat, 23 Jan 2016 22:53:58 +0000
 
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 0000000..80ddbc6
--- /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 <w at 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 at gmail.com
+Reported-by: Tetsuo Handa <penguin-kernel at I-love.SAKURA.ne.jp>
+Mitigates: CVE-2013-4312 (Linux 2.0+)
+Suggested-by: Linus Torvalds <torvalds at linux-foundation.org>
+Signed-off-by: Willy Tarreau <w at 1wt.eu>
+Signed-off-by: Al Viro <viro at 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
+@@ -39,6 +39,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.
+@@ -585,20 +591,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);
+ 	}
+ 
+@@ -609,6 +644,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)
+@@ -999,6 +1036,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;
+@@ -1070,6 +1108,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
+@@ -764,6 +764,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
+@@ -1683,6 +1683,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/debian/fix-abi-changes-for-cve-2013-4312-fix.patch
similarity index 53%
rename from debian/patches/debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch
rename to debian/patches/debian/fix-abi-changes-for-cve-2013-4312-fix.patch
index 88bbf65..b908f93 100644
--- a/debian/patches/debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch
+++ b/debian/patches/debian/fix-abi-changes-for-cve-2013-4312-fix.patch
@@ -1,33 +1,49 @@
 From: Ben Hutchings <ben at decadent.org.uk>
 Date: Sun, 17 Jan 2016 15:55:02 +0000
-Subject: unix: Fix ABI change for CVE-2013-4312
+Subject: Fix ABI changes 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.
 
+Similarly for pipe_inode_info::user.
+
 ---
 --- a/include/linux/sched.h
 +++ b/include/linux/sched.h
-@@ -763,7 +763,6 @@ struct user_struct {
+@@ -763,8 +763,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 */
-@@ -777,6 +776,10 @@ struct user_struct {
+@@ -778,6 +776,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
  };
  
  extern int uids_sysfs_init(void);
+--- a/include/linux/pipe_fs_i.h
++++ b/include/linux/pipe_fs_i.h
+@@ -58,7 +58,9 @@ struct pipe_inode_info {
+ 	struct fasync_struct *fasync_readers;
+ 	struct fasync_struct *fasync_writers;
+ 	struct pipe_buffer *bufs;
++#ifndef __GENKSYMS__
+ 	struct user_struct *user;
++#endif
+ };
+ 
+ /*
diff --git a/debian/patches/series b/debian/patches/series
index 821182a..ca74d64 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -674,7 +674,8 @@ bugfix/all/usb-serial-visor-fix-crash-on-detecting-device-without-write_urbs.pat
 bugfix/all/sctp-prevent-soft-lockup-when-sctp_accept-is-called-.patch
 bugfix/all/tty-fix-unsafe-ldisc-reference-via-ioctl-tiocgetd.patch
 bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch
-debian/unix-fix-abi-change-for-cve-2013-4312-fix.patch
+bugfix/all/pipe-limit-the-per-user-amount-of-pages-allocated-in.patch
+debian/fix-abi-changes-for-cve-2013-4312-fix.patch
 bugfix/all/KEYS-Fix-keyring-ref-leak-in-join_session_keyring.patch
 bugfix/all/fuse-break-infinite-loop-in-fuse_fill_write_pages.patch
 bugfix/all/aufs-tiny-extract-a-new-func-xino_fwrite_wkq.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list