Skip to content

Commit

Permalink
Merge branch 'ipv4-ipv6-refcount_t'
Browse files Browse the repository at this point in the history
Elena Reshetova says:

====================
v2 ipv4/ipv6 refcount conversions

Changes in v2:
 * rebase on top of net-next
 * currently by default refcount_t = atomic_t (*) and uses all
   atomic standard operations unless CONFIG_REFCOUNT_FULL is enabled.
   This is a compromise for the systems that are critical on
   performance (such as net) and cannot accept even slight delay
   on the refcounter operations.

This series, for ipv4/ipv6 network components, replaces atomic_t reference
counters with the new refcount_t type and API (see include/linux/refcount.h).
By doing this we prevent intentional or accidental
underflows or overflows that can led to use-after-free vulnerabilities.

The patches are fully independent and can be cherry-picked separately.
In order to try with refcount functionality enabled in run-time,
CONFIG_REFCOUNT_FULL must be enabled.

NOTE: automatic kernel builder for some reason doesn't like all my
network branches and regularly times out the builds on these branches.
Suggestion for "waiting a day for a good coverage" doesn't work, as
we have seen with generic network conversions. So please wait for the
full report from kernel test rebot before merging further up.
This has been compile-tested in 116 configs, but 71 timed out (including
all s390-related configs again). I am trying to see if they can fix
build coverage for me in meanwhile.

* The respective change is currently merged into -next as
  "locking/refcount: Create unchecked atomic_t implementation".
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 4, 2017
2 parents 25f4535 + 0029c0d commit 2671e9f
Show file tree
Hide file tree
Showing 15 changed files with 56 additions and 51 deletions.
14 changes: 7 additions & 7 deletions include/net/addrconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev)
rcu_read_lock();
idev = rcu_dereference(dev->ip6_ptr);
if (idev)
atomic_inc(&idev->refcnt);
refcount_inc(&idev->refcnt);
rcu_read_unlock();
return idev;
}
Expand All @@ -332,36 +332,36 @@ void in6_dev_finish_destroy(struct inet6_dev *idev);

static inline void in6_dev_put(struct inet6_dev *idev)
{
if (atomic_dec_and_test(&idev->refcnt))
if (refcount_dec_and_test(&idev->refcnt))
in6_dev_finish_destroy(idev);
}

static inline void __in6_dev_put(struct inet6_dev *idev)
{
atomic_dec(&idev->refcnt);
refcount_dec(&idev->refcnt);
}

static inline void in6_dev_hold(struct inet6_dev *idev)
{
atomic_inc(&idev->refcnt);
refcount_inc(&idev->refcnt);
}

void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp);

static inline void in6_ifa_put(struct inet6_ifaddr *ifp)
{
if (atomic_dec_and_test(&ifp->refcnt))
if (refcount_dec_and_test(&ifp->refcnt))
inet6_ifa_finish_destroy(ifp);
}

static inline void __in6_ifa_put(struct inet6_ifaddr *ifp)
{
atomic_dec(&ifp->refcnt);
refcount_dec(&ifp->refcnt);
}

static inline void in6_ifa_hold(struct inet6_ifaddr *ifp)
{
atomic_inc(&ifp->refcnt);
refcount_inc(&ifp->refcnt);
}


Expand Down
3 changes: 2 additions & 1 deletion include/net/cipso_ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <net/netlabel.h>
#include <net/request_sock.h>
#include <linux/atomic.h>
#include <linux/refcount.h>
#include <asm/unaligned.h>

/* known doi values */
Expand Down Expand Up @@ -85,7 +86,7 @@ struct cipso_v4_doi {
} map;
u8 tags[CIPSO_V4_TAG_MAXCNT];

atomic_t refcount;
refcount_t refcount;
struct list_head list;
struct rcu_head rcu;
};
Expand Down
9 changes: 5 additions & 4 deletions include/net/if_inet6.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <net/snmp.h>
#include <linux/ipv6.h>
#include <linux/refcount.h>

/* inet6_dev.if_flags */

