Feat 6231 deadlock (#6469)

* fixing recursive deadlock

* using atomics

* fixing init
This commit is contained in:
Mzack9999 2025-09-22 18:19:56 +02:00 committed by GitHub
parent 0ea42e5f66
commit 39e9286371
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -3,7 +3,7 @@ package authx
import ( import (
"fmt" "fmt"
"strings" "strings"
"sync" "sync/atomic"
"github.com/projectdiscovery/gologger" "github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
@ -30,8 +30,8 @@ type Dynamic struct {
Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret
Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret
fetchCallback LazyFetchSecret `json:"-" yaml:"-"` fetchCallback LazyFetchSecret `json:"-" yaml:"-"`
m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch fetched *atomic.Bool `json:"-" yaml:"-"` // atomic flag to check if the secret has been fetched
fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched fetching *atomic.Bool `json:"-" yaml:"-"` // atomic flag to prevent recursive fetch calls
error error `json:"-" yaml:"-"` // error if any error error `json:"-" yaml:"-"` // error if any
} }
@ -70,7 +70,8 @@ func (d *Dynamic) UnmarshalJSON(data []byte) error {
// Validate validates the dynamic secret // Validate validates the dynamic secret
func (d *Dynamic) Validate() error { func (d *Dynamic) Validate() error {
d.m = &sync.Mutex{} d.fetched = &atomic.Bool{}
d.fetching = &atomic.Bool{}
if d.TemplatePath == "" { if d.TemplatePath == "" {
return errkit.New(" template-path is required for dynamic secret") return errkit.New(" template-path is required for dynamic secret")
} }
@ -97,9 +98,7 @@ func (d *Dynamic) Validate() error {
func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) { func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) {
d.fetchCallback = func(d *Dynamic) error { d.fetchCallback = func(d *Dynamic) error {
err := callback(d) err := callback(d)
d.fetched = true
if err != nil { if err != nil {
d.error = err
return err return err
} }
if len(d.Extracted) == 0 { if len(d.Extracted) == 0 {
@ -184,9 +183,15 @@ func (d *Dynamic) applyValuesToSecret(secret *Secret) error {
// GetStrategy returns the auth strategies for the dynamic secret // GetStrategy returns the auth strategies for the dynamic secret
func (d *Dynamic) GetStrategies() []AuthStrategy { func (d *Dynamic) GetStrategies() []AuthStrategy {
if !d.fetched { if d.fetched.Load() {
if d.error != nil {
return nil
}
} else {
// Try to fetch if not already fetched
_ = d.Fetch(true) _ = d.Fetch(true)
} }
if d.error != nil { if d.error != nil {
return nil return nil
} }
@ -203,12 +208,23 @@ func (d *Dynamic) GetStrategies() []AuthStrategy {
// Fetch fetches the dynamic secret // Fetch fetches the dynamic secret
// if isFatal is true, it will stop the execution if the secret could not be fetched // if isFatal is true, it will stop the execution if the secret could not be fetched
func (d *Dynamic) Fetch(isFatal bool) error { func (d *Dynamic) Fetch(isFatal bool) error {
d.m.Lock() if d.fetched.Load() {
defer d.m.Unlock() return d.error
if d.fetched {
return nil
} }
// Try to set fetching flag atomically
if !d.fetching.CompareAndSwap(false, true) {
// Already fetching, return current error
return d.error
}
// We're the only one fetching, call the callback
d.error = d.fetchCallback(d) d.error = d.fetchCallback(d)
// Mark as fetched and clear fetching flag
d.fetched.Store(true)
d.fetching.Store(false)
if d.error != nil && isFatal { if d.error != nil && isFatal {
gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error) gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)
} }