Make BytesData generic to support all multiples of 4 bytes (up to 16).

Removes Data- types and switches to Go's built-in type system.
Modifies tests to allow for creation and interpretation of 8- and 12-byte data.

Change became necessary when it was noticed that some nft binary operations
allowed for 8-byte data. Previously believed data was restricted to 4 or 16
bytes, but turned out the restriction is for data to be given in multiples of 4
bytes. This change corrects that.

PiperOrigin-RevId: 666542163
This commit is contained in:
Jayden Nyamiaka
2024-08-22 16:54:35 -07:00
committed by gVisor bot
parent 16b019b78b
commit 452ecde42e
4 changed files with 433 additions and 109 deletions
+36 -62
View File
@@ -650,13 +650,14 @@ func NewComparison(sreg uint8, op int, data RegisterData) (*Comparison, error) {
// evaluate for Comparison compares the data in the source register to the given
// data and breaks from the rule if the comparison is false.
func (op Comparison) evaluate(regs *RegisterSet, pkt *stack.PacketBuffer) {
// Gets the data from the source register.
regBuf := getRegisterData(regs, op.sreg, op.data.Type())
// Gets the data to compare to.
bytesData, ok := op.data.(BytesData)
if !ok {
panic("comparison operation data is not BytesData")
}
// Gets the data from the source register.
regBuf := bytesData.getRegisterBuffer(regs, op.sreg)
// Compares from left to right in 4-byte chunks starting with the rightmost
// byte of every 4-byte chunk since the data is little endian.
// For example, 16-byte IPv6 address 2001:000a:130f:0000:0000:09c0:876a:130b
@@ -715,24 +716,8 @@ func isRegister(reg uint8) bool {
return isVerdictRegister(reg) || is16ByteRegister(reg) || is4ByteRegister(reg)
}
// RegisterDataType is the type of data to be set in a register.
type RegisterDataType int
const (
// DataVerdict represents a verdict to be stored in a register.
DataVerdict RegisterDataType = iota
// Data4Bytes represents 4 bytes of data to be stored in a register.
Data4Bytes
// Data16Bytes represents 16 bytes of data to be stored in a register.
Data16Bytes
)
// RegisterData represents the data to be set in a register.
type RegisterData interface {
// Type returns the register data type.
Type() RegisterDataType
// String returns a string representation of the register data.
String() string
@@ -759,9 +744,6 @@ type VerdictData struct {
// NewVerdictData creates a RegisterData for a verdict.
func NewVerdictData(verdict Verdict) RegisterData { return VerdictData{data: verdict} }
// Type returns the DataVerdict register data type for VerdictData.
func (rd VerdictData) Type() RegisterDataType { return DataVerdict }
// String returns a string representation of the verdict data.
func (rd VerdictData) String() string {
return rd.data.String()
@@ -769,13 +751,20 @@ func (rd VerdictData) String() string {
// Equal compares the verdict data to another RegisterData object.
func (rd VerdictData) Equal(other RegisterData) bool {
return other != nil && other.Type() == DataVerdict && rd.data == other.(VerdictData).data
if other == nil {
return false
}
otherVD, ok := other.(VerdictData)
if !ok {
return false
}
return rd.data == otherVD.data
}
// ValidateRegister ensures the register is compatible with VerdictData.
func (rd VerdictData) ValidateRegister(reg uint8) error {
if !isVerdictRegister(reg) {
return fmt.Errorf("verdict data type is only valid for register 0")
return fmt.Errorf("verdict can only be stored in verdict register")
}
return nil
}
@@ -788,28 +777,19 @@ func (rd VerdictData) StoreData(regs *RegisterSet, reg uint8) {
regs.verdict = rd.data
}
// BytesData represents a 4 or 16 bytes of data to be stored in a register.
// BytesData represents data in 4-byte chunks to be stored in a register.
type BytesData struct {
data []byte
}
// NewBytesData creates a RegisterData for 4 or 16 bytes of data.
// NewBytesData creates a RegisterData for 4, 8, 12, or 16 bytes of data.
func NewBytesData(bytes []byte) RegisterData {
if len(bytes) != 4 && len(bytes) != 16 {
if len(bytes)%4 != 0 || len(bytes) > 16 {
panic(fmt.Errorf("invalid byte data length: %d", len(bytes)))
}
return BytesData{data: bytes}
}
// Type returns the Data4Bytes or Data16Bytes register data type depending
// on the length of the BytesData.
func (rd BytesData) Type() RegisterDataType {
if len(rd.data) == 4 {
return Data4Bytes
}
return Data16Bytes
}
// String returns a string representation of the bytes data.
func (rd BytesData) String() string {
return fmt.Sprintf("%x", rd.data)
@@ -820,54 +800,47 @@ func (rd BytesData) Equal(other RegisterData) bool {
if other == nil {
return false
}
if other.Type() != rd.Type() {
otherBD, ok := other.(BytesData)
if !ok {
return false
}
return slices.Equal(rd.data, other.(BytesData).data)
return slices.Equal(rd.data, otherBD.data)
}
// ValidateRegister ensures the register is compatible with Bytes4Data.
// ValidateRegister ensures the register is compatible with this bytes data.
func (rd BytesData) ValidateRegister(reg uint8) error {
if rd.Type() == Data4Bytes {
if !is4ByteRegister(reg) && !is16ByteRegister(reg) {
return fmt.Errorf("4-byte data type is only valid for 4-byte and 16-byte registers")
}
} else {
if !is16ByteRegister(reg) {
return fmt.Errorf("16-byte data type is only valid for 16-byte registers")
}
if isVerdictRegister(reg) {
return fmt.Errorf("data cannot be stored in verdict register")
}
if is4ByteRegister(reg) && len(rd.data) != 4 {
return fmt.Errorf("%d-byte data cannot be stored in 4-byte register", len(rd.data))
}
// 16-byte register can be used for any data (guaranteed to be <= 16 bytes)
return nil
}
// getRegisterData is a helper function that gets the appropriate slice of
// getRegisterBuffer is a helper function that gets the appropriate slice of
// register data from the register set.
// Note: does not support verdict data and assumes the register is valid for the
// given data type.
func getRegisterData(regs *RegisterSet, reg uint8, dataType RegisterDataType) []byte {
// 4-byte data in a 4-byte register.
func (rd BytesData) getRegisterBuffer(regs *RegisterSet, reg uint8) []byte {
// The entire 4-byte register (data must be exactly 4 bytes)
if is4ByteRegister(reg) {
start := (reg - linux.NFT_REG32_00) * linux.NFT_REG32_SIZE
return regs.data[start : start+linux.NFT_REG32_SIZE]
}
// 16-byte data in a 16-byte register.
if dataType == Data16Bytes {
start := (reg - linux.NFT_REG_1) * linux.NFT_REG_SIZE
return regs.data[start : start+linux.NFT_REG_SIZE]
}
// 4-byte data in a 16-byte register
// The appropriate (mod 4)-byte data in a 16-byte register
// Leaves excess space on the left (bc the data is little endian).
start := (reg-linux.NFT_REG_1)*linux.NFT_REG_SIZE + linux.NFT_REG_SIZE - linux.NFT_REG32_SIZE
return regs.data[start : start+linux.NFT_REG32_SIZE]
end := (int(reg)-linux.NFT_REG_1)*linux.NFT_REG_SIZE + linux.NFT_REG_SIZE
return regs.data[end-len(rd.data) : end]
}
// StoreData sets the data in the destination register to the uint32.
// StoreData sets the data in the destination register to the bytes data.
func (rd BytesData) StoreData(regs *RegisterSet, reg uint8) {
if err := rd.ValidateRegister(reg); err != nil {
panic(err)
}
regBuf := getRegisterData(regs, reg, rd.Type())
copy(regBuf, rd.data)
copy(rd.getRegisterBuffer(regs, reg), rd.data)
}
// RegisterSet represents the set of registers supported by the kernel.
@@ -1572,10 +1545,11 @@ func isJumpOrGotoOperation(op Operation) (bool, string) {
if !ok {
return false, ""
}
if imm.data.Type() != DataVerdict {
verdictData, ok := imm.data.(VerdictData)
if !ok {
return false, ""
}
verdict := imm.data.(VerdictData).data
verdict := verdictData.data
if verdict.Code != VC(linux.NFT_JUMP) && verdict.Code != VC(linux.NFT_GOTO) {
return false, ""
}
+217
View File
@@ -300,6 +300,7 @@ func TestEvaluateComparison(t *testing.T) {
op2 Operation // will be nil if unused
res bool // should be true if we reach end of the rule (no breaks)
}{
// 4-byte data comparisons, alternates between 4-byte and 16-byte registers.
{
tname: "compare register == 4-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0, 0, 0, 0})),
@@ -396,6 +397,201 @@ func TestEvaluateComparison(t *testing.T) {
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0})),
res: false,
},
// 8-byte data comparisons.
{
tname: "compare register == 8-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register == 8-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register != 8-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{1, 7, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register != 8-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register < 8-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{29, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register < 8-byte data, false eq",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register < 8-byte data, false gt",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{200, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register > 8-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0, 0, 0, 1, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register > 8-byte data, false eq",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register > 8-byte data, false lt",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{28, 76, 230, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register <= 8-byte data, true lt",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{29, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register <= 8-byte data, true eq",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register <= 8-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{200, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register >= 8-byte data, true gt",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0, 0, 0, 1, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register >= 8-byte data, true eq",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register >= 8-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{28, 76, 230, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})),
res: false,
},
// 12-byte data comparisons.
{
tname: "compare register == 12-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare register == 12-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare register != 12-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_NEQ, NewBytesData([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})),
res: true,
},
{
tname: "compare register != 12-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})),
res: false,
},
{
tname: "compare register < 12-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: true,
},
{
tname: "compare register < 12-byte data, false eq",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
{
tname: "compare register < 12-byte data, false gt",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x21, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
{
tname: "compare register > 12-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x21, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: true,
},
{
tname: "compare register > 12-byte data, false eq",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
{
tname: "compare register > 12-byte data, false lt",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
{
tname: "compare register <= 12-byte data, true lt",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: true,
},
{
tname: "compare register <= 12-byte data, true eq",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: true,
},
{
tname: "compare register <= 12-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0xaa, 0xaa, 0xaa, 0x20, 0xaa, 0xaa, 0xaa, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
{
tname: "compare register >= 12-byte data, true gt",
op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0xaa, 0xaa, 0xaa, 0x20, 0xaa, 0xaa, 0xaa, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: true,
},
{
tname: "compare register >= 12-byte data, true eq",
op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0xab, 0xbc, 0xcd, 0xde, 0xef, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56})),
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0xab, 0xbc, 0xcd, 0xde, 0xef, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56})),
res: true,
},
{
tname: "compare register >= 12-byte data, false",
op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x19, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
res: false,
},
// 16-byte data comparisons.
{
tname: "compare register == 16-byte data, true",
op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})),
@@ -492,6 +688,7 @@ func TestEvaluateComparison(t *testing.T) {
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})),
res: false,
},
// Empty register comparisons.
{
tname: "compare empty 4-byte register, true",
op1: mustCreateComparison(t, linux.NFT_REG32_10, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0})),
@@ -502,6 +699,26 @@ func TestEvaluateComparison(t *testing.T) {
op1: mustCreateComparison(t, linux.NFT_REG32_11, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0})),
res: false,
},
{
tname: "compare empty 8-byte register, true",
op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0})),
res: true,
},
{
tname: "compare empty 8-byte register, false",
op1: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare empty 12-byte register, true",
op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0, 8, 9, 10, 11})),
res: true,
},
{
tname: "compare empty 12-byte register, false",
op1: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})),
res: false,
},
{
tname: "compare empty 16-byte register, true",
op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})),
+6 -6
View File
@@ -306,14 +306,14 @@ func parseRegisterData(reg uint8, tokens []string, lnIdx int, tkIdx int) (int, R
}
return nextIdx, NewVerdictData(verdict), nil
}
// Handles hex data (4- or 16-byte).
// Handles hex data (4-, 8-, 12-, or 16-byte).
if len(tokens[tkIdx]) > 1 && tokens[tkIdx][:2] == "0x" {
nextIdx, data, err := parseHexData(tokens, lnIdx, tkIdx)
if err != nil {
return 0, nil, err
}
// Validates the register data type. 4-byte data is valid for both 4- and
// 16-byte registers, but 16-byte data is only valid for 16-byte registers.
// 4-byte data is only valid for 4-byte register. Any byte data can be
// stored in 16-byte registerValidates the register data type.
if err := data.ValidateRegister(reg); err != nil {
return 0, nil, &LogicError{lnIdx, tkIdx, err}
}
@@ -385,10 +385,10 @@ func parseHexData(tokens []string, lnIdx int, tkIdx int) (int, RegisterData, err
}
bytes = append(bytes, bytes4...)
}
if len(bytes) == 4 || len(bytes) == 16 {
return tkIdx, NewBytesData(bytes), nil
if len(bytes) > 16 {
return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("cannot have more than 16 bytes of hexadecimal data, got %d", len(bytes))}
}
return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("incorrect number of bytes for hexadecimal data, should be 4 or 16, got %d", len(bytes))}
return tkIdx, NewBytesData(bytes), nil
}
// parseCmpOp parses the int representing the cmpOp from the given string.
+174 -41
View File
@@ -21,8 +21,6 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
)
// interpretOperationTestAction is a generic action for testing the
// interpretation of an operation.
type interpretOperationTestAction struct {
tname string
opStr string
@@ -107,10 +105,30 @@ func TestInterpretImmediateOps(t *testing.T) {
opStr: "[ immediate reg 2 jump -> next_chain ]",
expected: nil,
},
{
tname: "16-byte register with 2-byte data",
opStr: "[ immediate reg 2 0xb80d ]",
expected: nil, // can handle 2-byte data but must be padded to 4-bytes
},
{
tname: "16-byte register with 4-byte data",
opStr: "[ immediate reg 3 0x0201a8c0 ]",
expected: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x02, 0x01, 0xa8, 0xc0})),
opStr: "[ immediate reg 1 0x0201a8c0 ]",
expected: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x02, 0x01, 0xa8, 0xc0})),
},
{
tname: "16-byte register with 6-byte data",
opStr: "[ immediate reg 2 0xb80d0120 0x0050 ]",
expected: nil, // can handle 6-byte data but must be padded to 8-bytes
},
{
tname: "16-byte register with 8-byte data",
opStr: "[ immediate reg 2 0xb80d0120 0x00000050 ]",
expected: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x50})),
},
{
tname: "16-byte register with 12-byte data",
opStr: "[ immediate reg 3 0xb80d0120 0x00000050 0xb80d0120 ]",
expected: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x50, 0xb8, 0x0d, 0x01, 0x20})),
},
{
tname: "16-byte register with 16-byte data",
@@ -118,9 +136,9 @@ func TestInterpretImmediateOps(t *testing.T) {
expected: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00})),
},
{
tname: "16-byte register with 8-byte data",
opStr: "[ immediate reg 4 0xb80d0120 0x00000050 ]",
expected: nil,
tname: "16-byte register with uneven bytes data",
opStr: "[ immediate reg 2 0xb80d0120 0x0f60a0 ]",
expected: nil, // can handle uneven but must be padded to 4-byte multiple
},
{
tname: "4-byte register with verdict data",
@@ -132,6 +150,11 @@ func TestInterpretImmediateOps(t *testing.T) {
opStr: "[ immediate reg 9 goto -> next_chain ]",
expected: nil,
},
{
tname: "4-byte register with 2-byte data",
opStr: "[ immediate reg 8 0xb80d ]",
expected: nil, // can handle 2-byte data but must be padded to 4-bytes
},
{
tname: "4-byte register with 4-byte data",
opStr: "[ immediate reg 10 0x0201a8c0 ]",
@@ -173,72 +196,182 @@ func TestInterpretComparisonOps(t *testing.T) {
expected: nil,
},
{
tname: "register == 4-byte data",
opStr: "[ cmp eq reg 1 0x0302010a ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})),
tname: "verdict register with 8-byte data comparison",
opStr: "[ cmp lt reg 0 0xb80d0120 0x02000000 ]",
expected: nil,
},
{
tname: "register != 4-byte data",
opStr: "[ cmp neq reg 2 0x00000064 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})),
},
{
tname: "register < 4-byte data",
opStr: "[ cmp lt reg 3 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})),
},
{
tname: "register <= 4-byte data",
opStr: "[ cmp lte reg 4 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})),
},
{
tname: "register > 4-byte data",
opStr: "[ cmp gt reg 8 0xe8030000 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_00, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})),
},
{
tname: "register >= 4-byte data",
opStr: "[ cmp gte reg 9 0xc02b0000 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})),
tname: "verdict register with 12-byte data comparison",
opStr: "[ cmp gte reg 0 0xb80d0120 0x18305290 0x02000000 ]",
expected: nil,
},
{
tname: "verdict register with 16-byte data comparison",
opStr: "[ cmp gt reg 0 0xb80d0120 0x00000000 0x00000000 0x02000000 ]",
opStr: "[ cmp neq reg 0 0xb80d0120 0x18305290 0x18305290 0x02000000 ]",
expected: nil,
},
{
tname: "4-byte register == 4-byte data",
opStr: "[ cmp eq reg 8 0x0302010a ]",
expected: mustCreateComparison(t, linux.NFT_REG32_00, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})),
},
{
tname: "4-byte register != 4-byte data",
opStr: "[ cmp neq reg 9 0x00000064 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})),
},
{
tname: "4-byte register < 4-byte data",
opStr: "[ cmp lt reg 10 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_02, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})),
},
{
tname: "4-byte register <= 4-byte data",
opStr: "[ cmp lte reg 11 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_03, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})),
},
{
tname: "4-byte register > 4-byte data",
opStr: "[ cmp gt reg 12 0xe8030000 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_04, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})),
},
{
tname: "4-byte register >= 4-byte data",
opStr: "[ cmp gte reg 13 0xc02b0000 ]",
expected: mustCreateComparison(t, linux.NFT_REG32_05, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})),
},
{
tname: "4-byte register with 8-byte data comparison",
opStr: "[ cmp eq reg 14 0xb80d0120 0x02000000 ]",
expected: nil,
},
{
tname: "4-byte register with 12-byte data comparison",
opStr: "[ cmp lte reg 15 0xb80d0120 0x18305290 0x02000000 ]",
expected: nil,
},
{
tname: "4-byte register with 16-byte data comparison",
opStr: "[ cmp lte reg 8 0x0302010a 0x00000000 0x00000000 0x02000001 ]",
opStr: "[ cmp gt reg 16 0x0302010a 0x00000000 0x00000000 0x02000001 ]",
expected: nil,
},
{
tname: "register == 16-byte data",
tname: "16-byte register == 4-byte data",
opStr: "[ cmp eq reg 1 0x0302010a ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})),
},
{
tname: "16-byte register != 4-byte data",
opStr: "[ cmp neq reg 2 0x00000064 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})),
},
{
tname: "16-byte register < 4-byte data",
opStr: "[ cmp lt reg 3 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})),
},
{
tname: "16-byte register <= 4-byte data",
opStr: "[ cmp lte reg 4 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})),
},
{
tname: "16-byte register > 4-byte data",
opStr: "[ cmp gt reg 1 0xe8030000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})),
},
{
tname: "16-byte register >= 4-byte data",
opStr: "[ cmp gte reg 2 0xc02b0000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})),
},
{
tname: "16-byte register == 8-byte data",
opStr: "[ cmp eq reg 1 0x0302010a 0x12345678 ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x12, 0x34, 0x56, 0x78})),
},
{
tname: "16-byte register != 8-byte data",
opStr: "[ cmp neq reg 2 0x00000064 0x00000020 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20})),
},
{
tname: "16-byte register < 8-byte data",
opStr: "[ cmp lt reg 3 0x00000000 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
},
{
tname: "16-byte register <= 8-byte data",
opStr: "[ cmp lte reg 4 0x00000164 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})),
},
{
tname: "16-byte register > 8-byte data",
opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13})),
},
{
tname: "16-byte register >= 8-byte data",
opStr: "[ cmp gte reg 3 0x0a000120 0xc0090000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0xc0, 0x09, 0x00, 0x00})),
},
{
tname: "16-byte register == 12-byte data",
opStr: "[ cmp eq reg 1 0x0302010a 0x00000000 0x12345678 ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78})),
},
{
tname: "16-byte register != 12-byte data",
opStr: "[ cmp neq reg 2 0x00000064 0x00000000 0x00000020 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20})),
},
{
tname: "16-byte register < 12-byte data",
opStr: "[ cmp lt reg 3 0x00000000 0x00000000 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
},
{
tname: "16-byte register <= 12-byte data",
opStr: "[ cmp lte reg 4 0x00000164 0x00000164 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})),
},
{
tname: "16-byte register > 12-byte data",
opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 0xc0090000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
},
{
tname: "16-byte register >= 12-byte data",
opStr: "[ cmp gte reg 3 0x0a000120 0x00000f13 0xc0090000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})),
},
{
tname: "16-byte register == 16-byte data",
opStr: "[ cmp eq reg 1 0x0302010a 0x00000000 0x00000000 0x02000002 ]",
expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02})),
},
{
tname: "register != 16-byte data",
tname: "16-byte register != 16-byte data",
opStr: "[ cmp neq reg 2 0x00000064 0x00000000 0x00000000 0x02000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00})),
},
{
tname: "register < 16-byte data",
tname: "16-byte register < 16-byte data",
opStr: "[ cmp lt reg 3 0x00000000 0x00000000 0x00000000 0x00000000 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
},
{
tname: "register <= 16-byte data",
tname: "16-byte register <= 16-byte data",
opStr: "[ cmp lte reg 4 0x00000164 0x00000164 0x00000164 0x00000164 ]",
expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})),
},
{
tname: "register > 16-byte data",
tname: "16-byte register > 16-byte data",
opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 0xc0090000 0x0b136a87 ]",
expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})),
},
{
tname: "register >= 16-byte data",
tname: "16-byte register >= 16-byte data",
opStr: "[ cmp gte reg 3 0x0a000120 0x00000f13 0xc0090000 0x0b136a87 ]",
expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})),
},