Skip to content

Commit f613e8b

Browse files
edumazetkuba-moo
authored andcommitted
net: add proper RCU protection to /proc/net/ptype
Yin Fengwei reported an RCU stall in ptype_seq_show() and provided a patch. Real issue is that ptype_seq_next() and ptype_seq_show() violate RCU rules. ptype_seq_show() runs under rcu_read_lock(), and reads pt->dev to get device name without any barrier. At the same time, concurrent writers can remove a packet_type structure (which is correctly freed after an RCU grace period) and clear pt->dev without an RCU grace period. Define ptype_iter_state to carry a dev pointer along seq_net_private: struct ptype_iter_state { struct seq_net_private p; struct net_device *dev; // added in this patch }; We need to record the device pointer in ptype_get_idx() and ptype_seq_next() so that ptype_seq_show() is safe against concurrent pt->dev changes. We also need to add full RCU protection in ptype_seq_next(). (Missing READ_ONCE() when reading list.next values) Many thanks to Dong Chenchen for providing a repro. Fixes: 1da177e ("Linux-2.6.12-rc2") Fixes: 1d10f8a ("net-procfs: show net devices bound packet types") Fixes: c353e89 ("net: introduce per netns packet chains") Reported-by: Yin Fengwei <[email protected]> Reported-by: Dong Chenchen <[email protected]> Closes: https://lore.kernel.org/netdev/CANn89iKRRKPnWjJmb-_3a=sq+9h6DvTQM4DBZHT5ZRGPMzQaiA@mail.gmail.com/T/#m7b80b9fc9b9267f90e0b7aad557595f686f9c50d Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Tested-by: Yin Fengwei <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 7821154 commit f613e8b

1 file changed

Lines changed: 34 additions & 16 deletions

File tree

net/core/net-procfs.c

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,14 @@ static const struct seq_operations softnet_seq_ops = {
170170
.show = softnet_seq_show,
171171
};
172172

173+
struct ptype_iter_state {
174+
struct seq_net_private p;
175+
struct net_device *dev;
176+
};
177+
173178
static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
174179
{
180+
struct ptype_iter_state *iter = seq->private;
175181
struct list_head *ptype_list = NULL;
176182
struct packet_type *pt = NULL;
177183
struct net_device *dev;
@@ -181,12 +187,16 @@ static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
181187
for_each_netdev_rcu(seq_file_net(seq), dev) {
182188
ptype_list = &dev->ptype_all;
183189
list_for_each_entry_rcu(pt, ptype_list, list) {
184-
if (i == pos)
190+
if (i == pos) {
191+
iter->dev = dev;
185192
return pt;
193+
}
186194
++i;
187195
}
188196
}
189197

198+
iter->dev = NULL;
199+
190200
list_for_each_entry_rcu(pt, &seq_file_net(seq)->ptype_all, list) {
191201
if (i == pos)
192202
return pt;
@@ -218,6 +228,7 @@ static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
218228

219229
static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
220230
{
231+
struct ptype_iter_state *iter = seq->private;
221232
struct net *net = seq_file_net(seq);
222233
struct net_device *dev;
223234
struct packet_type *pt;
@@ -229,19 +240,21 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
229240
return ptype_get_idx(seq, 0);
230241

231242
pt = v;
232-
nxt = pt->list.next;
233-
if (pt->dev) {
234-
if (nxt != &pt->dev->ptype_all)
243+
nxt = READ_ONCE(pt->list.next);
244+
dev = iter->dev;
245+
if (dev) {
246+
if (nxt != &dev->ptype_all)
235247
goto found;
236248

237-
dev = pt->dev;
238249
for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
239-
if (!list_empty(&dev->ptype_all)) {
240-
nxt = dev->ptype_all.next;
250+
nxt = READ_ONCE(dev->ptype_all.next);
251+
if (nxt != &dev->ptype_all) {
252+
iter->dev = dev;
241253
goto found;
242254
}
243255
}
244-
nxt = net->ptype_all.next;
256+
iter->dev = NULL;
257+
nxt = READ_ONCE(net->ptype_all.next);
245258
goto net_ptype_all;
246259
}
247260

@@ -252,20 +265,20 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
252265

253266
if (nxt == &net->ptype_all) {
254267
/* continue with ->ptype_specific if it's not empty */
255-
nxt = net->ptype_specific.next;
268+
nxt = READ_ONCE(net->ptype_specific.next);
256269
if (nxt != &net->ptype_specific)
257270
goto found;
258271
}
259272

260273
hash = 0;
261-
nxt = ptype_base[0].next;
274+
nxt = READ_ONCE(ptype_base[0].next);
262275
} else
263276
hash = ntohs(pt->type) & PTYPE_HASH_MASK;
264277

265278
while (nxt == &ptype_base[hash]) {
266279
if (++hash >= PTYPE_HASH_SIZE)
267280
return NULL;
268-
nxt = ptype_base[hash].next;
281+
nxt = READ_ONCE(ptype_base[hash].next);
269282
}
270283
found:
271284
return list_entry(nxt, struct packet_type, list);
@@ -279,19 +292,24 @@ static void ptype_seq_stop(struct seq_file *seq, void *v)
279292

280293
static int ptype_seq_show(struct seq_file *seq, void *v)
281294
{
295+
struct ptype_iter_state *iter = seq->private;
282296
struct packet_type *pt = v;
297+
struct net_device *dev;
283298

284-
if (v == SEQ_START_TOKEN)
299+
if (v == SEQ_START_TOKEN) {
285300
seq_puts(seq, "Type Device Function\n");
286-
else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
287-
(!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
301+
return 0;
302+
}
303+
dev = iter->dev;
304+
if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
305+
(!dev || net_eq(dev_net(dev), seq_file_net(seq)))) {
288306
if (pt->type == htons(ETH_P_ALL))
289307
seq_puts(seq, "ALL ");
290308
else
291309
seq_printf(seq, "%04x", ntohs(pt->type));
292310

293311
seq_printf(seq, " %-8s %ps\n",
294-
pt->dev ? pt->dev->name : "", pt->func);
312+
dev ? dev->name : "", pt->func);
295313
}
296314

297315
return 0;
@@ -315,7 +333,7 @@ static int __net_init dev_proc_net_init(struct net *net)
315333
&softnet_seq_ops))
316334
goto out_dev;
317335
if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
318-
sizeof(struct seq_net_private)))
336+
sizeof(struct ptype_iter_state)))
319337
goto out_softnet;
320338

321339
if (wext_proc_init(net))

0 commit comments

Comments
 (0)