wireguard.lists.zx2c4.com archive mirror
 help / color / mirror / Atom feed
From: Laura Zelenku <laura.zelenku@wandera.com>
To: "Jason A. Donenfeld" <Jason@zx2c4.com>
Cc: WireGuard mailing list <wireguard@lists.zx2c4.com>
Subject: Re: Handshake state collision between parralel RoutineHandshake threads
Date: Tue, 23 Mar 2021 10:47:08 +0100	[thread overview]
Message-ID: <2F538518-C3E5-4418-910F-E8EBF7686C19@wandera.com> (raw)
In-Reply-To: <6B92ECA6-AEC9-42F2-AB98-013CBB70691C@wandera.com>

Or can we at least lower log level of "Failed to create response message” if it is not problem? The error is really disturbing and as you wrote one of the handshakes is processed.

> On 16. 3. 2021, at 15:03, Laura Zelenku <laura.zelenku@wandera.com> wrote:
> 
> Still struggling with this issue. Running RoutineHandshake in single instance will help. Remember there is shared resource “peer.handshake.state”. Even the resource is per peer there are two directions (upstream/downstream) that can fight for this resource and write it’s own value.
> 
> Index: device/receive.go
> IDEA additional info:
> Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
> <+>UTF-8
> ===================================================================
> --- device/receive.go	(revision 5f0c8b942d93be6ac36a156c0ba44c86c3698f91)
> +++ device/receive.go	(date 1615902577604)
> @@ -10,6 +10,7 @@
> 	"encoding/binary"
> 	"errors"
> 	"net"
> +	"runtime"
> 	"sync"
> 	"sync/atomic"
> 	"time"
> @@ -239,7 +240,9 @@
> func (device *Device) RoutineHandshake() {
> 	defer func() {
> 		device.log.Verbosef("Routine: handshake worker - stopped")
> -		device.queue.encryption.wg.Done()
> +		for i := 0; i < runtime.NumCPU(); i++ {
> +			device.queue.encryption.wg.Done()
> +		}
> 	}()
> 	device.log.Verbosef("Routine: handshake worker - started")
> 
> Index: device/device.go
> IDEA additional info:
> Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
> <+>UTF-8
> ===================================================================
> --- device/device.go	(revision 5f0c8b942d93be6ac36a156c0ba44c86c3698f91)
> +++ device/device.go	(date 1615902577566)
> @@ -305,12 +305,12 @@
> 
> 	cpus := runtime.NumCPU()
> 	device.state.stopping.Wait()
> -	device.queue.encryption.wg.Add(cpus) // One for each RoutineHandshake
> +	device.queue.encryption.wg.Add(cpus)
> 	for i := 0; i < cpus; i++ {
> 		go device.RoutineEncryption()
> 		go device.RoutineDecryption()
> -		go device.RoutineHandshake()
> -	}
> +	}
> +	go device.RoutineHandshake()
> 
> 	device.state.stopping.Add(1)      // RoutineReadFromTUN
> 	device.queue.encryption.wg.Add(1) // RoutineReadFromTUN
> 
> 
>> On 1. 3. 2021, at 15:08, Laura Zelenku <laura.zelenku@wandera.com> wrote:
>> 
>> Hi Jason,
>> I’ll try to explain the issue.
>> 
>> For incomming hanshake, the `handshake.state` is changing in the following way:
>> 1. set state handshakeInitiationConsumed
>> 2. check the state is handshakeInitiationConsumed otherwise "handshake initiation must be consumed first” error
>> 3. set state handshakeResponseCreated
>> 4. check the state is handshakeResponseCreated, otherwise "invalid state for keypair derivation” error
>> 5. set state handshakeZeroed
>> 
>> For outgoing handshake the `handshake.state` is changing:
>> 1. set state handshakeInitiationCreated
>> 2. <sending handshake and waiting for response>
>> 3. check the state is handshakeInitiationCreated, otherwise skip the packet
>> 4. set state handshakeResponseConsumed
>> 5. check the state is handshakeResponseConsumed, otherwise "invalid state for keypair derivation” error
>> 6. set state handshakeZeroed
>> 
>> Usually only “client” is sending handshake initiations and the “server” responding. But in case some delay (e.g. cause by some network issues mainly for mobile devices) the “server” can start sending handshake initiations (expiredNewHandshake or expiredRetransmitHandshake timers). In this time the client and server are sending hanshake initiations against each other. "go device.RoutineHandshake()” is running in multiple threads. `handshake.state` is defined per peer. Two threads (RoutineHandshake) can process both handshakes (incomming, outgoing) in the same time and these threads are working with shared resource, handshake.state. Because the routine is expecting state that was set before and the second thread can modify the state, the routine can fail on checking the expected handshake.state.
>> This is happening to us. We are getting error "handshake initiation must be consumed first”. handshakeInitiationConsumed is expected but handshakeZeroed is actually set (set by different thread). The error is logged on error level (Failed to create response message).
>> 
>> Hope this will help to understand the issue well.
>> 
>> Laura
>> 
>> 
>>> On 25 Feb 2021, at 12:23, Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>>> 
>>> Hi Laura,
>>> 
>>> I'm not sure this is actually a problem. The latest handshake message
>>> should probably win the race. I don't see state machine or data
>>> corruption here, but just one handshake interrupting another, which is
>>> par for the course with WireGuard.
>>> 
>>> Or have I overlooked something important in the state machine implementation?
>>> 
>>> Jason
>> 
> 


-- 
*IMPORTANT NOTICE*: This email, its attachments and any rights attaching 
hereto are confidential and intended exclusively for the person to whom the 
email is addressed. If you are not the intended recipient, do not read, 
copy, disclose or use the contents in any way. Wandera accepts no liability 
for any loss, damage or consequence resulting directly or indirectly from 
the use of this email and attachments.

      reply	other threads:[~2021-03-23 14:44 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-22 10:16 Handshake state collision between parralel RoutineHandshake threads Laura Zelenku
2021-01-08  6:56 ` Laura Zelenku
2021-02-25 11:23 ` Jason A. Donenfeld
2021-03-01 14:08   ` Laura Zelenku
2021-03-16 14:03     ` Laura Zelenku
2021-03-23  9:47       ` Laura Zelenku [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2F538518-C3E5-4418-910F-E8EBF7686C19@wandera.com \
    --to=laura.zelenku@wandera.com \
    --cc=Jason@zx2c4.com \
    --cc=wireguard@lists.zx2c4.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).