Skip to content

Commit 52be865

Browse files
Krishna Trlubos
authored andcommitted
[nrf fromlist] net: l2: ethernet: Fix double free
In the case of no ARP entry, the incoming packet is added to the ARP's pending queue, while ARP is being resolved. Here a reference is taken by the ARP layer to the packet to avoid it being freed, but the Ethernet immediately puts down the reference and send the ARP packet to the driver. If the ARP request fails for some reason, L2 returns failure to net_if which then puts down the reference and the packet will be freed as the reference count is now zero. But the packet is still in the ARP's pending queue and after timeout ARP will put down the reference causing double free bus fault (double free message is only seen if the CONFIG_NET_PKT_LOG_LEVEL_DBG is enabled, so, a bit hard to debug. Fix this by clearing the ARP entry and pending queue after taking a reference and then free ARP packet, IP packets are either freed by ARP pending queue drain or net_if layer. Upstream PR: zephyrproject-rtos/zephyr#54949 Signed-off-by: Krishna T <[email protected]> (cherry picked from commit f4d25fd09ddd010eb42184d725eda64f012c2b13)
1 parent 469a7e2 commit 52be865

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

subsys/net/l2/ethernet/arp.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,19 @@ void net_arp_clear_cache(struct net_if *iface)
744744
k_mutex_unlock(&arp_mutex);
745745
}
746746

747+
int net_arp_clear_pending(struct net_if *iface, struct in_addr *dst)
748+
{
749+
struct arp_entry *entry = arp_entry_find_pending(iface, dst);
750+
751+
if (!entry) {
752+
return -ENOENT;
753+
}
754+
755+
arp_entry_cleanup(entry, true);
756+
757+
return 0;
758+
}
759+
747760
int net_arp_foreach(net_arp_cb_t cb, void *user_data)
748761
{
749762
int ret = 0;

subsys/net/l2/ethernet/arp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ struct net_pkt *net_arp_prepare(struct net_pkt *pkt,
4949
enum net_verdict net_arp_input(struct net_pkt *pkt,
5050
struct net_eth_hdr *eth_hdr);
5151

52+
int net_arp_clear_pending(struct net_if *iface,
53+
struct in_addr *dst);
54+
5255
struct arp_entry {
5356
sys_snode_t node;
5457
uint32_t req_start;
@@ -79,6 +82,7 @@ void net_arp_init(void);
7982
#define net_arp_clear_cache(...)
8083
#define net_arp_foreach(...) 0
8184
#define net_arp_init(...)
85+
#define net_arp_clear_pending(...) 0
8286

8387
#endif /* CONFIG_NET_ARP */
8488

subsys/net/l2/ethernet/ethernet.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,9 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
601601
{
602602
const struct ethernet_api *api = net_if_get_device(iface)->api;
603603
struct ethernet_context *ctx = net_if_l2_data(iface);
604-
uint16_t ptype;
604+
uint16_t ptype = 0;
605605
int ret;
606+
struct net_pkt *orig_pkt = pkt;
606607

607608
if (!api) {
608609
ret = -ENOENT;
@@ -717,6 +718,19 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
717718
if (ret != 0) {
718719
eth_stats_update_errors_tx(iface);
719720
ethernet_remove_l2_header(pkt);
721+
if (IS_ENABLED(CONFIG_NET_ARP) && ptype == htons(NET_ETH_PTYPE_ARP)) {
722+
/* Original packet was added to ARP's pending Q, so, to avoid it
723+
* being freed, take a reference, the reference is dropped when we
724+
* clear the pending Q in ARP and then it will be freed by net_if.
725+
*/
726+
net_pkt_ref(orig_pkt);
727+
if (net_arp_clear_pending(iface,
728+
(struct in_addr *)NET_IPV4_HDR(pkt)->dst)) {
729+
NET_DBG("Could not find pending ARP entry");
730+
}
731+
/* Free the ARP request */
732+
net_pkt_unref(pkt);
733+
}
720734
goto error;
721735
}
722736

0 commit comments

Comments
 (0)