aboutsummaryrefslogtreecommitdiff
path: root/device/peer.go
diff options
context:
space:
mode:
authorDavid Anderson <danderson@tailscale.com>2020-03-01 00:39:24 -0800
committerDavid Crawshaw <david@zentus.com>2020-03-30 20:10:36 +1100
commitd49f4e9fe36f344ff1fa75523bdac18bdcb31945 (patch)
tree09d41b9c9f38123d9d162c810a2952170d7b41ec /device/peer.go
parent224bc9e60c7e2e1f04573bc377121c8f844d57c6 (diff)
downloadwireguard-go-d49f4e9fe36f344ff1fa75523bdac18bdcb31945.tar.gz
wireguard-go-d49f4e9fe36f344ff1fa75523bdac18bdcb31945.zip
device: make Peer fields safe for atomic access on 32-bit.
All atomic access must be aligned to 64 bits, even on 32-bit platforms. Go promises that the start of allocated structs is aligned to 64 bits. So, place the atomically-accessed things first in the struct so that they benefit from that alignment. As a side bonus, it cleanly separates fields that are accessed by atomic ops, and those that should be accessed under mu. Also adds a test that will fail consistently on 32-bit platforms if the struct ever changes again to violate the rules. This is likely not needed because unaligned access crashes reliably, but this will reliably fail even if tests accidentally pass due to lucky alignment. Signed-Off-By: David Anderson <danderson@tailscale.com>
Diffstat (limited to 'device/peer.go')
-rw-r--r--device/peer.go25
1 files changed, 16 insertions, 9 deletions
diff --git a/device/peer.go b/device/peer.go
index 8a8224c..65581d5 100644
--- a/device/peer.go
+++ b/device/peer.go
@@ -19,20 +19,27 @@ const (
)
type Peer struct {
- isRunning AtomicBool
- sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
- keypairs Keypairs
- handshake Handshake
- device *Device
- endpoint Endpoint
- persistentKeepaliveInterval uint16
-
- // This must be 64-bit aligned, so make sure the above members come out to even alignment and pad accordingly
+ // These fields are accessed with atomic operations, which must be
+ // 64-bit aligned even on 32-bit platforms. Go guarantees that an
+ // allocated struct will be 64-bit aligned. So we place
+ // atomically-accessed fields up front, so that they can share in
+ // this alignment before smaller fields throw it off.
stats struct {
txBytes uint64 // bytes send to peer (endpoint)
rxBytes uint64 // bytes received from peer
lastHandshakeNano int64 // nano seconds since epoch
}
+ // This field is only 32 bits wide, but is still aligned to 64
+ // bits. Don't place other atomic fields after this one.
+ isRunning AtomicBool
+
+ // Mostly protects endpoint, but is generally taken whenever we modify peer
+ sync.RWMutex
+ keypairs Keypairs
+ handshake Handshake
+ device *Device
+ endpoint Endpoint
+ persistentKeepaliveInterval uint16
timers struct {
retransmitHandshake *Timer