diff options
-rw-r--r-- | device/peer.go | 6 | ||||
-rw-r--r-- | device/peer_test.go | 43 |
2 files changed, 48 insertions, 1 deletions
diff --git a/device/peer.go b/device/peer.go index 8a8224c..19434cd 100644 --- a/device/peer.go +++ b/device/peer.go @@ -27,7 +27,11 @@ type Peer struct { 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 diff --git a/device/peer_test.go b/device/peer_test.go new file mode 100644 index 0000000..b389f1e --- /dev/null +++ b/device/peer_test.go @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. + */ + +package device + +import ( + "reflect" + "testing" + "unsafe" +) + +func checkAlignment(t *testing.T, name string, offset uintptr) { + t.Helper() + if offset%8 != 0 { + t.Errorf("offset of %q within struct is %d bytes, which does not align to 64-bit word boundaries (missing %d bytes). Atomic operations will crash on 32-bit systems.", name, offset, 8-(offset%8)) + } +} + +// TestPeerAlignment checks that atomically-accessed fields are +// aligned to 64-bit boundaries, as required by the atomic package. +// +// Unfortunately, violating this rule on 32-bit platforms results in a +// hard segfault at runtime. +func TestPeerAlignment(t *testing.T) { + var p Peer + + typ := reflect.TypeOf(p) + t.Logf("Peer type size: %d, with fields:", typ.Size()) + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)", + field.Name, + field.Offset, + field.Type.Size(), + field.Type.Align(), + ) + } + + checkAlignment(t, "Peer.stats", unsafe.Offsetof(p.stats)) + checkAlignment(t, "Peer.isRunning", unsafe.Offsetof(p.isRunning)) +} |