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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
8a534f8fb0
|
@ -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;
|
||||
unsigned long next_id = (unsigned long)id + 1;
|
||||
struct mlx5_fc *counter;
|
||||
unsigned long tmp;
|
||||
|
||||
rcu_read_lock();
|
||||
/* skip counters that are in idr, but not yet in counters list */
|
||||
while ((counter = idr_get_next_ul(&fc_stats->counters_idr,
|
||||
&next_id)) != NULL &&
|
||||
list_empty(&counter->list))
|
||||
next_id++;
|
||||
idr_for_each_entry_continue_ul(&fc_stats->counters_idr,
|
||||
counter, tmp, next_id) {
|
||||
if (!list_empty(&counter->list))
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return counter ? &counter->list : &fc_stats->counters;
|
||||
|
|
|
@ -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: IDR handle.
|
||||
* @entry: The type * to use as cursor.
|
||||
* @tmp: A temporary placeholder for ID.
|
||||
* @id: Entry ID.
|
||||
*
|
||||
* @entry and @id do not need to be initialized before the loop, and
|
||||
* after normal termination @entry is left with the value NULL. This
|
||||
* is convenient for a "not found" value.
|
||||
*/
|
||||
#define idr_for_each_entry_ul(idr, entry, id) \
|
||||
for (id = 0; ((entry) = idr_get_next_ul(idr, &(id))) != NULL; ++id)
|
||||
#define idr_for_each_entry_ul(idr, entry, tmp, 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
|
||||
|
@ -213,6 +216,20 @@ static inline void idr_preload_end(void)
|
|||
entry; \
|
||||
++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.
|
||||
*/
|
||||
|
|
|
@ -221,12 +221,13 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
|
|||
struct idr *idr = &idrinfo->action_idr;
|
||||
struct tc_action *p;
|
||||
unsigned long id = 1;
|
||||
unsigned long tmp;
|
||||
|
||||
mutex_lock(&idrinfo->lock);
|
||||
|
||||
s_i = cb->args[0];
|
||||
|
||||
idr_for_each_entry_ul(idr, p, id) {
|
||||
idr_for_each_entry_ul(idr, p, tmp, id) {
|
||||
index++;
|
||||
if (index < s_i)
|
||||
continue;
|
||||
|
@ -292,6 +293,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
|
|||
struct idr *idr = &idrinfo->action_idr;
|
||||
struct tc_action *p;
|
||||
unsigned long id = 1;
|
||||
unsigned long tmp;
|
||||
|
||||
nest = nla_nest_start_noflag(skb, 0);
|
||||
if (nest == NULL)
|
||||
|
@ -300,7 +302,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
|
|||
goto nla_put_failure;
|
||||
|
||||
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);
|
||||
if (ret == ACT_P_DELETED) {
|
||||
module_put(ops->owner);
|
||||
|
@ -533,8 +535,9 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
|
|||
struct tc_action *p;
|
||||
int ret;
|
||||
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);
|
||||
if (ret == ACT_P_DELETED)
|
||||
module_put(ops->owner);
|
||||
|
|
|
@ -524,24 +524,6 @@ static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle)
|
|||
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,
|
||||
bool *last, bool rtnl_held,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
@ -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,
|
||||
bool rtnl_held)
|
||||
{
|
||||
struct cls_fl_head *head = fl_head_dereference(tp);
|
||||
unsigned long id = arg->cookie, tmp;
|
||||
struct cls_fl_filter *f;
|
||||
|
||||
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) {
|
||||
__fl_put(f);
|
||||
arg->stop = 1;
|
||||
break;
|
||||
}
|
||||
__fl_put(f);
|
||||
arg->cookie++;
|
||||
arg->count++;
|
||||
}
|
||||
arg->cookie = id;
|
||||
}
|
||||
|
||||
static struct cls_fl_filter *
|
||||
|
|
|
@ -38,6 +38,25 @@
|
|||
"$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",
|
||||
"name": "Add 1M filters with the same action",
|
||||
|
|
Loading…
Reference in New Issue