Skip to content

Commit

Permalink
netfilter: refcounter conversions
Browse files Browse the repository at this point in the history
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: 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: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Reshetova, Elena authored and Pablo Neira Ayuso committed Mar 17, 2017
1 parent 3c679cb commit b54ab92
Show file tree
Hide file tree
Showing 21 changed files with 85 additions and 75 deletions.
16 changes: 9 additions & 7 deletions include/net/ip_vs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down
4 changes: 3 additions & 1 deletion include/net/netfilter/nf_conntrack_expect.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

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

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

/* Flags */
unsigned int flags;
Expand Down
3 changes: 2 additions & 1 deletion include/net/netfilter/nf_conntrack_timeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -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;
Expand Down
19 changes: 10 additions & 9 deletions net/ipv4/netfilter/ipt_CLUSTERIP.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
}


Expand All @@ -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);
}

Expand All @@ -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();
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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)) {
Expand Down
24 changes: 12 additions & 12 deletions net/netfilter/ipvs/ip_vs_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/ipvs/ip_vs_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport),
IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport),
cp->flags, atomic_read(&cp->refcnt));
cp->flags, refcount_read(&cp->refcnt));

ip_vs_conn_stats(cp, svc);
return cp;
Expand Down Expand Up @@ -1193,7 +1193,7 @@ struct ip_vs_conn *ip_vs_new_conn_out(struct ip_vs_service *svc,
IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport),
IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport),
cp->flags, atomic_read(&cp->refcnt));
cp->flags, refcount_read(&cp->refcnt));
LeaveFunction(12);
return cp;
}
Expand Down
12 changes: 6 additions & 6 deletions net/netfilter/ipvs/ip_vs_ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, int dest_af,
dest->vfwmark,
IP_VS_DBG_ADDR(dest->af, &dest->addr),
ntohs(dest->port),
atomic_read(&dest->refcnt));
refcount_read(&dest->refcnt));
if (dest->af == dest_af &&
ip_vs_addr_equal(dest_af, &dest->addr, daddr) &&
dest->port == dport &&
Expand Down Expand Up @@ -934,7 +934,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
atomic_set(&dest->activeconns, 0);
atomic_set(&dest->inactconns, 0);
atomic_set(&dest->persistconns, 0);
atomic_set(&dest->refcnt, 1);
refcount_set(&dest->refcnt, 1);

INIT_HLIST_NODE(&dest->d_list);
spin_lock_init(&dest->dst_lock);
Expand Down Expand Up @@ -998,7 +998,7 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
IP_VS_DBG_BUF(3, "Get destination %s:%u from trash, "
"dest->refcnt=%d, service %u/%s:%u\n",
IP_VS_DBG_ADDR(udest->af, &daddr), ntohs(dport),
atomic_read(&dest->refcnt),
refcount_read(&dest->refcnt),
dest->vfwmark,
IP_VS_DBG_ADDR(svc->af, &dest->vaddr),
ntohs(dest->vport));
Expand Down Expand Up @@ -1074,7 +1074,7 @@ static void __ip_vs_del_dest(struct netns_ipvs *ipvs, struct ip_vs_dest *dest,
spin_lock_bh(&ipvs->dest_trash_lock);
IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n",
IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port),
atomic_read(&dest->refcnt));
refcount_read(&dest->refcnt));
if (list_empty(&ipvs->dest_trash) && !cleanup)
mod_timer(&ipvs->dest_trash_timer,
jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1));
Expand Down Expand Up @@ -1157,7 +1157,7 @@ static void ip_vs_dest_trash_expire(unsigned long data)

spin_lock(&ipvs->dest_trash_lock);
list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) {
if (atomic_read(&dest->refcnt) > 1)
if (refcount_read(&dest->refcnt) > 1)
continue;
if (dest->idle_start) {
if (time_before(now, dest->idle_start +
Expand Down Expand Up @@ -1545,7 +1545,7 @@ ip_vs_forget_dev(struct ip_vs_dest *dest, struct net_device *dev)
dev->name,
IP_VS_DBG_ADDR(dest->af, &dest->addr),
ntohs(dest->port),
atomic_read(&dest->refcnt));
refcount_read(&dest->refcnt));
__ip_vs_dst_cache_reset(dest);
}
spin_unlock_bh(&dest->dst_lock);
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_lblc.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
atomic_read(&least->refcnt),
refcount_read(&least->refcnt),
atomic_read(&least->weight), loh);

return least;
Expand Down
6 changes: 3 additions & 3 deletions net/netfilter/ipvs/ip_vs_lblcr.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
atomic_read(&least->refcnt),
refcount_read(&least->refcnt),
atomic_read(&least->weight), loh);
return least;
}
Expand Down Expand Up @@ -249,7 +249,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
__func__,
IP_VS_DBG_ADDR(most->af, &most->addr), ntohs(most->port),
atomic_read(&most->activeconns),
atomic_read(&most->refcnt),
refcount_read(&most->refcnt),
atomic_read(&most->weight), moh);
return most;
}
Expand Down Expand Up @@ -612,7 +612,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
atomic_read(&least->refcnt),
refcount_read(&least->refcnt),
atomic_read(&least->weight), loh);

return least;
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_nq.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
atomic_read(&least->refcnt),
refcount_read(&least->refcnt),
atomic_read(&least->weight), loh);

return least;
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_proto_sctp.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
ntohs(cp->cport),
sctp_state_name(cp->state),
sctp_state_name(next_state),
atomic_read(&cp->refcnt));
refcount_read(&cp->refcnt));
if (dest) {
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
(next_state != IP_VS_SCTP_S_ESTABLISHED)) {
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_proto_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
ntohs(cp->cport),
tcp_state_name(cp->state),
tcp_state_name(new_state),
atomic_read(&cp->refcnt));
refcount_read(&cp->refcnt));

if (dest) {
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
Expand Down
Loading

0 comments on commit b54ab92

Please sign in to comment.