Fix unbounded allocations when reading untrusted binary data

This commit is contained in:
世界
2026-08-10 13:26:42 +08:00
parent 45ca32dcb9
commit f260e771ea
11 changed files with 151 additions and 90 deletions
+25 -47
View File
@@ -77,13 +77,14 @@ func Read(reader io.Reader, recover bool) (ruleSetCompat option.PlainRuleSetComp
return
}
ruleSetCompat.Version = version
ruleSetCompat.Options.Rules = make([]option.HeadlessRule, length)
for i := range length {
ruleSetCompat.Options.Rules[i], err = readRule(bReader, recover)
var rule option.HeadlessRule
rule, err = readRule(bReader, recover, 0)
if err != nil {
err = E.Cause(err, "read rule[", i, "]")
return
}
ruleSetCompat.Options.Rules = append(ruleSetCompat.Options.Rules, rule)
}
return
}
@@ -119,7 +120,13 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateVersion uint8)
return compressWriter.Close()
}
func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err error) {
const maxLogicalRuleDepth = 100
func readRule(reader varbin.Reader, recover bool, depth int) (rule option.HeadlessRule, err error) {
if depth > maxLogicalRuleDepth {
err = E.New("logical rule nested too deep")
return
}
var ruleType uint8
err = binary.Read(reader, binary.BigEndian, &ruleType)
if err != nil {
@@ -131,7 +138,7 @@ func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err
rule.DefaultOptions, err = readDefaultRule(reader, recover)
case 1:
rule.Type = C.RuleTypeLogical
rule.LogicalOptions, err = readLogicalRule(reader, recover)
rule.LogicalOptions, err = readLogicalRule(reader, recover, depth)
default:
err = E.New("unknown rule type: ", ruleType)
}
@@ -160,7 +167,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
switch itemType {
case ruleItemQueryType:
var rawQueryType []uint16
rawQueryType, err = readRuleItemUint16(reader)
rawQueryType, err = varbin.ReadSlice[uint16](reader, binary.BigEndian)
if err != nil {
return
}
@@ -200,11 +207,11 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
rule.IPCIDR = common.Map(rule.IPSet.Prefixes(), netip.Prefix.String)
}
case ruleItemSourcePort:
rule.SourcePort, err = readRuleItemUint16(reader)
rule.SourcePort, err = varbin.ReadSlice[uint16](reader, binary.BigEndian)
case ruleItemSourcePortRange:
rule.SourcePortRange, err = readRuleItemString(reader)
case ruleItemPort:
rule.Port, err = readRuleItemUint16(reader)
rule.Port, err = varbin.ReadSlice[uint16](reader, binary.BigEndian)
case ruleItemPortRange:
rule.PortRange, err = readRuleItemString(reader)
case ruleItemProcessName:
@@ -230,7 +237,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
rule.AdGuardDomain = matcher.Dump()
}
case ruleItemNetworkType:
rule.NetworkType, err = readRuleItemUint8[option.InterfaceType](reader)
rule.NetworkType, err = varbin.ReadSlice[option.InterfaceType](reader, binary.BigEndian)
case ruleItemNetworkIsExpensive:
rule.NetworkIsExpensive = true
case ruleItemNetworkIsConstrained:
@@ -242,7 +249,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
if err != nil {
return
}
for i := uint64(0); i < size; i++ {
for range size {
var key uint8
err = binary.Read(reader, binary.BigEndian, &key)
if err != nil {
@@ -510,18 +517,14 @@ func readRuleItemString(reader varbin.Reader) ([]string, error) {
if err != nil {
return nil, err
}
result := make([]string, length)
for i := range result {
strLen, err := binary.ReadUvarint(reader)
var result []string
for range length {
var value []byte
value, err = varbin.ReadSlice[byte](reader, binary.BigEndian)
if err != nil {
return nil, err
}
buf := make([]byte, strLen)
_, err = io.ReadFull(reader, buf)
if err != nil {
return nil, err
}
result[i] = string(buf)
result = append(result, string(value))
}
return result, nil
}
@@ -548,19 +551,6 @@ func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) e
return nil
}
func readRuleItemUint8[E ~uint8](reader varbin.Reader) ([]E, error) {
length, err := binary.ReadUvarint(reader)
if err != nil {
return nil, err
}
result := make([]E, length)
_, err = io.ReadFull(reader, *(*[]byte)(unsafe.Pointer(&result)))
if err != nil {
return nil, err
}
return result, nil
}
func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []E) error {
err := writer.WriteByte(itemType)
if err != nil {
@@ -574,19 +564,6 @@ func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []
return err
}
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
length, err := binary.ReadUvarint(reader)
if err != nil {
return nil, err
}
result := make([]uint16, length)
err = binary.Read(reader, binary.BigEndian, result)
if err != nil {
return nil, err
}
return result, nil
}
func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) error {
err := writer.WriteByte(itemType)
if err != nil {
@@ -625,7 +602,7 @@ func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) err
return writeIPSet(writer, ipSet)
}
func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) {
func readLogicalRule(reader varbin.Reader, recovery bool, depth int) (logicalRule option.LogicalHeadlessRule, err error) {
mode, err := reader.ReadByte()
if err != nil {
return
@@ -643,13 +620,14 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
if err != nil {
return
}
logicalRule.Rules = make([]option.HeadlessRule, length)
for i := range length {
logicalRule.Rules[i], err = readRule(reader, recovery)
var rule option.HeadlessRule
rule, err = readRule(reader, recovery, depth+1)
if err != nil {
err = E.Cause(err, "read logical rule [", i, "]")
return
}
logicalRule.Rules = append(logicalRule.Rules, rule)
}
err = binary.Read(reader, binary.BigEndian, &logicalRule.Invert)
if err != nil {