mirror of https://gitee.com/openkylin/linux.git
net_sched: reorder pernet ops and act ops registrations
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 <kjlx@templeofstupid.com> Tested-by: Krister Johansen <kjlx@templeofstupid.com> Cc: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d1ef006dc1
commit
ab102b80ce
|
@ -341,22 +341,25 @@ int tcf_register_action(struct tc_action_ops *act,
|
||||||
if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
|
if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
|
||||||
return -EINVAL;
|
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);
|
write_lock(&act_mod_lock);
|
||||||
list_for_each_entry(a, &act_base, head) {
|
list_for_each_entry(a, &act_base, head) {
|
||||||
if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
|
if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
|
||||||
write_unlock(&act_mod_lock);
|
write_unlock(&act_mod_lock);
|
||||||
|
unregister_pernet_subsys(ops);
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_add_tail(&act->head, &act_base);
|
list_add_tail(&act->head, &act_base);
|
||||||
write_unlock(&act_mod_lock);
|
write_unlock(&act_mod_lock);
|
||||||
|
|
||||||
ret = register_pernet_subsys(ops);
|
|
||||||
if (ret) {
|
|
||||||
tcf_unregister_action(act, ops);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tcf_register_action);
|
EXPORT_SYMBOL(tcf_register_action);
|
||||||
|
@ -367,8 +370,6 @@ int tcf_unregister_action(struct tc_action_ops *act,
|
||||||
struct tc_action_ops *a;
|
struct tc_action_ops *a;
|
||||||
int err = -ENOENT;
|
int err = -ENOENT;
|
||||||
|
|
||||||
unregister_pernet_subsys(ops);
|
|
||||||
|
|
||||||
write_lock(&act_mod_lock);
|
write_lock(&act_mod_lock);
|
||||||
list_for_each_entry(a, &act_base, head) {
|
list_for_each_entry(a, &act_base, head) {
|
||||||
if (a == act) {
|
if (a == act) {
|
||||||
|
@ -378,6 +379,8 @@ int tcf_unregister_action(struct tc_action_ops *act,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write_unlock(&act_mod_lock);
|
write_unlock(&act_mod_lock);
|
||||||
|
if (!err)
|
||||||
|
unregister_pernet_subsys(ops);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tcf_unregister_action);
|
EXPORT_SYMBOL(tcf_unregister_action);
|
||||||
|
|
Loading…
Reference in New Issue