Commit e73d5fb7 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ipv6-data-races'



Eric Dumazet says:

====================
ipv6: round of data-races fixes

This series is inspired by one related syzbot report.

Many inet6_sk(sk) fields reads or writes are racy.

Move 1-bit fields to inet->inet_flags to provide
atomic safety. inet6_{test|set|clear|assign}_bit() helpers
could be changed later if we need to make room in inet_flags.

Also add missing READ_ONCE()/WRITE_ONCE() when
lockless readers need access to specific fields.

np->srcprefs will be handled separately to avoid merge conflicts
because a prior patch was posted for net tree.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f2fa1c81 859f8b26
Loading
Loading
Loading
Loading
+16 −33
Original line number Diff line number Diff line
@@ -213,28 +213,9 @@ struct ipv6_pinfo {
	__be32			flow_label;
	__u32			frag_size;

	/*
	 * Packed in 16bits.
	 * Omit one shift by putting the signed field at MSB.
	 */
#if defined(__BIG_ENDIAN_BITFIELD)
	__s16			hop_limit:9;
	__u16			__unused_1:7;
#else
	__u16			__unused_1:7;
	__s16			hop_limit:9;
#endif
	s16			hop_limit;
	u8			mcast_hops;

#if defined(__BIG_ENDIAN_BITFIELD)
	/* Packed in 16bits. */
	__s16			mcast_hops:9;
	__u16			__unused_2:6,
				mc_loop:1;
#else
	__u16			mc_loop:1,
				__unused_2:6;
	__s16			mcast_hops:9;
#endif
	int			ucast_oif;
	int			mcast_oif;

@@ -262,21 +243,11 @@ struct ipv6_pinfo {
	} rxopt;

	/* sockopt flags */
	__u16			recverr:1,
	                        sndflow:1,
				repflow:1,
				pmtudisc:3,
				padding:1,	/* 1 bit hole */
				srcprefs:3,	/* 001: prefer temporary address
	__u8			srcprefs:3;	/* 001: prefer temporary address
						 * 010: prefer public address
						 * 100: prefer care-of address
						 */
				dontfrag:1,
				autoflowlabel:1,
				autoflowlabel_set:1,
				mc_all:1,
				recverr_rfc4884:1,
				rtalert_isolate:1;
	__u8			pmtudisc;
	__u8			min_hopcount;
	__u8			tclass;
	__be32			rcv_flowinfo;
@@ -293,6 +264,18 @@ struct ipv6_pinfo {
	struct inet6_cork	cork;
};

/* We currently use available bits from inet_sk(sk)->inet_flags,
 * this could change in the future.
 */
#define inet6_test_bit(nr, sk)			\
	test_bit(INET_FLAGS_##nr, &inet_sk(sk)->inet_flags)
#define inet6_set_bit(nr, sk)			\
	set_bit(INET_FLAGS_##nr, &inet_sk(sk)->inet_flags)
#define inet6_clear_bit(nr, sk)			\
	clear_bit(INET_FLAGS_##nr, &inet_sk(sk)->inet_flags)
#define inet6_assign_bit(nr, sk, val)		\
	assign_bit(INET_FLAGS_##nr, &inet_sk(sk)->inet_flags, val)

/* WARNING: don't change the layout of the members in {raw,udp,tcp}6_sock! */
struct raw6_sock {
	/* inet_sock has to be the first member of raw6_sock */
+10 −0
Original line number Diff line number Diff line
@@ -268,6 +268,16 @@ enum {
	INET_FLAGS_NODEFRAG	= 17,
	INET_FLAGS_BIND_ADDRESS_NO_PORT = 18,
	INET_FLAGS_DEFER_CONNECT = 19,
	INET_FLAGS_MC6_LOOP	= 20,
	INET_FLAGS_RECVERR6_RFC4884 = 21,
	INET_FLAGS_MC6_ALL	= 22,
	INET_FLAGS_AUTOFLOWLABEL_SET = 23,
	INET_FLAGS_AUTOFLOWLABEL = 24,
	INET_FLAGS_DONTFRAG	= 25,
	INET_FLAGS_RECVERR6	= 26,
	INET_FLAGS_REPFLOW	= 27,
	INET_FLAGS_RTALERT_ISOLATE = 28,
	INET_FLAGS_SNDFLOW	= 29,
};

/* cmsg flags for inet */
+9 −5
Original line number Diff line number Diff line
@@ -266,7 +266,7 @@ static inline unsigned int ip6_skb_dst_mtu(const struct sk_buff *skb)
	const struct dst_entry *dst = skb_dst(skb);
	unsigned int mtu;

	if (np && np->pmtudisc >= IPV6_PMTUDISC_PROBE) {
	if (np && READ_ONCE(np->pmtudisc) >= IPV6_PMTUDISC_PROBE) {
		mtu = READ_ONCE(dst->dev->mtu);
		mtu -= lwtunnel_headroom(dst->lwtstate, mtu);
	} else {
@@ -277,14 +277,18 @@ static inline unsigned int ip6_skb_dst_mtu(const struct sk_buff *skb)

static inline bool ip6_sk_accept_pmtu(const struct sock *sk)
{
	return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE &&
	       inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT;
	u8 pmtudisc = READ_ONCE(inet6_sk(sk)->pmtudisc);

	return pmtudisc != IPV6_PMTUDISC_INTERFACE &&
	       pmtudisc != IPV6_PMTUDISC_OMIT;
}

static inline bool ip6_sk_ignore_df(const struct sock *sk)
{
	return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO ||
	       inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT;
	u8 pmtudisc = READ_ONCE(inet6_sk(sk)->pmtudisc);

	return pmtudisc < IPV6_PMTUDISC_DO ||
	       pmtudisc == IPV6_PMTUDISC_OMIT;
}

static inline const struct in6_addr *rt6_nexthop(const struct rt6_info *rt,
+7 −9
Original line number Diff line number Diff line
@@ -373,12 +373,12 @@ static inline void ipcm6_init(struct ipcm6_cookie *ipc6)
}

static inline void ipcm6_init_sk(struct ipcm6_cookie *ipc6,
				 const struct ipv6_pinfo *np)
				 const struct sock *sk)
{
	*ipc6 = (struct ipcm6_cookie) {
		.hlimit = -1,
		.tclass = np->tclass,
		.dontfrag = np->dontfrag,
		.tclass = inet6_sk(sk)->tclass,
		.dontfrag = inet6_test_bit(DONTFRAG, sk),
	};
}

@@ -428,7 +428,7 @@ int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq,
			   int flags);
int ip6_flowlabel_init(void);
void ip6_flowlabel_cleanup(void);
bool ip6_autoflowlabel(struct net *net, const struct ipv6_pinfo *np);
bool ip6_autoflowlabel(struct net *net, const struct sock *sk);

static inline void fl6_sock_release(struct ip6_flowlabel *fl)
{
@@ -914,9 +914,9 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
	int hlimit;

	if (ipv6_addr_is_multicast(&fl6->daddr))
		hlimit = np->mcast_hops;
		hlimit = READ_ONCE(np->mcast_hops);
	else
		hlimit = np->hop_limit;
		hlimit = READ_ONCE(np->hop_limit);
	if (hlimit < 0)
		hlimit = ip6_dst_hoplimit(dst);
	return hlimit;
@@ -1303,9 +1303,7 @@ static inline int ip6_sock_set_v6only(struct sock *sk)

static inline void ip6_sock_set_recverr(struct sock *sk)
{
	lock_sock(sk);
	inet6_sk(sk)->recverr = true;
	release_sock(sk);
	inet6_set_bit(RECVERR6, sk);
}

static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
+1 −1
Original line number Diff line number Diff line
@@ -2238,7 +2238,7 @@ static inline void sock_confirm_neigh(struct sk_buff *skb, struct neighbour *n)
	}
}

bool sk_mc_loop(struct sock *sk);
bool sk_mc_loop(const struct sock *sk);

static inline bool sk_can_gso(const struct sock *sk)
{
Loading