CVE-2017-17712: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: The raw_sendmsg() function in net/ipv4/raw.c in the Linux kernel through 4.14.6 has a race condition in inet->hdrincl that leads to uninitialized stack pointer usage; this allows a local user to execute code and gain privileges.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 7
Published: 2017-12-16T01:29Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: net/ipv4/raw.c
Commit date: 2017-12-11 14:05:31 -0500
Commit message: net: ipv4: fix for a race condition in raw_sendmsg

inet->hdrincl is racy, and could lead to uninitialized stack pointer
usage, so its value should be read only once.

Fixes: c008ba5bdc9f ("ipv4: Avoid reading user iov twice after raw_probe_proto_opt")
Signed-off-by: Mohamed Ghannam <simo.ghannam@gmail.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 33b70bfd1122f0..125c1eab3eaa6d 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -511,15 +511,20 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 	__be32 saddr;
 	u8  tos;
 	int err;
 	struct ip_options_data opt_copy;
 	struct raw_frag_vec rfv;
+	int hdrincl;
 
 	err = -EMSGSIZE;
 	if (len > 0xFFFF)
 		goto out;
 
+	/* hdrincl should be READ_ONCE(inet->hdrincl)
+	 * but READ_ONCE() doesn't work with bit fields
+	 */
+	hdrincl = inet->hdrincl;
 	/*
 	 *	Check the flags.
 	 */
 
 	err = -EOPNOTSUPP;
@@ -591,11 +596,11 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 	if (ipc.opt) {
 		err = -EINVAL;
 		/* Linux does not mangle headers on raw sockets,
 		 * so that IP options + IP_HDRINCL is non-sense.
 		 */
-		if (inet->hdrincl)
+		if (hdrincl)
 			goto done;
 		if (ipc.opt->opt.srr) {
 			if (!daddr)
 				goto done;
 			daddr = ipc.opt->opt.faddr;
@@ -613,16 +618,16 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 	} else if (!ipc.oif)
 		ipc.oif = inet->uc_index;
 
 	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
 			   RT_SCOPE_UNIVERSE,
-			   inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
+			   hdrincl ? IPPROTO_RAW : sk->sk_protocol,
 			   inet_sk_flowi_flags(sk) |
-			    (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
+			    (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
 			   daddr, saddr, 0, 0, sk->sk_uid);
 
-	if (!inet->hdrincl) {
+	if (!hdrincl) {
 		rfv.msg = msg;
 		rfv.hlen = 0;
 
 		err = raw_probe_proto_opt(&rfv, &fl4);
 		if (err)
@@ -643,11 +648,11 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
 	if (msg->msg_flags & MSG_CONFIRM)
 		goto do_confirm;
 back_from_confirm:
 
-	if (inet->hdrincl)
+	if (hdrincl)
 		err = raw_send_hdrinc(sk, &fl4, msg, len,
 				      &rt, msg->msg_flags, &ipc.sockc);
 
 	 else {
 		sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);

Vulnerable code: 		if (inet->hdrincl)
			   inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
			    (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
	if (!inet->hdrincl) {
	if (inet->hdrincl)
Fixed code: 	int hdrincl;
	/* hdrincl should be READ_ONCE(inet->hdrincl)
	 * but READ_ONCE() doesn't work with bit fields
	 */
	hdrincl = inet->hdrincl;
		if (hdrincl)
			   hdrincl ? IPPROTO_RAW : sk->sk_protocol,
			    (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
	if (!hdrincl) {
	if (hdrincl)