aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix/garbage.c
diff options
context:
space:
mode:
authorwilly tarreau <w@1wt.eu>2016-01-10 01:54:56 -0500
committerDavid S. Miller <davem@davemloft.net>2016-01-11 00:05:30 -0500
commit712f4aad406bb1ed67f3f98d04c044191f0ff593 (patch)
treeee2f45594b6acfc83a69988a914b9fe15d6e4367 /net/unix/garbage.c
parent3e4006f0b86a5ae5eb0e8215f9a9e1db24506977 (diff)
unix: properly account for FDs passed over unix sockets
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 <penguin-kernel@I-love.SAKURA.ne.jp> Mitigates: CVE-2013-4312 (Linux 2.0+) Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: Willy Tarreau <w@1wt.eu> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix/garbage.c')
-rw-r--r--net/unix/garbage.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index a73a226f2d33..8fcdc2283af5 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -120,11 +120,11 @@ void unix_inflight(struct file *fp)
120{ 120{
121 struct sock *s = unix_get_socket(fp); 121 struct sock *s = unix_get_socket(fp);
122 122
123 spin_lock(&unix_gc_lock);
124
123 if (s) { 125 if (s) {
124 struct unix_sock *u = unix_sk(s); 126 struct unix_sock *u = unix_sk(s);
125 127
126 spin_lock(&unix_gc_lock);
127
128 if (atomic_long_inc_return(&u->inflight) == 1) { 128 if (atomic_long_inc_return(&u->inflight) == 1) {
129 BUG_ON(!list_empty(&u->link)); 129 BUG_ON(!list_empty(&u->link));
130 list_add_tail(&u->link, &gc_inflight_list); 130 list_add_tail(&u->link, &gc_inflight_list);
@@ -132,25 +132,28 @@ void unix_inflight(struct file *fp)
132 BUG_ON(list_empty(&u->link)); 132 BUG_ON(list_empty(&u->link));
133 } 133 }
134 unix_tot_inflight++; 134 unix_tot_inflight++;
135 spin_unlock(&unix_gc_lock);
136 } 135 }
136 fp->f_cred->user->unix_inflight++;
137 spin_unlock(&unix_gc_lock);
137} 138}
138 139
139void unix_notinflight(struct file *fp) 140void unix_notinflight(struct file *fp)
140{ 141{
141 struct sock *s = unix_get_socket(fp); 142 struct sock *s = unix_get_socket(fp);
142 143
144 spin_lock(&unix_gc_lock);
145
143 if (s) { 146 if (s) {
144 struct unix_sock *u = unix_sk(s); 147 struct unix_sock *u = unix_sk(s);
145 148
146 spin_lock(&unix_gc_lock);
147 BUG_ON(list_empty(&u->link)); 149 BUG_ON(list_empty(&u->link));
148 150
149 if (atomic_long_dec_and_test(&u->inflight)) 151 if (atomic_long_dec_and_test(&u->inflight))
150 list_del_init(&u->link); 152 list_del_init(&u->link);
151 unix_tot_inflight--; 153 unix_tot_inflight--;
152 spin_unlock(&unix_gc_lock);
153 } 154 }
155 fp->f_cred->user->unix_inflight--;
156 spin_unlock(&unix_gc_lock);
154} 157}
155 158
156static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *), 159static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),