Skip to content

Commit 712f4aa

Browse files
wtarreaudavem330
authored andcommitted
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: [email protected] Reported-by: Tetsuo Handa <[email protected]> Mitigates: CVE-2013-4312 (Linux 2.0+) Suggested-by: Linus Torvalds <[email protected]> Acked-by: Hannes Frederic Sowa <[email protected]> Signed-off-by: Willy Tarreau <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3e4006f commit 712f4aa

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

include/linux/sched.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ struct user_struct {
830830
unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
831831
#endif
832832
unsigned long locked_shm; /* How many pages of mlocked shm ? */
833+
unsigned long unix_inflight; /* How many files in flight in unix sockets */
833834

834835
#ifdef CONFIG_KEYS
835836
struct key *uid_keyring; /* UID specific keyring */

net/unix/af_unix.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,21 @@ static void unix_destruct_scm(struct sk_buff *skb)
15131513
sock_wfree(skb);
15141514
}
15151515

1516+
/*
1517+
* The "user->unix_inflight" variable is protected by the garbage
1518+
* collection lock, and we just read it locklessly here. If you go
1519+
* over the limit, there might be a tiny race in actually noticing
1520+
* it across threads. Tough.
1521+
*/
1522+
static inline bool too_many_unix_fds(struct task_struct *p)
1523+
{
1524+
struct user_struct *user = current_user();
1525+
1526+
if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)))
1527+
return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
1528+
return false;
1529+
}
1530+
15161531
#define MAX_RECURSION_LEVEL 4
15171532

15181533
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)
15211536
unsigned char max_level = 0;
15221537
int unix_sock_count = 0;
15231538

1539+
if (too_many_unix_fds(current))
1540+
return -ETOOMANYREFS;
1541+
15241542
for (i = scm->fp->count - 1; i >= 0; i--) {
15251543
struct sock *sk = unix_get_socket(scm->fp->fp[i]);
15261544

@@ -1542,10 +1560,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
15421560
if (!UNIXCB(skb).fp)
15431561
return -ENOMEM;
15441562

1545-
if (unix_sock_count) {
1546-
for (i = scm->fp->count - 1; i >= 0; i--)
1547-
unix_inflight(scm->fp->fp[i]);
1548-
}
1563+
for (i = scm->fp->count - 1; i >= 0; i--)
1564+
unix_inflight(scm->fp->fp[i]);
15491565
return max_level;
15501566
}
15511567

net/unix/garbage.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,37 +120,40 @@ void unix_inflight(struct file *fp)
120120
{
121121
struct sock *s = unix_get_socket(fp);
122122

123+
spin_lock(&unix_gc_lock);
124+
123125
if (s) {
124126
struct unix_sock *u = unix_sk(s);
125127

126-
spin_lock(&unix_gc_lock);
127-
128128
if (atomic_long_inc_return(&u->inflight) == 1) {
129129
BUG_ON(!list_empty(&u->link));
130130
list_add_tail(&u->link, &gc_inflight_list);
131131
} else {
132132
BUG_ON(list_empty(&u->link));
133133
}
134134
unix_tot_inflight++;
135-
spin_unlock(&unix_gc_lock);
136135
}
136+
fp->f_cred->user->unix_inflight++;
137+
spin_unlock(&unix_gc_lock);
137138
}
138139

139140
void unix_notinflight(struct file *fp)
140141
{
141142
struct sock *s = unix_get_socket(fp);
142143

144+
spin_lock(&unix_gc_lock);
145+
143146
if (s) {
144147
struct unix_sock *u = unix_sk(s);
145148

146-
spin_lock(&unix_gc_lock);
147149
BUG_ON(list_empty(&u->link));
148150

149151
if (atomic_long_dec_and_test(&u->inflight))
150152
list_del_init(&u->link);
151153
unix_tot_inflight--;
152-
spin_unlock(&unix_gc_lock);
153154
}
155+
fp->f_cred->user->unix_inflight--;
156+
spin_unlock(&unix_gc_lock);
154157
}
155158

156159
static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),

0 commit comments

Comments
 (0)