Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b54ab92b authored by Reshetova, Elena's avatar Reshetova, Elena Committed by Pablo Neira Ayuso
Browse files

netfilter: refcounter conversions



refcount_t type and corresponding API (see include/linux/refcount.h)
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: default avatarElena Reshetova <elena.reshetova@intel.com>
Signed-off-by: default avatarHans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarDavid Windsor <dwindsor@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 3c679cba
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include <linux/list.h>                 /* for struct list_head */
#include <linux/spinlock.h>             /* for struct rwlock_t */
#include <linux/atomic.h>               /* for struct atomic_t */
#include <linux/refcount.h>             /* for struct refcount_t */

#include <linux/compiler.h>
#include <linux/timer.h>
#include <linux/bug.h>
@@ -525,7 +527,7 @@ struct ip_vs_conn {
	struct netns_ipvs	*ipvs;

	/* counter and timer */
	atomic_t		refcnt;		/* reference count */
	refcount_t		refcnt;		/* reference count */
	struct timer_list	timer;		/* Expiration timer */
	volatile unsigned long	timeout;	/* timeout */

@@ -667,7 +669,7 @@ struct ip_vs_dest {
	atomic_t		conn_flags;	/* flags to copy to conn */
	atomic_t		weight;		/* server weight */

	atomic_t		refcnt;		/* reference counter */
	refcount_t		refcnt;		/* reference counter */
	struct ip_vs_stats      stats;          /* statistics */
	unsigned long		idle_start;	/* start time, jiffies */

@@ -1211,14 +1213,14 @@ struct ip_vs_conn * ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af,
 */
static inline bool __ip_vs_conn_get(struct ip_vs_conn *cp)
{
	return atomic_inc_not_zero(&cp->refcnt);
	return refcount_inc_not_zero(&cp->refcnt);
}

/* put back the conn without restarting its timer */
static inline void __ip_vs_conn_put(struct ip_vs_conn *cp)
{
	smp_mb__before_atomic();
	atomic_dec(&cp->refcnt);
	refcount_dec(&cp->refcnt);
}
void ip_vs_conn_put(struct ip_vs_conn *cp);
void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport);
@@ -1410,18 +1412,18 @@ void ip_vs_try_bind_dest(struct ip_vs_conn *cp);

static inline void ip_vs_dest_hold(struct ip_vs_dest *dest)
{
	atomic_inc(&dest->refcnt);
	refcount_inc(&dest->refcnt);
}

static inline void ip_vs_dest_put(struct ip_vs_dest *dest)
{
	smp_mb__before_atomic();
	atomic_dec(&dest->refcnt);
	refcount_dec(&dest->refcnt);
}

static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
{
	if (atomic_dec_and_test(&dest->refcnt))
	if (refcount_dec_and_test(&dest->refcnt))
		kfree(dest);
}

+3 −1
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#ifndef _NF_CONNTRACK_EXPECT_H
#define _NF_CONNTRACK_EXPECT_H

#include <linux/refcount.h>

#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_zones.h>

@@ -37,7 +39,7 @@ struct nf_conntrack_expect {
	struct timer_list timeout;

	/* Usage count. */
	atomic_t use;
	refcount_t use;

	/* Flags */
	unsigned int flags;
+2 −1
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <net/net_namespace.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
#include <linux/refcount.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_extend.h>

@@ -12,7 +13,7 @@
struct ctnl_timeout {
	struct list_head	head;
	struct rcu_head		rcu_head;
	atomic_t		refcnt;
	refcount_t		refcnt;
	char			name[CTNL_TIMEOUT_NAME_MAX];
	__u16			l3num;
	struct nf_conntrack_l4proto *l4proto;
+10 −9
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/icmp.h>
#include <linux/if_arp.h>
#include <linux/seq_file.h>
#include <linux/refcount.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
@@ -40,8 +41,8 @@ MODULE_DESCRIPTION("Xtables: CLUSTERIP target");

struct clusterip_config {
	struct list_head list;			/* list of all configs */
	atomic_t refcount;			/* reference count */
	atomic_t entries;			/* number of entries/rules
	refcount_t refcount;			/* reference count */
	refcount_t entries;			/* number of entries/rules
						 * referencing us */

	__be32 clusterip;			/* the IP address */
@@ -77,7 +78,7 @@ struct clusterip_net {
static inline void
clusterip_config_get(struct clusterip_config *c)
{
	atomic_inc(&c->refcount);
	refcount_inc(&c->refcount);
}


@@ -89,7 +90,7 @@ static void clusterip_config_rcu_free(struct rcu_head *head)
static inline void
clusterip_config_put(struct clusterip_config *c)
{
	if (atomic_dec_and_test(&c->refcount))
	if (refcount_dec_and_test(&c->refcount))
		call_rcu_bh(&c->rcu, clusterip_config_rcu_free);
}

@@ -103,7 +104,7 @@ clusterip_config_entry_put(struct clusterip_config *c)
	struct clusterip_net *cn = net_generic(net, clusterip_net_id);

	local_bh_disable();
	if (atomic_dec_and_lock(&c->entries, &cn->lock)) {
	if (refcount_dec_and_lock(&c->entries, &cn->lock)) {
		list_del_rcu(&c->list);
		spin_unlock(&cn->lock);
		local_bh_enable();
@@ -149,10 +150,10 @@ clusterip_config_find_get(struct net *net, __be32 clusterip, int entry)
			c = NULL;
		else
#endif
		if (unlikely(!atomic_inc_not_zero(&c->refcount)))
		if (unlikely(!refcount_inc_not_zero(&c->refcount)))
			c = NULL;
		else if (entry)
			atomic_inc(&c->entries);
			refcount_inc(&c->entries);
	}
	rcu_read_unlock_bh();

@@ -188,8 +189,8 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
	clusterip_config_init_nodelist(c, i);
	c->hash_mode = i->hash_mode;
	c->hash_initval = i->hash_initval;
	atomic_set(&c->refcount, 1);
	atomic_set(&c->entries, 1);
	refcount_set(&c->refcount, 1);
	refcount_set(&c->entries, 1);

	spin_lock_bh(&cn->lock);
	if (__clusterip_config_find(net, ip)) {
+12 −12
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)

	if (!(cp->flags & IP_VS_CONN_F_HASHED)) {
		cp->flags |= IP_VS_CONN_F_HASHED;
		atomic_inc(&cp->refcnt);
		refcount_inc(&cp->refcnt);
		hlist_add_head_rcu(&cp->c_list, &ip_vs_conn_tab[hash]);
		ret = 1;
	} else {
@@ -215,7 +215,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
	if (cp->flags & IP_VS_CONN_F_HASHED) {
		hlist_del_rcu(&cp->c_list);
		cp->flags &= ~IP_VS_CONN_F_HASHED;
		atomic_dec(&cp->refcnt);
		refcount_dec(&cp->refcnt);
		ret = 1;
	} else
		ret = 0;
@@ -242,13 +242,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
	if (cp->flags & IP_VS_CONN_F_HASHED) {
		ret = false;
		/* Decrease refcnt and unlink conn only if we are last user */
		if (atomic_cmpxchg(&cp->refcnt, 1, 0) == 1) {
		if (refcount_dec_if_one(&cp->refcnt)) {
			hlist_del_rcu(&cp->c_list);
			cp->flags &= ~IP_VS_CONN_F_HASHED;
			ret = true;
		}
	} else
		ret = atomic_read(&cp->refcnt) ? false : true;
		ret = refcount_read(&cp->refcnt) ? false : true;

	spin_unlock(&cp->lock);
	ct_write_unlock_bh(hash);
@@ -475,7 +475,7 @@ static void __ip_vs_conn_put_timer(struct ip_vs_conn *cp)
void ip_vs_conn_put(struct ip_vs_conn *cp)
{
	if ((cp->flags & IP_VS_CONN_F_ONE_PACKET) &&
	    (atomic_read(&cp->refcnt) == 1) &&
	    (refcount_read(&cp->refcnt) == 1) &&
	    !timer_pending(&cp->timer))
		/* expire connection immediately */
		__ip_vs_conn_put_notimer(cp);
@@ -617,8 +617,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
		      IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
		      IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport),
		      ip_vs_fwd_tag(cp), cp->state,
		      cp->flags, atomic_read(&cp->refcnt),
		      atomic_read(&dest->refcnt));
		      cp->flags, refcount_read(&cp->refcnt),
		      refcount_read(&dest->refcnt));

	/* Update the connection counters */
	if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
@@ -714,8 +714,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
		      IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
		      IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport),
		      ip_vs_fwd_tag(cp), cp->state,
		      cp->flags, atomic_read(&cp->refcnt),
		      atomic_read(&dest->refcnt));
		      cp->flags, refcount_read(&cp->refcnt),
		      refcount_read(&dest->refcnt));

	/* Update the connection counters */
	if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
@@ -863,10 +863,10 @@ static void ip_vs_conn_expire(unsigned long data)

  expire_later:
	IP_VS_DBG(7, "delayed: conn->refcnt=%d conn->n_control=%d\n",
		  atomic_read(&cp->refcnt),
		  refcount_read(&cp->refcnt),
		  atomic_read(&cp->n_control));

	atomic_inc(&cp->refcnt);
	refcount_inc(&cp->refcnt);
	cp->timeout = 60*HZ;

	if (ipvs->sync_state & IP_VS_STATE_MASTER)
@@ -941,7 +941,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
	 * it in the table, so that other thread run ip_vs_random_dropentry
	 * but cannot drop this entry.
	 */
	atomic_set(&cp->refcnt, 1);
	refcount_set(&cp->refcnt, 1);

	cp->control = NULL;
	atomic_set(&cp->n_control, 0);
Loading