Browse Source

[arp] Prevent ARP cache entries from being deleted mid-transmission

Each ARP cache entry maintains a transmission queue, which is sent out
as soon as the link-layer address is known.  If multiple packets are
queued, then it is possible for memory pressure to cause the ARP cache
discarder to be invoked during transmission of the first packet, which
may cause the ARP cache entry to be deleted before the second packet
can be sent.  This results in an invalid pointer dereference.

Avoid this problem by reference-counting ARP cache entries and
ensuring that an extra reference is held while processing the
transmission queue, and by using list_first_entry() rather than
list_for_each_entry_safe() to traverse the queue.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
19859d8ead
1 changed files with 38 additions and 11 deletions
  1. 38
    11
      src/net/arp.c

+ 38
- 11
src/net/arp.c View File

31
 #include <ipxe/retry.h>
31
 #include <ipxe/retry.h>
32
 #include <ipxe/timer.h>
32
 #include <ipxe/timer.h>
33
 #include <ipxe/malloc.h>
33
 #include <ipxe/malloc.h>
34
+#include <ipxe/refcnt.h>
34
 #include <ipxe/arp.h>
35
 #include <ipxe/arp.h>
35
 
36
 
36
 /** @file
37
 /** @file
51
 
52
 
52
 /** An ARP cache entry */
53
 /** An ARP cache entry */
53
 struct arp_entry {
54
 struct arp_entry {
55
+	/** Reference count */
56
+	struct refcnt refcnt;
54
 	/** List of ARP cache entries */
57
 	/** List of ARP cache entries */
55
 	struct list_head list;
58
 	struct list_head list;
56
 	/** Network device */
59
 	/** Network device */
76
 
79
 
77
 static void arp_expired ( struct retry_timer *timer, int over );
80
 static void arp_expired ( struct retry_timer *timer, int over );
78
 
81
 
82
+/**
83
+ * Free ARP cache entry
84
+ *
85
+ * @v refcnt		Reference count
86
+ */
87
+static void arp_free ( struct refcnt *refcnt ) {
88
+	struct arp_entry *arp =
89
+		container_of ( refcnt, struct arp_entry, refcnt );
90
+
91
+	/* Sanity check */
92
+	assert ( list_empty ( &arp->tx_queue ) );
93
+
94
+	/* Drop reference to network device */
95
+	netdev_put ( arp->netdev );
96
+
97
+	/* Free entry */
98
+	free ( arp );
99
+}
100
+
79
 /**
101
 /**
80
  * Create ARP cache entry
102
  * Create ARP cache entry
81
  *
103
  *
91
 				       const void *net_source ) {
113
 				       const void *net_source ) {
92
 	struct arp_entry *arp;
114
 	struct arp_entry *arp;
93
 
115
 
94
-	/* Allocate entry and add to cache */
116
+	/* Allocate and initialise entry */
95
 	arp = zalloc ( sizeof ( *arp ) );
117
 	arp = zalloc ( sizeof ( *arp ) );
96
 	if ( ! arp )
118
 	if ( ! arp )
97
 		return NULL;
119
 		return NULL;
98
-
99
-	/* Initialise entry and add to cache */
120
+	ref_init ( &arp->refcnt, arp_free );
100
 	arp->netdev = netdev_get ( netdev );
121
 	arp->netdev = netdev_get ( netdev );
101
 	arp->net_protocol = net_protocol;
122
 	arp->net_protocol = net_protocol;
102
 	memcpy ( arp->net_dest, net_dest,
123
 	memcpy ( arp->net_dest, net_dest,
103
 		 net_protocol->net_addr_len );
124
 		 net_protocol->net_addr_len );
104
 	memcpy ( arp->net_source, net_source,
125
 	memcpy ( arp->net_source, net_source,
105
 		 net_protocol->net_addr_len );
126
 		 net_protocol->net_addr_len );
106
-	timer_init ( &arp->timer, arp_expired, NULL );
127
+	timer_init ( &arp->timer, arp_expired, &arp->refcnt );
107
 	arp->timer.min_timeout = ARP_MIN_TIMEOUT;
128
 	arp->timer.min_timeout = ARP_MIN_TIMEOUT;
108
 	arp->timer.max_timeout = ARP_MAX_TIMEOUT;
129
 	arp->timer.max_timeout = ARP_MAX_TIMEOUT;
109
 	INIT_LIST_HEAD ( &arp->tx_queue );
130
 	INIT_LIST_HEAD ( &arp->tx_queue );
110
-	list_add ( &arp->list, &arp_entries );
111
 
131
 
112
 	/* Start timer running to trigger initial transmission */
132
 	/* Start timer running to trigger initial transmission */
113
 	start_timer_nodelay ( &arp->timer );
133
 	start_timer_nodelay ( &arp->timer );
114
 
134
 
135
+	/* Transfer ownership to cache */
136
+	list_add ( &arp->list, &arp_entries );
137
+
115
 	DBGC ( arp, "ARP %p %s %s %s created\n", arp, netdev->name,
138
 	DBGC ( arp, "ARP %p %s %s %s created\n", arp, netdev->name,
116
 	       net_protocol->name, net_protocol->ntoa ( net_dest ) );
139
 	       net_protocol->name, net_protocol->ntoa ( net_dest ) );
117
 	return arp;
140
 	return arp;
174
 	       net_protocol->name, net_protocol->ntoa ( arp->net_dest ),
197
 	       net_protocol->name, net_protocol->ntoa ( arp->net_dest ),
175
 	       strerror ( rc ) );
198
 	       strerror ( rc ) );
