Commit ecb3dea400d3beaf611ce76ac7a51d4230492cf2

Authored by Vlad Buslov
Committed by David S. Miller
1 parent a10674bf24

net: sched: flower: insert new filter to idr after setting its mask

When adding new filter to flower classifier, fl_change() inserts it to
handle_idr before initializing filter extensions and assigning it a mask.
Normally this ordering doesn't matter because all flower classifier ops
callbacks assume rtnl lock protection. However, when filter has an action
that doesn't have its kernel module loaded, rtnl lock is released before
call to request_module(). During this time the filter can be accessed bu
concurrent task before its initialization is completed, which can lead to a
crash.

Example case of NULL pointer dereference in concurrent dump:

Task 1                           Task 2

tc_new_tfilter()
 fl_change()
  idr_alloc_u32(fnew)
  fl_set_parms()
   tcf_exts_validate()
    tcf_action_init()
     tcf_action_init_1()
      rtnl_unlock()
      request_module()
      ...                        rtnl_lock()
      				 tc_dump_tfilter()
      				  tcf_chain_dump()
				   fl_walk()
				    idr_get_next_ul()
				    tcf_node_dump()
				     tcf_fill_node()
				      fl_dump()
				       mask = &f->mask->key; <- NULL ptr
      rtnl_lock()

Extension initialization and mask assignment don't depend on fnew->handle
that is allocated by idr_alloc_u32(). Move idr allocation code after action
creation and mask assignment in fl_change() to prevent concurrent access
to not fully initialized filter when rtnl lock is released to load action
module.

Fixes: 01683a146999 ("net: sched: refactor flower walk to iterate over idr")
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 22 additions and 21 deletions Side-by-side Diff

net/sched/cls_flower.c
... ... @@ -1348,46 +1348,46 @@
1348 1348 if (err < 0)
1349 1349 goto errout;
1350 1350  
1351   - if (!handle) {
1352   - handle = 1;
1353   - err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
1354   - INT_MAX, GFP_KERNEL);
1355   - } else if (!fold) {
1356   - /* user specifies a handle and it doesn't exist */
1357   - err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
1358   - handle, GFP_KERNEL);
1359   - }
1360   - if (err)
1361   - goto errout;
1362   - fnew->handle = handle;
1363   -
1364 1351 if (tb[TCA_FLOWER_FLAGS]) {
1365 1352 fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
1366 1353  
1367 1354 if (!tc_flags_valid(fnew->flags)) {
1368 1355 err = -EINVAL;
1369   - goto errout_idr;
  1356 + goto errout;
1370 1357 }
1371 1358 }
1372 1359  
1373 1360 err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr,
1374 1361 tp->chain->tmplt_priv, extack);
1375 1362 if (err)
1376   - goto errout_idr;
  1363 + goto errout;
1377 1364  
1378 1365 err = fl_check_assign_mask(head, fnew, fold, mask);
1379 1366 if (err)
1380   - goto errout_idr;
  1367 + goto errout;
1381 1368  
  1369 + if (!handle) {
  1370 + handle = 1;
  1371 + err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
  1372 + INT_MAX, GFP_KERNEL);
  1373 + } else if (!fold) {
  1374 + /* user specifies a handle and it doesn't exist */
  1375 + err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
  1376 + handle, GFP_KERNEL);
  1377 + }
  1378 + if (err)
  1379 + goto errout_mask;
  1380 + fnew->handle = handle;
  1381 +
1382 1382 if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
1383 1383 err = -EEXIST;
1384   - goto errout_mask;
  1384 + goto errout_idr;
1385 1385 }
1386 1386  
1387 1387 err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
1388 1388 fnew->mask->filter_ht_params);
1389 1389 if (err)
1390   - goto errout_mask;
  1390 + goto errout_idr;
1391 1391  
1392 1392 if (!tc_skip_hw(fnew->flags)) {
1393 1393 err = fl_hw_replace_filter(tp, fnew, extack);
1394 1394  
... ... @@ -1426,12 +1426,13 @@
1426 1426 rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node,
1427 1427 fnew->mask->filter_ht_params);
1428 1428  
1429   -errout_mask:
1430   - fl_mask_put(head, fnew->mask, false);
1431   -
1432 1429 errout_idr:
1433 1430 if (!fold)
1434 1431 idr_remove(&head->handle_idr, fnew->handle);
  1432 +
  1433 +errout_mask:
  1434 + fl_mask_put(head, fnew->mask, false);
  1435 +
1435 1436 errout:
1436 1437 tcf_exts_destroy(&fnew->exts);
1437 1438 kfree(fnew);