Skip to content

fix(oauth): reuse AccessToken API client to fix unbounded clientMap memory leak#79

Open
DBGR18 wants to merge 3 commits into
free5gc:mainfrom
DBGR18:fix/oauth-clientmap-leak
Open

fix(oauth): reuse AccessToken API client to fix unbounded clientMap memory leak#79
DBGR18 wants to merge 3 commits into
free5gc:mainfrom
DBGR18:fix/oauth-clientmap-leak

Conversation

@DBGR18

@DBGR18 DBGR18 commented Jul 6, 2026

Copy link
Copy Markdown

Summary:
oauth.sendAccTokenReq caches the AccessToken.APIClient in a package-level clientMap sync.Map, but keys the cache by the freshly-allocated *Configuration returned from NewConfiguration() on every call. Because that pointer is different on each invocation, the cache lookup never hits and a new entry (each holding an *AccessToken.APIClient with its own http.Client) is stored on every token request. The map only ever grows.

This is the root cause of free5gc/free5gc#1060.

Root cause:
oauth/get_token_context.go:

var clientMap sync.Map // package-level, never pruned

func sendAccTokenReq(...) (...) {
    configuration := AccessToken.NewConfiguration() // new *Configuration every call
    configuration.SetBasePath(nrfUri)

    if val, ok := clientMap.Load(configuration); ok { // key = ephemeral pointer -> always miss
        client = val.(*AccessToken.APIClient)
    } else {
        client = AccessToken.NewAPIClient(configuration)
        clientMap.Store(configuration, client) // new permanent entry on every request
    }
    ...
}

NewConfiguration() returns a *Configuration, so the map key is a distinct pointer on every call. tokenMap (keyed by the scope string) is fine and is correctly reused; only clientMap leaks.

Fix:
Key clientMap by the stable nrfUri string so the client is actually reused:

if val, ok := clientMap.Load(nrfUri); ok {
    client = val.(*AccessToken.APIClient)
} else {
    configuration := AccessToken.NewConfiguration()
    configuration.SetBasePath(nrfUri)
    client = AccessToken.NewAPIClient(configuration)
    clientMap.Store(nrfUri, client)
}

Copilot AI review requested due to automatic review settings July 6, 2026 08:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an unbounded memory leak in the OAuth AccessToken client cache by ensuring the cache key is stable across calls, so *AccessToken.APIClient instances are actually reused rather than continuously accumulated.

Changes:

  • Re-key clientMap from ephemeral *Configuration pointers to the stable nrfUri string to enable cache hits.
  • Move NewConfiguration() creation into the cache-miss path to avoid unnecessary allocations.
  • Add an explanatory comment documenting the original leak mechanism and the rationale for the fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// whose pointer differs on every call, so the lookup never hit and a new
// entry (each holding an *APIClient with its own http.Client) was stored on
// every token request, leaking unboundedly under SBI load.
if val, ok := clientMap.Load(nrfUri); ok {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants