mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2026-05-20 21:06:30 +03:00
## Summary
- ipv6rwc: validate IPv6 packet length before reading the version nibble
in writePC
- config: guard the BOM check against configs shorter than two bytes
- admin: replace unchecked net.Error type assertion with errors.As;
tolerate empty unix socket paths
- multicast: log and continue on ReadFrom errors instead of panicking;
use checked type assertion on UDPAddr
- mobile: reject negative length in SendBuffer; nil-check AddrForKey in
GetPeersJSON and SummaryForConfig
- admin/get{tree,paths,sessions}: skip entries when AddrForKey returns
nil instead of dereferencing
- core/nodeinfo: validate the requested public key length in
nodeInfoAdminHandler, matching the other proto handlers
- add regression tests for the panic paths
## Why
A handful of error paths and platform-API edge cases reach fixed-size
indexing or unchecked type assertions before any length validation.
Most are reachable only locally (an empty config piped to -useconf,
a 0-byte packet from the mobile bindings, an admin DialTimeout error
that doesn't satisfy net.Error on some platforms), but they crash the
daemon hard. Have them return errors or skip the entry instead.
## Testing
- go test ./...
- go vet ./...
71 lines
2.1 KiB
Go
71 lines
2.1 KiB
Go
package ipv6rwc
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// writePC indexed bs[0] before checking len(bs), so an empty buffer
|
|
// reaching it (via the mobile Send/SendBuffer API or a zero-byte TUN
|
|
// read) panicked with index out of range. Reject empty input cleanly.
|
|
func TestWritePCRejectsEmptyPacket(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Fatalf("writePC must not panic on empty input, got: %v", r)
|
|
}
|
|
}()
|
|
var k keyStore
|
|
if _, err := k.writePC(nil); err == nil {
|
|
t.Fatalf("expected an error for nil input, got nil")
|
|
}
|
|
if _, err := k.writePC([]byte{}); err == nil {
|
|
t.Fatalf("expected an error for zero-length input, got nil")
|
|
}
|
|
}
|
|
|
|
// Buffers shorter than the minimum IPv6 header (40 bytes) but non-empty
|
|
// must still be rejected without panicking.
|
|
func TestWritePCRejectsTruncatedPacket(t *testing.T) {
|
|
var k keyStore
|
|
for _, tc := range []struct {
|
|
name string
|
|
buf []byte
|
|
}{
|
|
{name: "single byte ipv6 marker", buf: []byte{0x60}},
|
|
{name: "ten bytes ipv6 marker", buf: append([]byte{0x60}, make([]byte, 9)...)},
|
|
{name: "thirty-nine bytes ipv6 marker", buf: append([]byte{0x60}, make([]byte, 38)...)},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Fatalf("writePC must not panic on truncated input, got: %v", r)
|
|
}
|
|
}()
|
|
if _, err := k.writePC(tc.buf); err == nil {
|
|
t.Fatalf("expected an error for truncated input, got nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Non-IPv6 packets (e.g. IPv4) must report "not an IPv6 packet" rather
|
|
// than the length-based error, regardless of whether they meet the IPv6
|
|
// 40-byte minimum.
|
|
func TestWritePCReportsIPv4AsNonIPv6(t *testing.T) {
|
|
var k keyStore
|
|
for _, tc := range []struct {
|
|
name string
|
|
buf []byte
|
|
}{
|
|
{name: "ipv4 short", buf: []byte{0x45, 0, 0, 20}},
|
|
{name: "ipv4 padded to 40", buf: append([]byte{0x45, 0, 0, 60}, make([]byte, 36)...)},
|
|
{name: "ipv4 padded to 64", buf: append([]byte{0x45, 0, 0, 60}, make([]byte, 60)...)},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := k.writePC(tc.buf)
|
|
if err == nil || err.Error() != "not an IPv6 packet" {
|
|
t.Fatalf("expected \"not an IPv6 packet\" error, got: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|