aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sched.h1
-rw-r--r--net/unix/af_unix.c24
-rw-r--r--net/unix/garbage.c13
3 files changed, 29 insertions, 9 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index edad7a43edea..fbf25f19b3b5 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -830,6 +830,7 @@ struct user_struct {
830 unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ 830 unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
831#endif 831#endif
832 unsigned long locked_shm; /* How many pages of mlocked shm ? */ 832 unsigned long locked_shm; /* How many pages of mlocked shm ? */
833 unsigned long unix_inflight; /* How many files in flight in unix sockets */
833 834
834#ifdef CONFIG_KEYS 835#ifdef CONFIG_KEYS
835 struct key *uid_keyring; /* UID specific keyring */ 836 struct key *uid_keyring; /* UID specific keyring */
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index ef05cd9403d4..e3f85bc8b135 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)
1513 sock_wfree(skb); 1513 sock_wfree(skb);
1514} 1514}
1515 1515
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 */
1522static 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
1516#define MAX_RECURSION_LEVEL 4 1531#define MAX_RECURSION_LEVEL 4
1517 1532
1518static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) 1533static 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)
1521 unsigned char max_level = 0; 1536 unsigned char max_level = 0;
1522 int unix_sock_count = 0; 1537 int unix_sock_count = 0;
1523 1538
1539 if (too_many_unix_fds(current))
1540 return -ETOOMANYREFS;
1541
1524 for (i = scm->fp->count - 1; i >= 0; i--) { 1542 for (i = scm->fp->count - 1; i >= 0; i--) {
1525 struct sock *sk = unix_get_socket(scm->fp->fp[i]); 1543 struct sock *sk = unix_get_socket(scm->fp->fp[i]);
1526 1544
@@ -1542,10 +1560,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
1542 if (!UNIXCB(skb).fp) 1560 if (!UNIXCB(skb).fp)
1543 return -ENOMEM; 1561 return -ENOMEM;
1544 1562
1545 if (unix_sock_count) { 1563 for (i = scm->fp->count - 1; i >= 0; i--)
1546 for (i = scm->fp->count - 1; i >= 0; i--) 1564 unix_inflight(scm->fp->fp[i]);
1547 unix_inflight(scm->fp->fp[i]);
1548 }
1549 return max_level; 1565 return max_level;
1550} 1566}
1551 1567
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 *),