176
 
199
 
177
-	/* Drop reference to network device, remove from cache and free */
178
-	netdev_put ( arp->netdev );
200
+	/* Remove from cache and drop reference */
179
 	list_del ( &arp->list );
201
 	list_del ( &arp->list );
180
-	free ( arp );
202
+	ref_put ( &arp->refcnt );
181
 }
203
 }
182
 
204
 
183
 /**
205
 /**
241
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
263
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
242
 	struct net_protocol *net_protocol = arp->net_protocol;
264
 	struct net_protocol *net_protocol = arp->net_protocol;
243
 	struct io_buffer *iobuf;
265
 	struct io_buffer *iobuf;
244
-	struct io_buffer *tmp;
245
 	int rc;
266
 	int rc;
246
 
267
 
247
 	DBGC ( arp, "ARP %p %s %s %s updated => %s\n", arp, netdev->name,
268
 	DBGC ( arp, "ARP %p %s %s %s updated => %s\n", arp, netdev->name,
254
 	/* Stop retransmission timer */
275
 	/* Stop retransmission timer */
255
 	stop_timer ( &arp->timer );
276
 	stop_timer ( &arp->timer );
256
 
277
 
257
-	/* Transmit any packets in queue */
258
-	list_for_each_entry_safe ( iobuf, tmp, &arp->tx_queue, list ) {
278
+	/* Transmit any packets in queue.  Take out a temporary
279
+	 * reference on the entry to prevent it from going out of
280
+	 * scope during the call to net_tx().
281
+	 */
282
+	ref_get ( &arp->refcnt );
283
+	while ( ( iobuf = list_first_entry ( &arp->tx_queue, struct io_buffer,
284
+					     list ) ) != NULL ) {
259
 		DBGC2 ( arp, "ARP %p %s %s %s transmitting deferred packet\n",
285
 		DBGC2 ( arp, "ARP %p %s %s %s transmitting deferred packet\n",
260
 			arp, netdev->name, net_protocol->name,
286
 			arp, netdev->name, net_protocol->name,
261
 			net_protocol->ntoa ( arp->net_dest ) );
287
 			net_protocol->ntoa ( arp->net_dest ) );
267
 			/* Ignore error and continue */
293
 			/* Ignore error and continue */
268
 		}
294
 		}
269
 	}
295
 	}
296
+	ref_put ( &arp->refcnt );
270
 }
297
 }
271
 
298
 
272
 /**
299
 /**

Loading…
Cancel
Save