mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix bugs in fragment handling.
Strengthen the header.IPv4.IsValid check to correctly check for IHL/TotalLength fields. Also add a check to make sure fragmentOffsets + size of the fragment do not cause a wrap around for the end of the fragment. PiperOrigin-RevId: 274049313
This commit is contained in:
committed by
gVisor bot
parent
065339193e
commit
c7e901f47a
@@ -137,11 +137,13 @@ var Metrics = tcpip.Stats{
|
||||
},
|
||||
},
|
||||
IP: tcpip.IPStats{
|
||||
PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."),
|
||||
InvalidAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."),
|
||||
PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."),
|
||||
PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."),
|
||||
OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."),
|
||||
PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."),
|
||||
InvalidAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."),
|
||||
PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."),
|
||||
PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."),
|
||||
OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."),
|
||||
MalformedPacketsReceived: mustCreateMetric("/netstack/ip/malformed_packets_received", "Total number of IP packets which failed IP header validation checks."),
|
||||
MalformedFragmentsReceived: mustCreateMetric("/netstack/ip/malformed_fragments_received", "Total number of IP fragments which failed IP fragment validation checks."),
|
||||
},
|
||||
TCP: tcpip.TCPStats{
|
||||
ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."),
|
||||
|
||||
@@ -284,7 +284,7 @@ func (b IPv4) IsValid(pktSize int) bool {
|
||||
|
||||
hlen := int(b.HeaderLength())
|
||||
tlen := int(b.TotalLength())
|
||||
if hlen > tlen || tlen > pktSize {
|
||||
if hlen < IPv4MinimumSize || hlen > tlen || tlen > pktSize {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -296,6 +296,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
|
||||
headerView := vv.First()
|
||||
h := header.IPv4(headerView)
|
||||
if !h.IsValid(vv.Size()) {
|
||||
r.Stats().IP.MalformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -306,8 +307,23 @@ func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
|
||||
|
||||
more := (h.Flags() & header.IPv4FlagMoreFragments) != 0
|
||||
if more || h.FragmentOffset() != 0 {
|
||||
if vv.Size() == 0 {
|
||||
// Drop the packet as it's marked as a fragment but has
|
||||
// no payload.
|
||||
r.Stats().IP.MalformedPacketsReceived.Increment()
|
||||
r.Stats().IP.MalformedFragmentsReceived.Increment()
|
||||
return
|
||||
}
|
||||
// The packet is a fragment, let's try to reassemble it.
|
||||
last := h.FragmentOffset() + uint16(vv.Size()) - 1
|
||||
// Drop the packet if the fragmentOffset is incorrect. i.e the
|
||||
// combination of fragmentOffset and vv.size() causes a wrap
|
||||
// around resulting in last being less than the offset.
|
||||
if last < h.FragmentOffset() {
|
||||
r.Stats().IP.MalformedPacketsReceived.Increment()
|
||||
r.Stats().IP.MalformedFragmentsReceived.Increment()
|
||||
return
|
||||
}
|
||||
var ready bool
|
||||
vv, ready = e.fragmentation.Process(hash.IPv4FragmentHash(h), h.FragmentOffset(), last, more, vv)
|
||||
if !ready {
|
||||
|
||||
@@ -366,3 +366,107 @@ func TestFragmentationErrors(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidFragments(t *testing.T) {
|
||||
// These packets have both IHL and TotalLength set to 0.
|
||||
testCases := []struct {
|
||||
name string
|
||||
packets [][]byte
|
||||
wantMalformedIPPackets uint64
|
||||
wantMalformedFragments uint64
|
||||
}{
|
||||
{
|
||||
"ihl_totallen_zero_valid_frag_offset",
|
||||
[][]byte{
|
||||
{0x40, 0x30, 0x00, 0x00, 0x6c, 0x74, 0x7d, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x32, 0x39, 0x33, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
1,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"ihl_totallen_zero_invalid_frag_offset",
|
||||
[][]byte{
|
||||
{0x40, 0x30, 0x00, 0x00, 0x6c, 0x74, 0x20, 0x00, 0x30, 0x30, 0x30, 0x30, 0x39, 0x32, 0x39, 0x33, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
1,
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Total Length of 37(20 bytes IP header + 17 bytes of
|
||||
// payload)
|
||||
// Frag Offset of 0x1ffe = 8190*8 = 65520
|
||||
// Leading to the fragment end to be past 65535.
|
||||
"ihl_totallen_valid_invalid_frag_offset_1",
|
||||
[][]byte{
|
||||
{0x45, 0x30, 0x00, 0x25, 0x6c, 0x74, 0x1f, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x39, 0x32, 0x39, 0x33, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
1,
|
||||
1,
|
||||
},
|
||||
// The following 3 tests were found by running a fuzzer and were
|
||||
// triggering a panic in the IPv4 reassembler code.
|
||||
{
|
||||
"ihl_less_than_ipv4_minimum_size_1",
|
||||
[][]byte{
|
||||
{0x42, 0x30, 0x0, 0x30, 0x30, 0x40, 0x0, 0xf3, 0x30, 0x1, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
{0x42, 0x30, 0x0, 0x8, 0x30, 0x40, 0x20, 0x0, 0x30, 0x1, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
2,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"ihl_less_than_ipv4_minimum_size_2",
|
||||
[][]byte{
|
||||
{0x42, 0x30, 0x0, 0x30, 0x30, 0x40, 0xb3, 0x12, 0x30, 0x6, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
{0x42, 0x30, 0x0, 0x8, 0x30, 0x40, 0x20, 0x0, 0x30, 0x6, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
2,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"ihl_less_than_ipv4_minimum_size_3",
|
||||
[][]byte{
|
||||
{0x42, 0x30, 0x0, 0x30, 0x30, 0x40, 0xb3, 0x30, 0x30, 0x6, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
{0x42, 0x30, 0x0, 0x8, 0x30, 0x40, 0x20, 0x0, 0x30, 0x6, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
2,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"fragment_with_short_total_len_extra_payload",
|
||||
[][]byte{
|
||||
{0x46, 0x30, 0x00, 0x30, 0x30, 0x40, 0x0e, 0x12, 0x30, 0x06, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
{0x46, 0x30, 0x00, 0x18, 0x30, 0x40, 0x20, 0x00, 0x30, 0x06, 0x30, 0x30, 0x73, 0x73, 0x69, 0x6e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
|
||||
},
|
||||
1,
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
const nicid tcpip.NICID = 42
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{
|
||||
ipv4.NewProtocol(),
|
||||
},
|
||||
})
|
||||
|
||||
var linkAddr = tcpip.LinkAddress([]byte{0x30, 0x30, 0x30, 0x30, 0x30, 0x30})
|
||||
var remoteLinkAddr = tcpip.LinkAddress([]byte{0x30, 0x30, 0x30, 0x30, 0x30, 0x31})
|
||||
ep := channel.New(10, 1500, linkAddr)
|
||||
s.CreateNIC(nicid, sniffer.New(ep))
|
||||
|
||||
for _, pkt := range tc.packets {
|
||||
ep.InjectLinkAddr(header.IPv4ProtocolNumber, remoteLinkAddr, buffer.NewVectorisedView(len(pkt), []buffer.View{pkt}))
|
||||
}
|
||||
|
||||
if got, want := s.Stats().IP.MalformedPacketsReceived.Value(), tc.wantMalformedIPPackets; got != want {
|
||||
t.Errorf("incorrect Stats.IP.MalformedPacketsReceived, got: %d, want: %d", got, want)
|
||||
}
|
||||
if got, want := s.Stats().IP.MalformedFragmentsReceived.Value(), tc.wantMalformedFragments; got != want {
|
||||
t.Errorf("incorrect Stats.IP.MalformedFragmentsReceived, got: %d, want: %d", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,6 +853,14 @@ type IPStats struct {
|
||||
// OutgoingPacketErrors is the total number of IP packets which failed
|
||||
// to write to a link-layer endpoint.
|
||||
OutgoingPacketErrors *StatCounter
|
||||
|
||||
// MalformedPacketsReceived is the total number of IP Packets that were
|
||||
// dropped due to the IP packet header failing validation checks.
|
||||
MalformedPacketsReceived *StatCounter
|
||||
|
||||
// MalformedFragmentsReceived is the total number of IP Fragments that were
|
||||
// dropped due to the fragment failing validation checks.
|
||||
MalformedFragmentsReceived *StatCounter
|
||||
}
|
||||
|
||||
// TCPStats collects TCP-specific stats.
|
||||
|
||||
Reference in New Issue
Block a user