mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-14 10:00:34 +00:00
DomainMatcher: Fix Match() result slice aliasing race (#5959)
Fixes https://github.com/XTLS/Xray-core/pull/5814
This commit is contained in:
+4
-4
@@ -271,11 +271,11 @@ func (s *DNS) sortClients(domain string) []*Client {
|
|||||||
|
|
||||||
// Priority domain matching
|
// Priority domain matching
|
||||||
hasMatch := false
|
hasMatch := false
|
||||||
MatchSlice := s.domainMatcher.Match(strings.ToLower(domain))
|
matchSlice := s.domainMatcher.Match(strings.ToLower(domain))
|
||||||
sort.Slice(MatchSlice, func(i, j int) bool {
|
sort.Slice(matchSlice, func(i, j int) bool {
|
||||||
return MatchSlice[i] < MatchSlice[j]
|
return matchSlice[i] < matchSlice[j]
|
||||||
})
|
})
|
||||||
for _, match := range MatchSlice {
|
for _, match := range matchSlice {
|
||||||
info := s.matcherInfos[match]
|
info := s.matcherInfos[match]
|
||||||
client := s.clients[info.clientIdx]
|
client := s.clients[info.clientIdx]
|
||||||
domainRule := info.domainRule
|
domainRule := info.domainRule
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DomainMatcher interface {
|
type DomainMatcher interface {
|
||||||
|
// Match returns the indices of all rules that match the input domain.
|
||||||
|
// The returned slice is owned by the caller and may be safely modified.
|
||||||
|
// Note: the slice may contain duplicates and the order is unspecified.
|
||||||
Match(input string) []uint32
|
Match(input string) []uint32
|
||||||
|
|
||||||
MatchAny(input string) bool
|
MatchAny(input string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,3 +48,25 @@ func TestCompactDomainMatcher_PreservesMixedRuleIndices(t *testing.T) {
|
|||||||
t.Fatalf("Match() = %v, want %v", got, want)
|
t.Fatalf("Match() = %v, want %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMphDomainMatcher_MatchReturnsDetachedSlice(t *testing.T) {
|
||||||
|
matcher, err := (&MphDomainMatcherFactory{}).BuildMatcher([]*DomainRule{
|
||||||
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "example.com"}}},
|
||||||
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Domain, Value: "example.com"}}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildMatcher() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := matcher.Match("example.com")
|
||||||
|
if !reflect.DeepEqual(got, []uint32{0, 1}) {
|
||||||
|
t.Fatalf("Match() = %v, want %v", got, []uint32{0, 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
got[0] = 1
|
||||||
|
|
||||||
|
gotAgain := matcher.Match("example.com")
|
||||||
|
if !reflect.DeepEqual(gotAgain, []uint32{0, 1}) {
|
||||||
|
t.Fatalf("Match() after caller mutation = %v, want %v", gotAgain, []uint32{0, 1})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package strmatcher
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -253,13 +254,12 @@ func AddMatcherToGroup(g MatcherGroup, matcher Matcher, value uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompositeMatches flattens the matches slice to produce a single matched indices slice.
|
// CompositeMatches flattens the matches slice to produce a single matched indices slice.
|
||||||
// It is designed to avoid new memory allocation as possible.
|
|
||||||
func CompositeMatches(matches [][]uint32) []uint32 {
|
func CompositeMatches(matches [][]uint32) []uint32 {
|
||||||
switch len(matches) {
|
switch len(matches) {
|
||||||
case 0:
|
case 0:
|
||||||
return nil
|
return nil
|
||||||
case 1:
|
case 1:
|
||||||
return matches[0]
|
return slices.Clone(matches[0])
|
||||||
default:
|
default:
|
||||||
result := make([]uint32, 0, 5)
|
result := make([]uint32, 0, 5)
|
||||||
for i := 0; i < len(matches); i++ {
|
for i := 0; i < len(matches); i++ {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ type IndexMatcher interface {
|
|||||||
// Match returns the indices of all matchers that matches the input.
|
// Match returns the indices of all matchers that matches the input.
|
||||||
// * Empty array is returned if no such matcher exists.
|
// * Empty array is returned if no such matcher exists.
|
||||||
// * The order of returned matchers should follow priority specification.
|
// * The order of returned matchers should follow priority specification.
|
||||||
|
// * The returned slice is owned by the caller and may be safely modified.
|
||||||
// Priority specification:
|
// Priority specification:
|
||||||
// 1. Priority between matcher types: full > domain > substr > regex.
|
// 1. Priority between matcher types: full > domain > substr > regex.
|
||||||
// 2. Priority of same-priority matchers matching at same position: the early added takes precedence.
|
// 2. Priority of same-priority matchers matching at same position: the early added takes precedence.
|
||||||
@@ -89,6 +90,7 @@ type ValueMatcher interface {
|
|||||||
// * Empty array is returned if no such matcher exists.
|
// * Empty array is returned if no such matcher exists.
|
||||||
// * The order of returned values should follow priority specification.
|
// * The order of returned values should follow priority specification.
|
||||||
// * Same value may appear multiple times if multiple matched matchers were added with that value.
|
// * Same value may appear multiple times if multiple matched matchers were added with that value.
|
||||||
|
// * The returned slice is owned by the caller and may be safely modified.
|
||||||
// Priority specification:
|
// Priority specification:
|
||||||
// 1. Priority between matcher types: full > domain > substr > regex.
|
// 1. Priority between matcher types: full > domain > substr > regex.
|
||||||
// 2. Priority of same-priority matchers matching at same position: the early added takes precedence.
|
// 2. Priority of same-priority matchers matching at same position: the early added takes precedence.
|
||||||
|
|||||||
Reference in New Issue
Block a user