From f335c0cfcc1879a21f4acdad2c6860084bf271a2 Mon Sep 17 00:00:00 2001 From: Salvatore Bonaccorso Date: Sun, 17 Jan 2016 09:24:19 +0100 Subject: [PATCH] unix: properly account for FDs passed over unix sockets (CVE-2013-4312) --- debian/changelog | 4 + ...count-for-FDs-passed-over-unix-socke.patch | 140 ++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 145 insertions(+) create mode 100644 debian/patches/bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch diff --git a/debian/changelog b/debian/changelog index f93cd4a73..f342fd7a8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ linux (4.3.3-6) UNRELEASED; urgency=medium + [ Ben Hutchings ] * debian.py: Implement stable order of fields in debian/tests/control * debian.py: Implement stable order of fields not in the predefined order * genorig.py: Make orig tarballs really reproducible: @@ -18,6 +19,9 @@ linux (4.3.3-6) UNRELEASED; urgency=medium * block/sd: Fix device-imposed transfer length limits (Closes: #805252) * [x86] drm/vmwgfx: Fix a width / pitch mismatch on framebuffer updates + [ Salvatore Bonaccorso ] + * unix: properly account for FDs passed over unix sockets (CVE-2013-4312) + -- Ben Hutchings Fri, 08 Jan 2016 12:08:13 +0000 linux (4.3.3-5) unstable; urgency=medium diff --git a/debian/patches/bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch b/debian/patches/bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch new file mode 100644 index 000000000..8cd6bb4ac --- /dev/null +++ b/debian/patches/bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch @@ -0,0 +1,140 @@ +From: willy tarreau +Date: Sun, 10 Jan 2016 07:54:56 +0100 +Subject: unix: properly account for FDs passed over unix sockets +Origin: https://git.kernel.org/linus/712f4aad406bb1ed67f3f98d04c044191f0ff593 + +It is possible for a process to allocate and accumulate far more FDs than +the process' limit by sending them over a unix socket then closing them +to keep the process' fd count low. + +This change addresses this problem by keeping track of the number of FDs +in flight per user and preventing non-privileged processes from having +more FDs in flight than their configured FD limit. + +Reported-by: socketpair@gmail.com +Reported-by: Tetsuo Handa +Mitigates: CVE-2013-4312 (Linux 2.0+) +Suggested-by: Linus Torvalds +Acked-by: Hannes Frederic Sowa +Signed-off-by: Willy Tarreau +Signed-off-by: David S. Miller +--- + include/linux/sched.h | 1 + + net/unix/af_unix.c | 24 ++++++++++++++++++++---- + net/unix/garbage.c | 13 ++++++++----- + 3 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/include/linux/sched.h b/include/linux/sched.h +index edad7a4..fbf25f1 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -830,6 +830,7 @@ 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 */ + + #ifdef CONFIG_KEYS + struct key *uid_keyring; /* UID specific keyring */ +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index ef05cd9..e3f85bc 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -1513,6 +1513,21 @@ static void unix_destruct_scm(struct sk_buff *skb) + sock_wfree(skb); + } + ++/* ++ * The "user->unix_inflight" variable is protected by the garbage ++ * collection lock, and we just read it locklessly here. If you go ++ * over the limit, there might be a tiny race in actually noticing ++ * it across threads. Tough. ++ */ ++static inline bool too_many_unix_fds(struct task_struct *p) ++{ ++ struct user_struct *user = current_user(); ++ ++ if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE))) ++ return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); ++ return false; ++} ++ + #define MAX_RECURSION_LEVEL 4 + + static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) +@@ -1521,6 +1536,9 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) + unsigned char max_level = 0; + int unix_sock_count = 0; + ++ if (too_many_unix_fds(current)) ++ return -ETOOMANYREFS; ++ + for (i = scm->fp->count - 1; i >= 0; i--) { + struct sock *sk = unix_get_socket(scm->fp->fp[i]); + +@@ -1542,10 +1560,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) + if (!UNIXCB(skb).fp) + return -ENOMEM; + +- if (unix_sock_count) { +- for (i = scm->fp->count - 1; i >= 0; i--) +- unix_inflight(scm->fp->fp[i]); +- } ++ for (i = scm->fp->count - 1; i >= 0; i--) ++ unix_inflight(scm->fp->fp[i]); + return max_level; + } + +diff --git a/net/unix/garbage.c b/net/unix/garbage.c +index a73a226..8fcdc22 100644 +--- a/net/unix/garbage.c ++++ b/net/unix/garbage.c +@@ -120,11 +120,11 @@ void unix_inflight(struct file *fp) + { + struct sock *s = unix_get_socket(fp); + ++ spin_lock(&unix_gc_lock); ++ + if (s) { + struct unix_sock *u = unix_sk(s); + +- spin_lock(&unix_gc_lock); +- + if (atomic_long_inc_return(&u->inflight) == 1) { + BUG_ON(!list_empty(&u->link)); + list_add_tail(&u->link, &gc_inflight_list); +@@ -132,25 +132,28 @@ void unix_inflight(struct file *fp) + BUG_ON(list_empty(&u->link)); + } + unix_tot_inflight++; +- spin_unlock(&unix_gc_lock); + } ++ fp->f_cred->user->unix_inflight++; ++ spin_unlock(&unix_gc_lock); + } + + void unix_notinflight(struct file *fp) + { + struct sock *s = unix_get_socket(fp); + ++ spin_lock(&unix_gc_lock); ++ + if (s) { + struct unix_sock *u = unix_sk(s); + +- spin_lock(&unix_gc_lock); + BUG_ON(list_empty(&u->link)); + + if (atomic_long_dec_and_test(&u->inflight)) + list_del_init(&u->link); + unix_tot_inflight--; +- spin_unlock(&unix_gc_lock); + } ++ fp->f_cred->user->unix_inflight--; ++ spin_unlock(&unix_gc_lock); + } + + static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *), +-- +2.7.0.rc3 + diff --git a/debian/patches/series b/debian/patches/series index ad076a3f1..64d5a76f6 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -138,3 +138,4 @@ bugfix/all/usb-serial-visor-fix-crash-on-detecting-device-without-write_urbs.pat bugfix/all/tty-fix-unsafe-ldisc-reference-via-ioctl-tiocgetd.patch bugfix/all/block-sd-fix-device-imposed-transfer-length-limits.patch bugfix/x86/drm-vmwgfx-fix-a-width-pitch-mismatch-on-framebuffer.patch +bugfix/all/unix-properly-account-for-FDs-passed-over-unix-socke.patch