Expand Down Expand Up @@ -45,7 +46,7 @@ struct inet6_ifaddr {
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
__u32 valid_lft;
__u32 prefered_lft;
atomic_t refcnt;
refcount_t refcnt;
spinlock_t lock;

int state;
Expand Down Expand Up @@ -126,7 +127,7 @@ struct ifmcaddr6 {
struct timer_list mca_timer;
unsigned int mca_flags;
int mca_users;
atomic_t mca_refcnt;
refcount_t mca_refcnt;
spinlock_t mca_lock;
unsigned long mca_cstamp;
unsigned long mca_tstamp;
Expand All @@ -146,7 +147,7 @@ struct ifacaddr6 {
struct rt6_info *aca_rt;
struct ifacaddr6 *aca_next;
int aca_users;
atomic_t aca_refcnt;
refcount_t aca_refcnt;
unsigned long aca_cstamp;
unsigned long aca_tstamp;
};
Expand Down Expand Up @@ -187,7 +188,7 @@ struct inet6_dev {

struct ifacaddr6 *ac_list;
rwlock_t lock;
atomic_t refcnt;
refcount_t refcnt;
__u32 if_flags;
int dead;

Expand Down
7 changes: 4 additions & 3 deletions include/net/ip_fib.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <net/inetpeer.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
#include <linux/refcount.h>

struct fib_config {
u8 fc_dst_len;
Expand Down Expand Up @@ -105,7 +106,7 @@ struct fib_info {
struct hlist_node fib_lhash;
struct net *fib_net;
int fib_treeref;
atomic_t fib_clntref;
refcount_t fib_clntref;
unsigned int fib_flags;
unsigned char fib_dead;
unsigned char fib_protocol;
Expand Down Expand Up @@ -430,12 +431,12 @@ void free_fib_info(struct fib_info *fi);

static inline void fib_info_hold(struct fib_info *fi)
{
atomic_inc(&fi->fib_clntref);
refcount_inc(&fi->fib_clntref);
}

static inline void fib_info_put(struct fib_info *fi)
{
if (atomic_dec_and_test(&fi->fib_clntref))
if (refcount_dec_and_test(&fi->fib_clntref))
free_fib_info(fi);
}

Expand Down
7 changes: 4 additions & 3 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/ipv6.h>
#include <linux/hardirq.h>
#include <linux/jhash.h>
#include <linux/refcount.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
#include <net/flow.h>
Expand Down Expand Up @@ -203,7 +204,7 @@ extern rwlock_t ip6_ra_lock;
*/

struct ipv6_txoptions {
atomic_t refcnt;
refcount_t refcnt;
/* Length of this structure */
int tot_len;

Expand Down Expand Up @@ -265,7 +266,7 @@ static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np)
rcu_read_lock();
opt = rcu_dereference(np->opt);
if (opt) {
if (!atomic_inc_not_zero(&opt->refcnt))
if (!refcount_inc_not_zero(&opt->refcnt))
opt = NULL;
else
opt = rcu_pointer_handoff(opt);
Expand All @@ -276,7 +277,7 @@ static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np)

static inline void txopt_put(struct ipv6_txoptions *opt)
{
if (opt && atomic_dec_and_test(&opt->refcnt))
if (opt && refcount_dec_and_test(&opt->refcnt))
kfree_rcu(opt, rcu);
}

Expand Down
12 changes: 6 additions & 6 deletions net/ipv4/cipso_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
struct cipso_v4_doi *iter;

list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
if (iter->doi == doi && atomic_read(&iter->refcount))
if (iter->doi == doi && refcount_read(&iter->refcount))
return iter;
return NULL;
}
Expand Down Expand Up @@ -429,7 +429,7 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def,
}
}

atomic_set(&doi_def->refcount, 1);
refcount_set(&doi_def->refcount, 1);

spin_lock(&cipso_v4_doi_list_lock);
if (cipso_v4_doi_search(doi_def->doi)) {
Expand Down Expand Up @@ -533,7 +533,7 @@ int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
ret_val = -ENOENT;
goto doi_remove_return;
}
if (!atomic_dec_and_test(&doi_def->refcount)) {
if (!refcount_dec_and_test(&doi_def->refcount)) {
spin_unlock(&cipso_v4_doi_list_lock);
ret_val = -EBUSY;
goto doi_remove_return;
Expand Down Expand Up @@ -576,7 +576,7 @@ struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
doi_def = cipso_v4_doi_search(doi);
if (!doi_def)
goto doi_getdef_return;
if (!atomic_inc_not_zero(&doi_def->refcount))
if (!refcount_inc_not_zero(&doi_def->refcount))
doi_def = NULL;

doi_getdef_return:
Expand All @@ -597,7 +597,7 @@ void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
if (!doi_def)
return;

if (!atomic_dec_and_test(&doi_def->refcount))
if (!refcount_dec_and_test(&doi_def->refcount))
return;
spin_lock(&cipso_v4_doi_list_lock);
list_del_rcu(&doi_def->list);
Expand Down Expand Up @@ -630,7 +630,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt,

rcu_read_lock();
list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
if (atomic_read(&iter_doi->refcount) > 0) {
if (refcount_read(&iter_doi->refcount) > 0) {
if (doi_cnt++ < *skip_cnt)
continue;
ret_val = callback(iter_doi, cb_arg);
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/fib_semantics.c
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
}

fi->fib_treeref++;
atomic_inc(&fi->fib_clntref);
refcount_set(&fi->fib_clntref, 1);
spin_lock_bh(&fib_info_lock);
hlist_add_head(&fi->fib_hash,
&fib_info_hash[fib_info_hashfn(fi)]);
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/fib_trie.c
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
}

if (!(fib_flags & FIB_LOOKUP_NOREF))
atomic_inc(&fi->fib_clntref);
refcount_inc(&fi->fib_clntref);

res->prefix = htonl(n->key);
res->prefixlen = KEYLENGTH - fa->fa_slen;
Expand Down
4 changes: 2 additions & 2 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
}

/* One reference from device. */
in6_dev_hold(ndev);
refcount_set(&ndev->refcnt, 1);

if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
ndev->cnf.accept_dad = -1;
Expand Down Expand Up @@ -1050,7 +1050,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,

ifa->idev = idev;
/* For caller */
in6_ifa_hold(ifa);
refcount_set(&ifa->refcnt, 1);

/* Add to big hash table */
hash = inet6_addr_hash(addr);
Expand Down
9 changes: 5 additions & 4 deletions net/ipv6/addrlabel.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/if_addrlabel.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/refcount.h>

#if 0
#define ADDRLABEL(x...) printk(x)
Expand All @@ -36,7 +37,7 @@ struct ip6addrlbl_entry {
int addrtype;
u32 label;
struct hlist_node list;
atomic_t refcnt;
refcount_t refcnt;
struct rcu_head rcu;
};

Expand Down Expand Up @@ -137,12 +138,12 @@ static void ip6addrlbl_free_rcu(struct rcu_head *h)

static bool ip6addrlbl_hold(struct ip6addrlbl_entry *p)
{
return atomic_inc_not_zero(&p->refcnt);
return refcount_inc_not_zero(&p->refcnt);
}

static inline void ip6addrlbl_put(struct ip6addrlbl_entry *p)
{
if (atomic_dec_and_test(&p->refcnt))
if (refcount_dec_and_test(&p->refcnt))
call_rcu(&p->rcu, ip6addrlbl_free_rcu);
}

Expand Down Expand Up @@ -236,7 +237,7 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net,
newp->label = label;
INIT_HLIST_NODE(&newp->list);
write_pnet(&newp->lbl_net, net);
atomic_set(&newp->refcnt, 1);
refcount_set(&newp->refcnt, 1);
return newp;
}

Expand Down
6 changes: 3 additions & 3 deletions net/ipv6/anycast.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,12 @@ void ipv6_sock_ac_close(struct sock *sk)

static void aca_get(struct ifacaddr6 *aca)
{
atomic_inc(&aca->aca_refcnt);
refcount_inc(&aca->aca_refcnt);
}

static void aca_put(struct ifacaddr6 *ac)
{
if (atomic_dec_and_test(&ac->aca_refcnt)) {
if (refcount_dec_and_test(&ac->aca_refcnt)) {
in6_dev_put(ac->aca_idev);
dst_release(&ac->aca_rt->dst);
kfree(ac);
Expand All @@ -232,7 +232,7 @@ static struct ifacaddr6 *aca_alloc(struct rt6_info *rt,
aca->aca_users = 1;
/* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies;
atomic_set(&aca->aca_refcnt, 1);
refcount_set(&aca->aca_refcnt, 1);

return aca;
}
Expand Down
4 changes: 2 additions & 2 deletions net/ipv6/exthdrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
*((char **)&opt2->dst1opt) += dif;
if (opt2->srcrt)
*((char **)&opt2->srcrt) += dif;
atomic_set(&opt2->refcnt, 1);
refcount_set(&opt2->refcnt, 1);
}
return opt2;
}
Expand Down Expand Up @@ -1056,7 +1056,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
return ERR_PTR(-ENOBUFS);

memset(opt2, 0, tot_len);
atomic_set(&opt2->refcnt, 1);
refcount_set(&opt2->refcnt, 1);
opt2->tot_len = tot_len;
p = (char *)(opt2 + 1);

Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/ipv6_sockglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
break;

memset(opt, 0, sizeof(*opt));
atomic_set(&opt->refcnt, 1);
refcount_set(&opt->refcnt, 1);
opt->tot_len = sizeof(*opt) + optlen;
retv = -EFAULT;
if (copy_from_user(opt+1, optval, optlen))
Expand Down
Loading

0 comments on commit 2671e9f

Please sign in to comment.