diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2016-04-05 11:10:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-04-07 16:44:14 -0400 |
commit | 8ced425ee630c03beea06c1dfa35190bf8395d07 (patch) | |
tree | 24f93b5ab435e0bd4cada95b9f91df0a2e205186 | |
parent | 1e1d04e678cf72442f57ce82803c7a407769135f (diff) |
tun: use socket locks for sk_{attach,detatch}_filter
This reverts commit 5a5abb1fa3b05dd ("tun, bpf: fix suspicious RCU usage
in tun_{attach, detach}_filter") and replaces it to use lock_sock around
sk_{attach,detach}_filter. The checks inside filter.c are updated with
lockdep_sock_is_held to check for proper socket locks.
It keeps the code cleaner by ensuring that only one lock governs the
socket filter instead of two independent locks.
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/tun.c | 14 | ||||
-rw-r--r-- | include/linux/filter.h | 4 | ||||
-rw-r--r-- | net/core/filter.c | 35 |
3 files changed, 22 insertions, 31 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9abc36bf77ea..64bc143eddd9 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -622,8 +622,9 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte | |||
622 | 622 | ||
623 | /* Re-attach the filter to persist device */ | 623 | /* Re-attach the filter to persist device */ |
624 | if (!skip_filter && (tun->filter_attached == true)) { | 624 | if (!skip_filter && (tun->filter_attached == true)) { |
625 | err = __sk_attach_filter(&tun->fprog, tfile->socket.sk, | 625 | lock_sock(tfile->socket.sk); |
626 | lockdep_rtnl_is_held()); | 626 | err = sk_attach_filter(&tun->fprog, tfile->socket.sk); |
627 | release_sock(tfile->socket.sk); | ||
627 | if (!err) | 628 | if (!err) |
628 | goto out; | 629 | goto out; |
629 | } | 630 | } |
@@ -1824,7 +1825,9 @@ static void tun_detach_filter(struct tun_struct *tun, int n) | |||
1824 | 1825 | ||
1825 | for (i = 0; i < n; i++) { | 1826 | for (i = 0; i < n; i++) { |
1826 | tfile = rtnl_dereference(tun->tfiles[i]); | 1827 | tfile = rtnl_dereference(tun->tfiles[i]); |
1827 | __sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held()); | 1828 | lock_sock(tfile->socket.sk); |
1829 | sk_detach_filter(tfile->socket.sk); | ||
1830 | release_sock(tfile->socket.sk); | ||
1828 | } | 1831 | } |
1829 | 1832 | ||
1830 | tun->filter_attached = false; | 1833 | tun->filter_attached = false; |
@@ -1837,8 +1840,9 @@ static int tun_attach_filter(struct tun_struct *tun) | |||
1837 | 1840 | ||
1838 | for (i = 0; i < tun->numqueues; i++) { | 1841 | for (i = 0; i < tun->numqueues; i++) { |
1839 | tfile = rtnl_dereference(tun->tfiles[i]); | 1842 | tfile = rtnl_dereference(tun->tfiles[i]); |
1840 | ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk, | 1843 | lock_sock(tfile->socket.sk); |
1841 | lockdep_rtnl_is_held()); | 1844 | ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); |
1845 | release_sock(tfile->socket.sk); | ||
1842 | if (ret) { | 1846 | if (ret) { |
1843 | tun_detach_filter(tun, i); | 1847 | tun_detach_filter(tun, i); |
1844 | return ret; | 1848 | return ret; |
diff --git a/include/linux/filter.h b/include/linux/filter.h index a51a5361695f..43aa1f8855c7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h | |||
@@ -465,14 +465,10 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, | |||
465 | void bpf_prog_destroy(struct bpf_prog *fp); | 465 | void bpf_prog_destroy(struct bpf_prog *fp); |
466 | 466 | ||
467 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); | 467 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); |
468 | int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, | ||
469 | bool locked); | ||
470 | int sk_attach_bpf(u32 ufd, struct sock *sk); | 468 | int sk_attach_bpf(u32 ufd, struct sock *sk); |
471 | int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk); | 469 | int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk); |
472 | int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk); | 470 | int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk); |
473 | int sk_detach_filter(struct sock *sk); | 471 | int sk_detach_filter(struct sock *sk); |
474 | int __sk_detach_filter(struct sock *sk, bool locked); | ||
475 | |||
476 | int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, | 472 | int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, |
477 | unsigned int len); | 473 | unsigned int len); |
478 | 474 | ||
diff --git a/net/core/filter.c b/net/core/filter.c index ca7f832b2980..e8486ba601ea 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -1149,8 +1149,7 @@ void bpf_prog_destroy(struct bpf_prog *fp) | |||
1149 | } | 1149 | } |
1150 | EXPORT_SYMBOL_GPL(bpf_prog_destroy); | 1150 | EXPORT_SYMBOL_GPL(bpf_prog_destroy); |
1151 | 1151 | ||
1152 | static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk, | 1152 | static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) |
1153 | bool locked) | ||
1154 | { | 1153 | { |
1155 | struct sk_filter *fp, *old_fp; | 1154 | struct sk_filter *fp, *old_fp; |
1156 | 1155 | ||
@@ -1166,8 +1165,10 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk, | |||
1166 | return -ENOMEM; | 1165 | return -ENOMEM; |
1167 | } | 1166 | } |
1168 | 1167 | ||
1169 | old_fp = rcu_dereference_protected(sk->sk_filter, locked); | 1168 | old_fp = rcu_dereference_protected(sk->sk_filter, |
1169 | lockdep_sock_is_held(sk)); | ||
1170 | rcu_assign_pointer(sk->sk_filter, fp); | 1170 | rcu_assign_pointer(sk->sk_filter, fp); |
1171 | |||
1171 | if (old_fp) | 1172 | if (old_fp) |
1172 | sk_filter_uncharge(sk, old_fp); | 1173 | sk_filter_uncharge(sk, old_fp); |
1173 | 1174 | ||
@@ -1246,8 +1247,7 @@ struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk) | |||
1246 | * occurs or there is insufficient memory for the filter a negative | 1247 | * occurs or there is insufficient memory for the filter a negative |
1247 | * errno code is returned. On success the return is zero. | 1248 | * errno code is returned. On success the return is zero. |
1248 | */ | 1249 | */ |
1249 | int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, | 1250 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) |
1250 | bool locked) | ||
1251 | { | 1251 | { |
1252 | struct bpf_prog *prog = __get_filter(fprog, sk); | 1252 | struct bpf_prog *prog = __get_filter(fprog, sk); |
1253 | int err; | 1253 | int err; |
@@ -1255,7 +1255,7 @@ int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, | |||
1255 | if (IS_ERR(prog)) | 1255 | if (IS_ERR(prog)) |
1256 | return PTR_ERR(prog); | 1256 | return PTR_ERR(prog); |
1257 | 1257 | ||
1258 | err = __sk_attach_prog(prog, sk, locked); | 1258 | err = __sk_attach_prog(prog, sk); |
1259 | if (err < 0) { | 1259 | if (err < 0) { |
1260 | __bpf_prog_release(prog); | 1260 | __bpf_prog_release(prog); |
1261 | return err; | 1261 | return err; |
@@ -1263,12 +1263,7 @@ int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, | |||
1263 | 1263 | ||
1264 | return 0; | 1264 | return 0; |
1265 | } | 1265 | } |
1266 | EXPORT_SYMBOL_GPL(__sk_attach_filter); | 1266 | EXPORT_SYMBOL_GPL(sk_attach_filter); |
1267 | |||
1268 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | ||
1269 | { | ||
1270 | return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk)); | ||
1271 | } | ||
1272 | 1267 | ||
1273 | int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk) | 1268 | int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk) |
1274 | { | 1269 | { |
@@ -1314,7 +1309,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) | |||
1314 | if (IS_ERR(prog)) | 1309 | if (IS_ERR(prog)) |
1315 | return PTR_ERR(prog); | 1310 | return PTR_ERR(prog); |
1316 | 1311 | ||
1317 | err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk)); | 1312 | err = __sk_attach_prog(prog, sk); |
1318 | if (err < 0) { | 1313 | if (err < 0) { |
1319 | bpf_prog_put(prog); | 1314 | bpf_prog_put(prog); |
1320 | return err; | 1315 | return err; |
@@ -2255,7 +2250,7 @@ static int __init register_sk_filter_ops(void) | |||
2255 | } | 2250 | } |
2256 | late_initcall(register_sk_filter_ops); | 2251 | late_initcall(register_sk_filter_ops); |
2257 | 2252 | ||
2258 | int __sk_detach_filter(struct sock *sk, bool locked) | 2253 | int sk_detach_filter(struct sock *sk) |
2259 | { | 2254 | { |
2260 | int ret = -ENOENT; | 2255 | int ret = -ENOENT; |
2261 | struct sk_filter *filter; | 2256 | struct sk_filter *filter; |
@@ -2263,7 +2258,8 @@ int __sk_detach_filter(struct sock *sk, bool locked) | |||
2263 | if (sock_flag(sk, SOCK_FILTER_LOCKED)) | 2258 | if (sock_flag(sk, SOCK_FILTER_LOCKED)) |
2264 | return -EPERM; | 2259 | return -EPERM; |
2265 | 2260 | ||
2266 | filter = rcu_dereference_protected(sk->sk_filter, locked); | 2261 | filter = rcu_dereference_protected(sk->sk_filter, |
2262 | lockdep_sock_is_held(sk)); | ||
2267 | if (filter) { | 2263 | if (filter) { |
2268 | RCU_INIT_POINTER(sk->sk_filter, NULL); | 2264 | RCU_INIT_POINTER(sk->sk_filter, NULL); |
2269 | sk_filter_uncharge(sk, filter); | 2265 | sk_filter_uncharge(sk, filter); |
@@ -2272,12 +2268,7 @@ int __sk_detach_filter(struct sock *sk, bool locked) | |||
2272 | 2268 | ||
2273 | return ret; | 2269 | return ret; |
2274 | } | 2270 | } |
2275 | EXPORT_SYMBOL_GPL(__sk_detach_filter); | 2271 | EXPORT_SYMBOL_GPL(sk_detach_filter); |
2276 | |||
2277 | int sk_detach_filter(struct sock *sk) | ||
2278 | { | ||
2279 | return __sk_detach_filter(sk, sock_owned_by_user(sk)); | ||
2280 | } | ||
2281 | 2272 | ||
2282 | int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, | 2273 | int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, |
2283 | unsigned int len) | 2274 | unsigned int len) |
@@ -2288,7 +2279,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, | |||
2288 | 2279 | ||
2289 | lock_sock(sk); | 2280 | lock_sock(sk); |
2290 | filter = rcu_dereference_protected(sk->sk_filter, | 2281 | filter = rcu_dereference_protected(sk->sk_filter, |
2291 | sock_owned_by_user(sk)); | 2282 | lockdep_sock_is_held(sk)); |
2292 | if (!filter) | 2283 | if (!filter) |
2293 | goto out; | 2284 | goto out; |
2294 | 2285 | ||