java-topology/defects/caddy/patch/caddy-0001.patch

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
}