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

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
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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;
+19 −2
Original line number Diff line number Diff line
@@ -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.
 */
+6 −3
Original line number Diff line number Diff line
@@ -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);
+7 −20
Original line number Diff line number Diff line
@@ -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 *
+19 −0
Original line number Diff line number Diff line
@@ -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",