aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Fastabend <john.fastabend@gmail.com>2018-06-30 09:17:41 -0400
committerDaniel Borkmann <daniel@iogearbox.net>2018-06-30 19:21:31 -0400
commit54fedb42c6537dcb0102e4a58a88456a6286999d (patch)
tree5ba57be260f4c18e7977edd75f5e60d3ce3859b6
parent9901c5d77e969d8215a8e8d087ef02e6feddc84c (diff)
bpf: sockmap, fix smap_list_map_remove when psock is in many maps
If a hashmap is free'd with open socks it removes the reference to the hash entry from the psock. If that is the last reference to the psock then it will also be free'd by the reference counting logic. However the current logic that removes the hash reference from the list of references is broken. In smap_list_remove() we first check if the sockmap entry matches and then check if the hashmap entry matches. But, the sockmap entry sill always match because its NULL in this case which causes the first entry to be removed from the list. If this is always the "right" entry (because the user adds/removes entries in order) then everything is OK but otherwise a subsequent bpf_tcp_close() may reference a free'd object. To fix this create two list handlers one for sockmap and one for sockhash. Reported-by: syzbot+0ce137753c78f7b6acc1@syzkaller.appspotmail.com Fixes: 81110384441a ("bpf: sockmap, add hash map support") Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r--kernel/bpf/sockmap.c34
1 files changed, 22 insertions, 12 deletions
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index bfdfbd199c3b..65a937ed5762 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -1602,17 +1602,27 @@ free_stab:
1602 return ERR_PTR(err); 1602 return ERR_PTR(err);
1603} 1603}
1604 1604
1605static void smap_list_remove(struct smap_psock *psock, 1605static void smap_list_map_remove(struct smap_psock *psock,
1606 struct sock **entry, 1606 struct sock **entry)
1607 struct htab_elem *hash_link)
1608{ 1607{
1609 struct smap_psock_map_entry *e, *tmp; 1608 struct smap_psock_map_entry *e, *tmp;
1610 1609
1611 list_for_each_entry_safe(e, tmp, &psock->maps, list) { 1610 list_for_each_entry_safe(e, tmp, &psock->maps, list) {
1612 if (e->entry == entry || e->hash_link == hash_link) { 1611 if (e->entry == entry)
1612 list_del(&e->list);
1613 }
1614}
1615
1616static void smap_list_hash_remove(struct smap_psock *psock,
1617 struct htab_elem *hash_link)
1618{
1619 struct smap_psock_map_entry *e, *tmp;
1620
1621 list_for_each_entry_safe(e, tmp, &psock->maps, list) {
1622 struct htab_elem *c = e->hash_link;
1623
1624 if (c == hash_link)
1613 list_del(&e->list); 1625 list_del(&e->list);
1614 break;
1615 }
1616 } 1626 }
1617} 1627}
1618 1628
@@ -1647,7 +1657,7 @@ static void sock_map_free(struct bpf_map *map)
1647 * to be null and queued for garbage collection. 1657 * to be null and queued for garbage collection.
1648 */ 1658 */
1649 if (likely(psock)) { 1659 if (likely(psock)) {
1650 smap_list_remove(psock, &stab->sock_map[i], NULL); 1660 smap_list_map_remove(psock, &stab->sock_map[i]);
1651 smap_release_sock(psock, sock); 1661 smap_release_sock(psock, sock);
1652 } 1662 }
1653 write_unlock_bh(&sock->sk_callback_lock); 1663 write_unlock_bh(&sock->sk_callback_lock);
@@ -1706,7 +1716,7 @@ static int sock_map_delete_elem(struct bpf_map *map, void *key)
1706 1716
1707 if (psock->bpf_parse) 1717 if (psock->bpf_parse)
1708 smap_stop_sock(psock, sock); 1718 smap_stop_sock(psock, sock);
1709 smap_list_remove(psock, &stab->sock_map[k], NULL); 1719 smap_list_map_remove(psock, &stab->sock_map[k]);
1710 smap_release_sock(psock, sock); 1720 smap_release_sock(psock, sock);
1711out: 1721out:
1712 write_unlock_bh(&sock->sk_callback_lock); 1722 write_unlock_bh(&sock->sk_callback_lock);
@@ -1908,7 +1918,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
1908 struct smap_psock *opsock = smap_psock_sk(osock); 1918 struct smap_psock *opsock = smap_psock_sk(osock);
1909 1919
1910 write_lock_bh(&osock->sk_callback_lock); 1920 write_lock_bh(&osock->sk_callback_lock);
1911 smap_list_remove(opsock, &stab->sock_map[i], NULL); 1921 smap_list_map_remove(opsock, &stab->sock_map[i]);
1912 smap_release_sock(opsock, osock); 1922 smap_release_sock(opsock, osock);
1913 write_unlock_bh(&osock->sk_callback_lock); 1923 write_unlock_bh(&osock->sk_callback_lock);
1914 } 1924 }
@@ -2142,7 +2152,7 @@ static void sock_hash_free(struct bpf_map *map)
2142 * (psock) to be null and queued for garbage collection. 2152 * (psock) to be null and queued for garbage collection.
2143 */ 2153 */
2144 if (likely(psock)) { 2154 if (likely(psock)) {
2145 smap_list_remove(psock, NULL, l); 2155 smap_list_hash_remove(psock, l);
2146 smap_release_sock(psock, sock); 2156 smap_release_sock(psock, sock);
2147 } 2157 }
2148 write_unlock_bh(&sock->sk_callback_lock); 2158 write_unlock_bh(&sock->sk_callback_lock);
@@ -2322,7 +2332,7 @@ static int sock_hash_ctx_update_elem(struct bpf_sock_ops_kern *skops,
2322 psock = smap_psock_sk(l_old->sk); 2332 psock = smap_psock_sk(l_old->sk);
2323 2333
2324 hlist_del_rcu(&l_old->hash_node); 2334 hlist_del_rcu(&l_old->hash_node);
2325 smap_list_remove(psock, NULL, l_old); 2335 smap_list_hash_remove(psock, l_old);
2326 smap_release_sock(psock, l_old->sk); 2336 smap_release_sock(psock, l_old->sk);
2327 free_htab_elem(htab, l_old); 2337 free_htab_elem(htab, l_old);
2328 } 2338 }
@@ -2390,7 +2400,7 @@ static int sock_hash_delete_elem(struct bpf_map *map, void *key)
2390 * to be null and queued for garbage collection. 2400 * to be null and queued for garbage collection.
2391 */ 2401 */
2392 if (likely(psock)) { 2402 if (likely(psock)) {
2393 smap_list_remove(psock, NULL, l); 2403 smap_list_hash_remove(psock, l);
2394 smap_release_sock(psock, sock); 2404 smap_release_sock(psock, sock);
2395 } 2405 }
2396 write_unlock_bh(&sock->sk_callback_lock); 2406 write_unlock_bh(&sock->sk_callback_lock);