diff --git a/pkg/tcpip/nftables/nft_payload_set.go b/pkg/tcpip/nftables/nft_payload_set.go index e7b90892b1..ae169e4598 100644 --- a/pkg/tcpip/nftables/nft_payload_set.go +++ b/pkg/tcpip/nftables/nft_payload_set.go @@ -157,6 +157,13 @@ func (op payloadSet) evaluate(regs *registerSet, evalCtx opEvalCtx) { // Updates the checksum of the header specified by the payload base. if op.csumType == linux.NFT_PAYLOAD_CSUM_INET { + // Equivalent to Linux skb_copy_bits returning -1 when the + // checksum field is outside the packet; matches the data + // bounds check at line 105. + if int(op.csumOffset)+2 > len(payload) { + regs.verdict = stack.NFVerdict{Code: VC(linux.NFT_BREAK)} + return + } // Reads the old checksum from the packet payload. oldTotalCsum := binary.BigEndian.Uint16(payload[op.csumOffset:]) diff --git a/pkg/tcpip/nftables/nftables_test.go b/pkg/tcpip/nftables/nftables_test.go index bfc9c5483a..44c575b747 100644 --- a/pkg/tcpip/nftables/nftables_test.go +++ b/pkg/tcpip/nftables/nftables_test.go @@ -2087,6 +2087,20 @@ func TestEvaluatePayloadSet(t *testing.T) { op1: mustCreateImmediate(t, linux.NFT_REG_3, arbitraryIPv6AddrB[:], stack.NFVerdict{}), op2: mustCreatePayloadSet(t, linux.NFT_PAYLOAD_NETWORK_HEADER, ipv6DstAddrOffset, ipv6DstAddrLen, linux.NFT_REG_3, linux.NFT_PAYLOAD_CSUM_NONE, 0, linux.NFT_PAYLOAD_L4CSUM_PSEUDOHDR), }, + { + tname: "csumOffset equal to network header length triggers NFT_BREAK", + pkt: makeIPv4Packet(header.IPv4MinimumSize, arbitraryIPv4Fields()), + outPkt: nil, + op1: mustCreateImmediate(t, linux.NFT_REG_1, numToBE(0, ipv4LengthLen), stack.NFVerdict{}), + op2: mustCreatePayloadSet(t, linux.NFT_PAYLOAD_NETWORK_HEADER, ipv4LengthOffset, ipv4LengthLen, linux.NFT_REG_1, linux.NFT_PAYLOAD_CSUM_INET, header.IPv4MinimumSize, 0x0), + }, + { + tname: "csumOffset past network header length triggers NFT_BREAK", + pkt: makeIPv4Packet(header.IPv4MinimumSize, arbitraryIPv4Fields()), + outPkt: nil, + op1: mustCreateImmediate(t, linux.NFT_REG_1, numToBE(0, ipv4LengthLen), stack.NFVerdict{}), + op2: mustCreatePayloadSet(t, linux.NFT_PAYLOAD_NETWORK_HEADER, ipv4LengthOffset, ipv4LengthLen, linux.NFT_REG_1, linux.NFT_PAYLOAD_CSUM_INET, 250, 0x0), + }, } { t.Run(test.tname, func(t *testing.T) { // Sets up an NFTables object with a single table, chain, and rule.