Commit 8a534f8f authored by David S. Miller's avatar David S. Miller

Merge branch 'idr-fix-overflow-cases-on-32-bit-CPU'

Cong Wang says:

====================
idr: fix overflow cases on 32-bit CPU

idr_get_next_ul() is problematic by design, it can't handle
the following overflow case well on 32-bit CPU:

u32 id = UINT_MAX;
idr_alloc_u32(&id);
while (idr_get_next_ul(&id) != NULL)
 id++;

when 'id' overflows and becomes 0 after UINT_MAX, the loop
goes infinite.

Fix this by eliminating external users of idr_get_next_ul()
and migrating them to idr_for_each_entry_continue_ul(). And
add an additional parameter for these iteration macros to detect
overflow properly.

Please merge this through networking tree, as all the users
are in networking subsystem.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents eb1f5c02 95b9395b
...@@ -102,13 +102,15 @@ static struct list_head *mlx5_fc_counters_lookup_next(struct mlx5_core_dev *dev, ...@@ -102,13 +102,15 @@ static struct list_head *mlx5_fc_counters_lookup_next(struct mlx5_core_dev *dev,
struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
unsigned long next_id = (unsigned long)id + 1; unsigned long next_id = (unsigned long)id + 1;
struct mlx5_fc *counter; struct mlx5_fc *counter;
unsigned long tmp;
rcu_read_lock(); rcu_read_lock();
/* skip counters that are in idr, but not yet in counters list */ /* skip counters that are in idr, but not yet in counters list */
while ((counter = idr_get_next_ul(&fc_stats->counters_idr, idr_for_each_entry_continue_ul(&fc_stats->counters_idr,
&next_id)) != NULL && counter, tmp, next_id) {
list_empty(&counter->list)) if (!list_empty(&counter->list))
next_id++; break;
}
rcu_read_unlock(); rcu_read_unlock();
return counter ? &counter->list : &fc_stats->counters; return counter ? &counter->list : &fc_stats->counters;
......
...@@ -191,14 +191,17 @@ static inline void idr_preload_end(void) ...@@ -191,14 +191,17 @@ static inline void idr_preload_end(void)
* idr_for_each_entry_ul() - Iterate over an IDR's elements of a given type. * idr_for_each_entry_ul() - Iterate over an IDR's elements of a given type.
* @idr: IDR handle. * @idr: IDR handle.
* @entry: The type * to use as cursor. * @entry: The type * to use as cursor.
* @tmp: A temporary placeholder for ID.
* @id: Entry ID. * @id: Entry ID.
* *
* @entry and @id do not need to be initialized before the loop, and * @entry and @id do not need to be initialized before the loop, and
* after normal termination @entry is left with the value NULL. This * after normal termination @entry is left with the value NULL. This
* is convenient for a "not found" value. * is convenient for a "not found" value.
*/ */
#define idr_for_each_entry_ul(idr, entry, id) \ #define idr_for_each_entry_ul(idr, entry, tmp, id) \
for (id = 0; ((entry) = idr_get_next_ul(idr, &(id))) != NULL; ++id) for (tmp = 0, id = 0; \
tmp <= id && ((entry) = idr_get_next_ul(idr, &(id))) != NULL; \
tmp = id, ++id)
/** /**
* idr_for_each_entry_continue() - Continue iteration over an IDR's elements of a given type * idr_for_each_entry_continue() - Continue iteration over an IDR's elements of a given type
...@@ -213,6 +216,20 @@ static inline void idr_preload_end(void) ...@@ -213,6 +216,20 @@ static inline void idr_preload_end(void)
entry; \ entry; \
++id, (entry) = idr_get_next((idr), &(id))) ++id, (entry) = idr_get_next((idr), &(id)))
/**
* idr_for_each_entry_continue_ul() - Continue iteration over an IDR's elements of a given type
* @idr: IDR handle.
* @entry: The type * to use as a cursor.
* @tmp: A temporary placeholder for ID.
* @id: Entry ID.
*
* Continue to iterate over entries, continuing after the current position.
*/
#define idr_for_each_entry_continue_ul(idr, entry, tmp, id) \
for (tmp = id; \
tmp <= id && ((entry) = idr_get_next_ul(idr, &(id))) != NULL; \
tmp = id, ++id)
/* /*
* IDA - ID Allocator, use when translation from id to pointer isn't necessary. * IDA - ID Allocator, use when translation from id to pointer isn't necessary.
*/ */
......
...@@ -221,12 +221,13 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, ...@@ -221,12 +221,13 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
struct idr *idr = &idrinfo->action_idr; struct idr *idr = &idrinfo->action_idr;
struct tc_action *p; struct tc_action *p;
unsigned long id = 1; unsigned long id = 1;
unsigned long tmp;
mutex_lock(&idrinfo->lock); mutex_lock(&idrinfo->lock);
s_i = cb->args[0]; s_i = cb->args[0];
idr_for_each_entry_ul(idr, p, id) { idr_for_each_entry_ul(idr, p, tmp, id) {
index++; index++;
if (index < s_i) if (index < s_i)
continue; continue;
...@@ -292,6 +293,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, ...@@ -292,6 +293,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
struct idr *idr = &idrinfo->action_idr; struct idr *idr = &idrinfo->action_idr;
struct tc_action *p; struct tc_action *p;
unsigned long id = 1; unsigned long id = 1;
unsigned long tmp;
nest = nla_nest_start_noflag(skb, 0); nest = nla_nest_start_noflag(skb, 0);
if (nest == NULL) if (nest == NULL)
...@@ -300,7 +302,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, ...@@ -300,7 +302,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
goto nla_put_failure; goto nla_put_failure;
mutex_lock(&idrinfo->lock); mutex_lock(&idrinfo->lock);
idr_for_each_entry_ul(idr, p, id) { idr_for_each_entry_ul(idr, p, tmp, id) {
ret = tcf_idr_release_unsafe(p); ret = tcf_idr_release_unsafe(p);
if (ret == ACT_P_DELETED) { if (ret == ACT_P_DELETED) {
module_put(ops->owner); module_put(ops->owner);
...@@ -533,8 +535,9 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops, ...@@ -533,8 +535,9 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
struct tc_action *p; struct tc_action *p;
int ret; int ret;
unsigned long id = 1; unsigned long id = 1;
unsigned long tmp;
idr_for_each_entry_ul(idr, p, id) { idr_for_each_entry_ul(idr, p, tmp, id) {
ret = __tcf_idr_release(p, false, true); ret = __tcf_idr_release(p, false, true);
if (ret == ACT_P_DELETED) if (ret == ACT_P_DELETED)
module_put(ops->owner); module_put(ops->owner);
......
...@@ -524,24 +524,6 @@ static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle) ...@@ -524,24 +524,6 @@ static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle)
return f; return f;
} }
static struct cls_fl_filter *fl_get_next_filter(struct tcf_proto *tp,
unsigned long *handle)
{
struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *f;
rcu_read_lock();
while ((f = idr_get_next_ul(&head->handle_idr, handle))) {
/* don't return filters that are being deleted */
if (refcount_inc_not_zero(&f->refcnt))
break;
++(*handle);
}
rcu_read_unlock();
return f;
}
static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
bool *last, bool rtnl_held, bool *last, bool rtnl_held,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -1691,20 +1673,25 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last, ...@@ -1691,20 +1673,25 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg, static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
bool rtnl_held) bool rtnl_held)
{ {
struct cls_fl_head *head = fl_head_dereference(tp);
unsigned long id = arg->cookie, tmp;
struct cls_fl_filter *f; struct cls_fl_filter *f;
arg->count = arg->skip; arg->count = arg->skip;
while ((f = fl_get_next_filter(tp, &arg->cookie)) != NULL) { idr_for_each_entry_continue_ul(&head->handle_idr, f, tmp, id) {
/* don't return filters that are being deleted */
if (!refcount_inc_not_zero(&f->refcnt))
continue;
if (arg->fn(tp, f, arg) < 0) { if (arg->fn(tp, f, arg) < 0) {
__fl_put(f); __fl_put(f);
arg->stop = 1; arg->stop = 1;
break; break;
} }
__fl_put(f); __fl_put(f);
arg->cookie++;
arg->count++; arg->count++;
} }
arg->cookie = id;
} }
static struct cls_fl_filter * static struct cls_fl_filter *
......
...@@ -38,6 +38,25 @@ ...@@ -38,6 +38,25 @@
"$TC qdisc del dev $DEV1 clsact" "$TC qdisc del dev $DEV1 clsact"
] ]
}, },
{
"id": "2ff3",
"name": "Add flower with max handle and then dump it",
"category": [
"filter",
"flower"
],
"setup": [
"$TC qdisc add dev $DEV2 ingress"
],
"cmdUnderTest": "$TC filter add dev $DEV2 protocol ip pref 1 parent ffff: handle 0xffffffff flower action ok",
"expExitCode": "0",
"verifyCmd": "$TC filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower.*handle 0xffffffff",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DEV2 ingress"
]
},
{ {
"id": "d052", "id": "d052",
"name": "Add 1M filters with the same action", "name": "Add 1M filters with the same action",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment