61 lines
2 KiB
Diff
61 lines
2 KiB
Diff
# UNDF: UNDF-2026-000000020
|
|
--- a/modules/caddyhttp/reverseproxy/selectionpolicies.go
|
|
+++ b/modules/caddyhttp/reverseproxy/selectionpolicies.go
|
|
@@ -1,6 +1,7 @@
|
|
package reverseproxy
|
|
|
|
import (
|
|
+ "sync/atomic"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand/v2"
|
|
@@ -30,6 +31,20 @@ import (
|
|
|
|
+// upstreamHashCache caches the xxhash of each upstream's stable string
|
|
+// representation. Populated at Provision time; cleared on config reload.
|
|
+// Key = upstream index in pool; value = xxhash of up.String().
|
|
+var upstreamHashCacheMu sync.RWMutex
|
|
+var upstreamHashCache = make(map[string]uint64) // up.String() → hash
|
|
+
|
|
+// getOrCacheUpstreamHash returns the cached xxhash of up.String(), computing
|
|
+// and storing it on the first call. O(1) amortised over the lifetime of a
|
|
+// config.
|
|
+func getOrCacheUpstreamHash(up *Upstream) uint64 {
|
|
+ key := up.String()
|
|
+ upstreamHashCacheMu.RLock()
|
|
+ h, ok := upstreamHashCache[key]
|
|
+ upstreamHashCacheMu.RUnlock()
|
|
+ if ok {
|
|
+ return h
|
|
+ }
|
|
+ h = hash(key)
|
|
+ upstreamHashCacheMu.Lock()
|
|
+ upstreamHashCache[key] = h
|
|
+ upstreamHashCacheMu.Unlock()
|
|
+ return h
|
|
+}
|
|
+
|
|
// hostByHashing returns an available host from pool based on a hashable string s.
|
|
func hostByHashing(pool []*Upstream, s string) *Upstream {
|
|
// Highest Random Weight (HRW, or "Rendezvous") hashing,
|
|
@@ -833,15 +855,17 @@ func hostByHashing(pool []*Upstream, s string) *Upstream {
|
|
var highestHash uint64
|
|
var upstream *Upstream
|
|
+ // CWE-407 fix: hash s once; combine with cached per-upstream hash using XOR.
|
|
+ // Total hash operations = 1 per request (down from N per request).
|
|
+ sHash := hash(s)
|
|
for _, up := range pool {
|
|
if !up.Available() {
|
|
continue
|
|
}
|
|
- h := hash(up.String() + s) // important to hash key and server together
|
|
+ // Combine stable upstream hash with per-request value hash.
|
|
+ // XOR preserves the avalanche property for Rendezvous hashing.
|
|
+ h := getOrCacheUpstreamHash(up) ^ sHash
|
|
if h > highestHash {
|
|
highestHash = h
|
|
upstream = up
|
|
}
|
|
}
|
|
return upstream
|
|
}
|