Skip to content
/ linux Public

Commit 3e4fbcb

Browse files
ralflicigregkh
authored andcommitted
ovpn: fix possible use-after-free in ovpn_net_xmit
[ Upstream commit a5ec7ba ] When building the skb_list in ovpn_net_xmit, skb_share_check will free the original skb if it is shared. The current implementation continues to use the stale skb pointer for subsequent operations: - peer lookup, - skb_dst_drop (even though all segments produced by skb_gso_segment will have a dst attached), - ovpn_peer_stats_increment_tx. Fix this by moving the peer lookup and skb_dst_drop before segmentation so that the original skb is still valid when used. Return early if all segments fail skb_share_check and the list ends up empty. Also switch ovpn_peer_stats_increment_tx to use skb_list.next; the next patch fixes the stats logic. Fixes: 08857b5 ("ovpn: implement basic TX path (UDP)") Signed-off-by: Ralf Lici <ralf@mandelbit.com> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net> Signed-off-by: Antonio Quartulli <antonio@openvpn.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 6f076ba commit 3e4fbcb

File tree

1 file changed

+31
-21
lines changed
  • drivers/net/ovpn

1 file changed

+31
-21
lines changed

drivers/net/ovpn/io.c

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,27 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
365365
/* verify IP header size in network packet */
366366
proto = ovpn_ip_check_protocol(skb);
367367
if (unlikely(!proto || skb->protocol != proto))
368-
goto drop;
368+
goto drop_no_peer;
369+
370+
/* retrieve peer serving the destination IP of this packet */
371+
peer = ovpn_peer_get_by_dst(ovpn, skb);
372+
if (unlikely(!peer)) {
373+
switch (skb->protocol) {
374+
case htons(ETH_P_IP):
375+
net_dbg_ratelimited("%s: no peer to send data to dst=%pI4\n",
376+
netdev_name(ovpn->dev),
377+
&ip_hdr(skb)->daddr);
378+
break;
379+
case htons(ETH_P_IPV6):
380+
net_dbg_ratelimited("%s: no peer to send data to dst=%pI6c\n",
381+
netdev_name(ovpn->dev),
382+
&ipv6_hdr(skb)->daddr);
383+
break;
384+
}
385+
goto drop_no_peer;
386+
}
387+
/* dst was needed for peer selection - it can now be dropped */
388+
skb_dst_drop(skb);
369389

370390
if (skb_is_gso(skb)) {
371391
segments = skb_gso_segment(skb, 0);
@@ -396,34 +416,24 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
396416

397417
__skb_queue_tail(&skb_list, curr);
398418
}
399-
skb_list.prev->next = NULL;
400419

401-
/* retrieve peer serving the destination IP of this packet */
402-
peer = ovpn_peer_get_by_dst(ovpn, skb);
403-
if (unlikely(!peer)) {
404-
switch (skb->protocol) {
405-
case htons(ETH_P_IP):
406-
net_dbg_ratelimited("%s: no peer to send data to dst=%pI4\n",
407-
netdev_name(ovpn->dev),
408-
&ip_hdr(skb)->daddr);
409-
break;
410-
case htons(ETH_P_IPV6):
411-
net_dbg_ratelimited("%s: no peer to send data to dst=%pI6c\n",
412-
netdev_name(ovpn->dev),
413-
&ipv6_hdr(skb)->daddr);
414-
break;
415-
}
416-
goto drop;
420+
/* no segments survived: don't jump to 'drop' because we already
421+
* incremented the counter for each failure in the loop
422+
*/
423+
if (unlikely(skb_queue_empty(&skb_list))) {
424+
ovpn_peer_put(peer);
425+
return NETDEV_TX_OK;
417426
}
418-
/* dst was needed for peer selection - it can now be dropped */
419-
skb_dst_drop(skb);
427+
skb_list.prev->next = NULL;
420428

421-
ovpn_peer_stats_increment_tx(&peer->vpn_stats, skb->len);
429+
ovpn_peer_stats_increment_tx(&peer->vpn_stats, skb_list.next->len);
422430
ovpn_send(ovpn, skb_list.next, peer);
423431

424432
return NETDEV_TX_OK;
425433

426434
drop:
435+
ovpn_peer_put(peer);
436+
drop_no_peer:
427437
dev_dstats_tx_dropped(ovpn->dev);
428438
skb_tx_error(skb);
429439
kfree_skb_list(skb);

0 commit comments

Comments
 (0)