Przeglądaj źródła

net, sched: convert Qdisc.refcnt from atomic_t to refcount_t

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Hans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: David Windsor <dwindsor@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Reshetova, Elena 8 lat temu
rodzic
commit
7b93640502
3 zmienionych plików z 10 dodań i 9 usunięć
  1. 2 1
      include/net/sch_generic.h
  2. 4 4
      net/sched/sch_api.c
  3. 4 4
      net/sched/sch_generic.c

+ 2 - 1
include/net/sch_generic.h

@@ -9,6 +9,7 @@
 #include <linux/percpu.h>
 #include <linux/percpu.h>
 #include <linux/dynamic_queue_limits.h>
 #include <linux/dynamic_queue_limits.h>
 #include <linux/list.h>
 #include <linux/list.h>
+#include <linux/refcount.h>
 #include <net/gen_stats.h>
 #include <net/gen_stats.h>
 #include <net/rtnetlink.h>
 #include <net/rtnetlink.h>
 
 
@@ -95,7 +96,7 @@ struct Qdisc {
 	struct sk_buff		*skb_bad_txq;
 	struct sk_buff		*skb_bad_txq;
 	struct rcu_head		rcu_head;
 	struct rcu_head		rcu_head;
 	int			padded;
 	int			padded;
-	atomic_t		refcnt;
+	refcount_t		refcnt;
 
 
 	spinlock_t		busylock ____cacheline_aligned_in_smp;
 	spinlock_t		busylock ____cacheline_aligned_in_smp;
 };
 };

+ 4 - 4
net/sched/sch_api.c

@@ -839,7 +839,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
 
 
 			old = dev_graft_qdisc(dev_queue, new);
 			old = dev_graft_qdisc(dev_queue, new);
 			if (new && i > 0)
 			if (new && i > 0)
-				atomic_inc(&new->refcnt);
+				refcount_inc(&new->refcnt);
 
 
 			if (!ingress)
 			if (!ingress)
 				qdisc_destroy(old);
 				qdisc_destroy(old);
@@ -850,7 +850,7 @@ skip:
 			notify_and_destroy(net, skb, n, classid,
 			notify_and_destroy(net, skb, n, classid,
 					   dev->qdisc, new);
 					   dev->qdisc, new);
 			if (new && !new->ops->attach)
 			if (new && !new->ops->attach)
-				atomic_inc(&new->refcnt);
+				refcount_inc(&new->refcnt);
 			dev->qdisc = new ? : &noop_qdisc;
 			dev->qdisc = new ? : &noop_qdisc;
 
 
 			if (new && new->ops->attach)
 			if (new && new->ops->attach)
@@ -1259,7 +1259,7 @@ replay:
 				if (q == p ||
 				if (q == p ||
 				    (p && check_loop(q, p, 0)))
 				    (p && check_loop(q, p, 0)))
 					return -ELOOP;
 					return -ELOOP;
-				atomic_inc(&q->refcnt);
+				refcount_inc(&q->refcnt);
 				goto graft;
 				goto graft;
 			} else {
 			} else {
 				if (!q)
 				if (!q)
@@ -1374,7 +1374,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
 	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
 	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
 	tcm->tcm_parent = clid;
 	tcm->tcm_parent = clid;
 	tcm->tcm_handle = q->handle;
 	tcm->tcm_handle = q->handle;
-	tcm->tcm_info = atomic_read(&q->refcnt);
+	tcm->tcm_info = refcount_read(&q->refcnt);
 	if (nla_put_string(skb, TCA_KIND, q->ops->id))
 	if (nla_put_string(skb, TCA_KIND, q->ops->id))
 		goto nla_put_failure;
 		goto nla_put_failure;
 	if (q->ops->dump && q->ops->dump(q, skb) < 0)
 	if (q->ops->dump && q->ops->dump(q, skb) < 0)

+ 4 - 4
net/sched/sch_generic.c

@@ -633,7 +633,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
 	sch->dequeue = ops->dequeue;
 	sch->dequeue = ops->dequeue;
 	sch->dev_queue = dev_queue;
 	sch->dev_queue = dev_queue;
 	dev_hold(dev);
 	dev_hold(dev);
-	atomic_set(&sch->refcnt, 1);
+	refcount_set(&sch->refcnt, 1);
 
 
 	return sch;
 	return sch;
 errout:
 errout:
@@ -701,7 +701,7 @@ void qdisc_destroy(struct Qdisc *qdisc)
 	const struct Qdisc_ops  *ops = qdisc->ops;
 	const struct Qdisc_ops  *ops = qdisc->ops;
 
 
 	if (qdisc->flags & TCQ_F_BUILTIN ||
 	if (qdisc->flags & TCQ_F_BUILTIN ||
-	    !atomic_dec_and_test(&qdisc->refcnt))
+	    !refcount_dec_and_test(&qdisc->refcnt))
 		return;
 		return;
 
 
 #ifdef CONFIG_NET_SCHED
 #ifdef CONFIG_NET_SCHED
@@ -739,7 +739,7 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
 	spin_lock_bh(root_lock);
 	spin_lock_bh(root_lock);
 
 
 	/* Prune old scheduler */
 	/* Prune old scheduler */
-	if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1)
+	if (oqdisc && refcount_read(&oqdisc->refcnt) <= 1)
 		qdisc_reset(oqdisc);
 		qdisc_reset(oqdisc);
 
 
 	/* ... and graft new one */
 	/* ... and graft new one */
@@ -785,7 +785,7 @@ static void attach_default_qdiscs(struct net_device *dev)
 	    dev->priv_flags & IFF_NO_QUEUE) {
 	    dev->priv_flags & IFF_NO_QUEUE) {
 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
 		dev->qdisc = txq->qdisc_sleeping;
 		dev->qdisc = txq->qdisc_sleeping;
-		atomic_inc(&dev->qdisc->refcnt);
+		refcount_inc(&dev->qdisc->refcnt);
 	} else {
 	} else {
 		qdisc = qdisc_create_dflt(txq, &mq_qdisc_ops, TC_H_ROOT);
 		qdisc = qdisc_create_dflt(txq, &mq_qdisc_ops, TC_H_ROOT);
 		if (qdisc) {
 		if (qdisc) {