Skip to content

Commit

Permalink
net_sched: reorder pernet ops and act ops registrations
Browse files Browse the repository at this point in the history
Krister reported a kernel NULL pointer dereference after
tcf_action_init_1() invokes a_o->init(), it is a race condition
where one thread calling tcf_register_action() to initialize
the netns data after putting act ops in the global list and
the other thread searching the list and then calling
a_o->init(net, ...).

Fix this by moving the pernet ops registration before making
the action ops visible. This is fine because: a) we don't
rely on act_base in pernet ops->init(), b) in the worst case we
have a fully initialized netns but ops is still not ready so
new actions still can't be created.

Reported-by: Krister Johansen <[email protected]>
Tested-by: Krister Johansen <[email protected]>
Cc: Jamal Hadi Salim <[email protected]>
Signed-off-by: Cong Wang <[email protected]>
Acked-by: Jamal Hadi Salim <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
congwang authored and davem330 committed Oct 13, 2016
1 parent d1ef006 commit ab102b8
Showing 1 changed file with 11 additions and 8 deletions.
19 changes: 11 additions & 8 deletions net/sched/act_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,22 +341,25 @@ int tcf_register_action(struct tc_action_ops *act,
if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
return -EINVAL;

/* We have to register pernet ops before making the action ops visible,
* otherwise tcf_action_init_1() could get a partially initialized
* netns.
*/
ret = register_pernet_subsys(ops);
if (ret)
return ret;

write_lock(&act_mod_lock);
list_for_each_entry(a, &act_base, head) {
if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
write_unlock(&act_mod_lock);
unregister_pernet_subsys(ops);
return -EEXIST;
}
}
list_add_tail(&act->head, &act_base);
write_unlock(&act_mod_lock);

ret = register_pernet_subsys(ops);
if (ret) {
tcf_unregister_action(act, ops);
return ret;
}

return 0;
}
EXPORT_SYMBOL(tcf_register_action);
Expand All @@ -367,8 +370,6 @@ int tcf_unregister_action(struct tc_action_ops *act,
struct tc_action_ops *a;
int err = -ENOENT;

unregister_pernet_subsys(ops);

write_lock(&act_mod_lock);
list_for_each_entry(a, &act_base, head) {
if (a == act) {
Expand All @@ -378,6 +379,8 @@ int tcf_unregister_action(struct tc_action_ops *act,
}
}
write_unlock(&act_mod_lock);
if (!err)
unregister_pernet_subsys(ops);
return err;
}
EXPORT_SYMBOL(tcf_unregister_action);
Expand Down

0 comments on commit ab102b8

Please sign in to comment.