forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									edcd9270be
								
							
						
					
					
						commit
						7b93640502
					
				
					 3 changed files with 10 additions and 9 deletions
				
			
		| 
						 | 
					@ -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;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
 | 
				
			||||||
			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 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
				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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue