diff mbox series

[v2] net: ipv4: emulate READ_ONCE() on ->hdrincl bit-field in raw_sendmsg()

Message ID 20180108145444.19457-1-nstange@suse.de
State Accepted, archived
Delegated to: David Miller
Headers show
Series [v2] net: ipv4: emulate READ_ONCE() on ->hdrincl bit-field in raw_sendmsg() | expand

Commit Message

Nicolai Stange Jan. 8, 2018, 2:54 p.m. UTC
Commit 8f659a03a0ba ("net: ipv4: fix for a race condition in
raw_sendmsg") fixed the issue of possibly inconsistent ->hdrincl handling
due to concurrent updates by reading this bit-field member into a local
variable and using the thus stabilized value in subsequent tests.

However, aforementioned commit also adds the (correct) comment that

  /* hdrincl should be READ_ONCE(inet->hdrincl)
   * but READ_ONCE() doesn't work with bit fields
   */

because as it stands, the compiler is free to shortcut or even eliminate
the local variable at its will.

Note that I have not seen anything like this happening in reality and thus,
the concern is a theoretical one.

However, in order to be on the safe side, emulate a READ_ONCE() on the
bit-field by doing it on the local 'hdrincl' variable itself:

	int hdrincl = inet->hdrincl;
	hdrincl = READ_ONCE(hdrincl);

This breaks the chain in the sense that the compiler is not allowed
to replace subsequent reads from hdrincl with reloads from inet->hdrincl.

Fixes: 8f659a03a0ba ("net: ipv4: fix for a race condition in raw_sendmsg")
Signed-off-by: Nicolai Stange <nstange@suse.de>
---
 Compile-tested only (with inspection of compiler output on x86_64).
 Note that there's still a ->hdrincl reload, but that's due to the
 inet_sk_flowi_flags() invocation and probably harmless.

 Applicable to linux-next-20180108.

Changes to v2:
 - Apply review from Stefano Brivio, i.e. drop the __hdrincl
   intermediate, update comment and commit message accordingly. 

 net/ipv4/raw.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Comments

Stefano Brivio Jan. 8, 2018, 3:11 p.m. UTC | #1
On Mon,  8 Jan 2018 15:54:44 +0100
Nicolai Stange <nstange@suse.de> wrote:

> Commit 8f659a03a0ba ("net: ipv4: fix for a race condition in
> raw_sendmsg") fixed the issue of possibly inconsistent ->hdrincl handling
> due to concurrent updates by reading this bit-field member into a local
> variable and using the thus stabilized value in subsequent tests.
> 
> However, aforementioned commit also adds the (correct) comment that
> 
>   /* hdrincl should be READ_ONCE(inet->hdrincl)
>    * but READ_ONCE() doesn't work with bit fields
>    */
> 
> because as it stands, the compiler is free to shortcut or even eliminate
> the local variable at its will.
> 
> Note that I have not seen anything like this happening in reality and thus,
> the concern is a theoretical one.
> 
> However, in order to be on the safe side, emulate a READ_ONCE() on the
> bit-field by doing it on the local 'hdrincl' variable itself:
> 
> 	int hdrincl = inet->hdrincl;
> 	hdrincl = READ_ONCE(hdrincl);
> 
> This breaks the chain in the sense that the compiler is not allowed
> to replace subsequent reads from hdrincl with reloads from inet->hdrincl.
> 
> Fixes: 8f659a03a0ba ("net: ipv4: fix for a race condition in raw_sendmsg")
> Signed-off-by: Nicolai Stange <nstange@suse.de>

Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
David Miller Jan. 9, 2018, 4:59 p.m. UTC | #2
From: Nicolai Stange <nstange@suse.de>
Date: Mon,  8 Jan 2018 15:54:44 +0100

> Commit 8f659a03a0ba ("net: ipv4: fix for a race condition in
> raw_sendmsg") fixed the issue of possibly inconsistent ->hdrincl handling
> due to concurrent updates by reading this bit-field member into a local
> variable and using the thus stabilized value in subsequent tests.
> 
> However, aforementioned commit also adds the (correct) comment that
> 
>   /* hdrincl should be READ_ONCE(inet->hdrincl)
>    * but READ_ONCE() doesn't work with bit fields
>    */
> 
> because as it stands, the compiler is free to shortcut or even eliminate
> the local variable at its will.
> 
> Note that I have not seen anything like this happening in reality and thus,
> the concern is a theoretical one.
> 
> However, in order to be on the safe side, emulate a READ_ONCE() on the
> bit-field by doing it on the local 'hdrincl' variable itself:
> 
> 	int hdrincl = inet->hdrincl;
> 	hdrincl = READ_ONCE(hdrincl);
> 
> This breaks the chain in the sense that the compiler is not allowed
> to replace subsequent reads from hdrincl with reloads from inet->hdrincl.
> 
> Fixes: 8f659a03a0ba ("net: ipv4: fix for a race condition in raw_sendmsg")
> Signed-off-by: Nicolai Stange <nstange@suse.de>

Applied, thank you.
diff mbox series

Patch

diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 5b9bd5c33d9d..6717cf2008f0 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -520,9 +520,11 @@  static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 		goto out;
 
 	/* hdrincl should be READ_ONCE(inet->hdrincl)
-	 * but READ_ONCE() doesn't work with bit fields
+	 * but READ_ONCE() doesn't work with bit fields.
+	 * Doing this indirectly yields the same result.
 	 */
 	hdrincl = inet->hdrincl;
+	hdrincl = READ_ONCE(hdrincl);
 	/*
 	 *	Check the flags.
 	 */