When packet_set_ring creates a ring buffer it will initialize a struct timer_list if the packet version is TPACKET_V3. This value can then be raced by a different thread calling setsockopt to set the version to TPACKET_V1 before packet_set_ring has finished. This leads to a use-after-free on a function pointer in the struct timer_list when the socket is closed as the previously initialized timer will not be deleted. The bug is fixed by taking lock_sock(sk) in packet_setsockopt when changing the packet version while also taking the lock at the start of packet_set_ring.
Unified · Split
| @@ -3648,19 +3648,25 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv · 3647 unmodified lines | ||
| 3648 | 3648 | |
| 3649 | 3649 | if (optlen != sizeof(val)) |
| 3650 | 3650 | return -EINVAL; |
| 3651 | − if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) | |
| 3652 | − return -EBUSY; | |
| 3653 | 3651 | if (copy_from_user(&val, optval, sizeof(val))) |
| 3654 | 3652 | return -EFAULT; |
| 3655 | 3653 | switch (val) { |
| 3656 | 3654 | case TPACKET_V1: |
| 3657 | 3655 | case TPACKET_V2: |
| 3658 | 3656 | case TPACKET_V3: |
| 3659 | − po->tp_version = val; | |
| 3660 | − return 0; | |
| 3657 | + break; | |
| 3661 | 3658 | default: |
| 3662 | 3659 | return -EINVAL; |
| 3663 | 3660 | } |
| 3661 | + lock_sock(sk); | |
| 3662 | + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { | |
| 3663 | + ret = -EBUSY; | |
| 3664 | + } else { | |
| 3665 | + po->tp_version = val; | |
| 3666 | + ret = 0; | |
| 3667 | + } | |
| 3668 | + release_sock(sk); | |
| 3669 | + return ret; | |
| 3664 | 3670 | } |
| 3665 | 3671 | case PACKET_RESERVE: |
| 3666 | 3672 | { |
| @@ -4164,6 +4170,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, · 497 unmodified lines | ||
| 4164 | 4170 | /* Added to avoid minimal code churn */ |
| 4165 | 4171 | struct tpacket_req *req = &req_u->req; |
| 4166 | 4172 | |
| 4173 | + lock_sock(sk); | |
| 4167 | 4174 | /* Opening a Tx-ring is NOT supported in TPACKET_V3 */ |
| 4168 | 4175 | if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) { |
| 4169 | 4176 | net_warn_ratelimited("Tx-ring is not supported.\n"); |
| @@ -4245,7 +4252,6 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, · 75 unmodified lines | ||
| 4245 | 4252 | goto out; |
| 4246 | 4253 | } |
| 4247 | 4254 | |
| 4248 | − lock_sock(sk); | |
| 4249 | 4255 | |
| 4250 | 4256 | /* Detach socket from network */ |
| 4251 | 4257 | spin_lock(&po->bind_lock); |
| @@ -4294,11 +4300,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, · 42 unmodified lines | ||
| 4294 | 4300 | if (!tx_ring) |
| 4295 | 4301 | prb_shutdown_retire_blk_timer(po, rb_queue); |
| 4296 | 4302 | } |
| 4297 | − release_sock(sk); | |
| 4298 | 4303 | |
| 4299 | 4304 | if (pg_vec) |
| 4300 | 4305 | free_pg_vec(pg_vec, order, req->tp_block_nr); |
| 4301 | 4306 | out: |
| 4307 | + release_sock(sk); | |
| 4302 | 4308 | return err; |
| 4303 | 4309 | } |
| 4304 | 4310 | |