mirror of
https://github.com/soypat/lneto.git
synced 2026-08-11 02:13:44 +00:00
add IP/TCP/HTTP fuzzing and fixes to panics found
This commit is contained in:
@@ -21,6 +21,8 @@ Userspace networking primitives.
|
||||
- Can produce **very** small binaries. Ideal for embedded systems.
|
||||
- Extremely simple networking stack construction. Can be used to teach basics of networking
|
||||
- Only one networking interface fulfilled by all implementations. See [abstractions](#abstractions).
|
||||
- Stack can be fuzz tested very fast:
|
||||
- `go test ./x/xnet/ -run FuzzStackAsyncHTTP -fuzz=.` fuzzes Ethernet/IP/TCP/HTTP stack with 170k HTTP exchanges per second on 12 core machine: `fuzz: elapsed: 4m9s, execs: 42649941 (169428/sec), new interesting: 5 (total: 52)`
|
||||
|
||||
## `xcurl` example
|
||||
You may try lneto out on linux with the [xcurl example](./examples/xcurl/) which gets an HTTP page by doing all the low-level networking part using absolutely no standard library.
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ func (ifrm Frame) ValidateSize(v *lneto.Validator) {
|
||||
if int(tl) > len(ifrm.RawData()) {
|
||||
v.AddError(errShort)
|
||||
}
|
||||
if ihl < 5 {
|
||||
if ihl < 5 || uint16(ihl)*4 > tl {
|
||||
v.AddError(errBadIHL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
int(0)
|
||||
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 00000\x11000000\n\x00\x00\xff000000000000")
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
int(3)
|
||||
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00H0\x00\x1a00000\x06000000\n\x00\x00\xff000000")
|
||||
@@ -0,0 +1,131 @@
|
||||
package xnet
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/http/httpraw"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
func FuzzStackAsyncHTTP(f *testing.F) {
|
||||
const MTU = 1500
|
||||
const seed = 1
|
||||
var buf [MTU + ethernet.MaxOverheadSize]byte
|
||||
s1, s2, c1, c2 := newTCPStacks(f, seed, MTU)
|
||||
var hdr httpraw.Header
|
||||
err := s1.ListenTCP(c1, 80)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort()))
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
hdr.SetMethod("GET")
|
||||
hdr.SetProtocol("HTTP/1.1")
|
||||
hdr.SetRequestURI("/")
|
||||
data := hdr.AppendHeaders(nil)
|
||||
|
||||
pktnum := 0
|
||||
written := false
|
||||
closed := false
|
||||
for {
|
||||
n1, err := s1.Encapsulate(buf[:], -1, 0)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
if n1 > 0 {
|
||||
err = s2.Demux(buf[:n1], 0)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
f.Add(pktnum, buf[:n1])
|
||||
pktnum++
|
||||
if !written && c2.State() >= tcp.StateEstablished {
|
||||
_, err = c2.Write(data)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
written = true
|
||||
}
|
||||
}
|
||||
n2, err := s2.Encapsulate(buf[:], -1, 0)
|
||||
if n2 > 0 {
|
||||
pktnum++
|
||||
err = s1.Demux(buf[:n2], 0)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
f.Add(pktnum, buf[:n2])
|
||||
}
|
||||
if n1 == 0 && n2 == 0 {
|
||||
if !closed {
|
||||
c2.Close()
|
||||
closed = true
|
||||
continue
|
||||
}
|
||||
break // No more data to send
|
||||
}
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, pktnum int, a []byte) {
|
||||
var buf [MTU + ethernet.MaxOverheadSize]byte
|
||||
s1, s2, c1, c2 := newTCPStacks(t, seed, MTU)
|
||||
err := s1.ListenTCP(c1, 80)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pkt := 0
|
||||
written := false
|
||||
closed := false
|
||||
const maxpkts = 100
|
||||
for {
|
||||
n1, err := s1.Encapsulate(buf[:], -1, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n1 > 0 {
|
||||
if pkt == pktnum {
|
||||
n1 = copy(buf[:], a)
|
||||
}
|
||||
s2.Demux(buf[:n1], 0)
|
||||
pkt++
|
||||
if !written && c2.State() >= tcp.StateEstablished {
|
||||
c2.Write(data)
|
||||
written = true
|
||||
}
|
||||
}
|
||||
n2, err := s2.Encapsulate(buf[:], -1, 0)
|
||||
if n2 > 0 {
|
||||
if pkt == pktnum {
|
||||
n2 = copy(buf[:], a)
|
||||
}
|
||||
pkt++
|
||||
s1.Demux(buf[:n2], 0)
|
||||
}
|
||||
if n1 == 0 && n2 == 0 {
|
||||
if !closed {
|
||||
if c1.BufferedInput() > 0 {
|
||||
var hdr httpraw.Header
|
||||
n, _ := c1.Read(buf[:])
|
||||
hdr.ReadFromBytes(buf[:n])
|
||||
hdr.TryParse(false)
|
||||
}
|
||||
c2.Close()
|
||||
closed = true
|
||||
continue
|
||||
}
|
||||
break // No more data to send
|
||||
}
|
||||
if pkt > maxpkts {
|
||||
panic("infinite retransmission loop")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -166,7 +166,7 @@ func TestStackAsyncTCP_singlepacket(t *testing.T) {
|
||||
tst.TestTCPClose(client, sv, clconn, svconn)
|
||||
}
|
||||
|
||||
func newTCPStacks(t *testing.T, randSeed int64, mtu int) (s1, s2 *StackAsync, c1, c2 *tcp.Conn) {
|
||||
func newTCPStacks(t testing.TB, randSeed int64, mtu int) (s1, s2 *StackAsync, c1, c2 *tcp.Conn) {
|
||||
s1, s2 = new(StackAsync), new(StackAsync)
|
||||
c1, c2 = new(tcp.Conn), new(tcp.Conn)
|
||||
byte1 := byte(randSeed)/4 - 1
|
||||
|
||||
Reference in New Issue
Block a user