Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/sched: cls_u32: fix refcount leak in the error path of u32_change()

when users replace cls_u32 filters with new ones having wrong parameters,
so that u32_change() fails to validate them, the kernel doesn't roll-back
correctly, and leaves semi-configured rules.

Fix this in u32_walk(), avoiding a call to the walker function on filters
that don't have a match rule connected. The side effect is, these "empty"
filters are not even dumped when present; but that shouldn't be a problem
as long as we are restoring the original behaviour, where semi-configured
filters were not even added in the error path of u32_change().

Fixes: 6676d5e416ee ("net: sched: set dedicated tcf_walker flag when tp is empty")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Davide Caratti and committed by
David S. Miller
275c44aa 615f22f5

+25
+25
net/sched/cls_u32.c
··· 1108 1108 return err; 1109 1109 } 1110 1110 1111 + static bool u32_hnode_empty(struct tc_u_hnode *ht, bool *non_root_ht) 1112 + { 1113 + int i; 1114 + 1115 + if (!ht) 1116 + return true; 1117 + if (!ht->is_root) { 1118 + *non_root_ht = true; 1119 + return false; 1120 + } 1121 + if (*non_root_ht) 1122 + return false; 1123 + if (ht->refcnt < 2) 1124 + return true; 1125 + 1126 + for (i = 0; i <= ht->divisor; i++) { 1127 + if (rtnl_dereference(ht->ht[i])) 1128 + return false; 1129 + } 1130 + return true; 1131 + } 1132 + 1111 1133 static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg, 1112 1134 bool rtnl_held) 1113 1135 { 1114 1136 struct tc_u_common *tp_c = tp->data; 1137 + bool non_root_ht = false; 1115 1138 struct tc_u_hnode *ht; 1116 1139 struct tc_u_knode *n; 1117 1140 unsigned int h; ··· 1147 1124 ht = rtnl_dereference(ht->next)) { 1148 1125 if (ht->prio != tp->prio) 1149 1126 continue; 1127 + if (u32_hnode_empty(ht, &non_root_ht)) 1128 + return; 1150 1129 if (arg->count >= arg->skip) { 1151 1130 if (arg->fn(tp, ht, arg) < 0) { 1152 1131 arg->stop = 1;