Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SslSessionsCache growth on Linux with MTLS using many client certificates #71401

Closed
RobinsonWM opened this issue Jun 28, 2022 · 6 comments
Closed
Assignees
Labels
area-System.Net.Security bug needs-author-action An issue or pull request that requires more info or actions from the author.
Milestone

Comments

@RobinsonWM
Copy link

Description

If an application acting as a client creates many mutually-authenticated TLS connections to a server, with each connection using a different client certificate, the application's resident memory grows linearly.

If I run the same code on Windows and Linux, I only see the memory growth on Linux.

This cache appears to grow without bound:

private static readonly ConcurrentDictionary<SslCredKey, SafeCredentialReference> s_cachedCreds =

There are several IDisposables involved in generating a certificate and making an HTTP request, and I believe I am disposing all of them.

Reproduction Steps

https://gist.github.com/RobinsonWM/cae7244762173127d85f9b842cc3c1ba

The code in this gist reproduces the issue reliably for me. I was running it by starting a container, docker run mcr.microsoft.com/dotnet/sdk:6.0, copying in the code, and then running dotnet run.

The code starts an HTTPS server and then makes requests against it very quickly, each from a new HttpClient with a unique certificate. The test app periodically logs out the result of GC.GetTotalMemory(true) and SslSessionsCache.s_cachedCreds.Count, showing that both increase linearly on Linux but not on Windows.

This test application does muddy up the water a bit since it's running both the client and the server in the same process. I did that to make it easy to reproduce this issue. When I originally encountered this issue, it was in an application that was running only the client; the server was a separate process not written in .NET.

Expected behavior

Over time, if load is steady, the application's resident memory usage is also steady.

Actual behavior

The application works fine for a long time, but its resident memory usage grows linearly.

Regression?

I don't know if this is a regression

Known Workarounds

The same workaround for #65563 works for this issue, except for this issue it would need to be run on a timer.

private static void ClearCache()
{
    var sslAssembly = Assembly.GetAssembly(typeof(SslStream));
    var sslSessionCacheClass = sslAssembly.GetType("System.Net.Security.SslSessionsCache");
    var cachedCredsInfo = sslSessionCacheClass.GetField("s_cachedCreds", BindingFlags.NonPublic | BindingFlags.Static);
    var cachedCreds = cachedCredsInfo.GetValue(null);
    cachedCreds.GetType().GetMethod("Clear", BindingFlags.Public | BindingFlags.Instance).Invoke(cachedCreds, null);
}

Configuration

.NET 6.0 on Linux x64, with the Docker image mcr.microsoft.com/dotnet/runtime-deps:6.0
I have observed this issue on Linux x64. The same code run on Windows x64 works and does not exhibit the problem. I believe this issue is specific to Linux.

Other information

I realize this use case may sound contrived. I ran into this issue while developing a load-generation application used as part of a performance test. In order to simulate multiple users when generating load, I needed my HTTPS requests to use certificates generated on-the-fly, one for each user.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jun 28, 2022
@ghost
Copy link

ghost commented Jun 28, 2022

Tagging subscribers to this area: @dotnet/ncl, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

If an application acting as a client creates many mutually-authenticated TLS connections to a server, with each connection using a different client certificate, the application's resident memory grows linearly.

If I run the same code on Windows and Linux, I only see the memory growth on Linux.

This cache appears to grow without bound:

private static readonly ConcurrentDictionary<SslCredKey, SafeCredentialReference> s_cachedCreds =

There are several IDisposables involved in generating a certificate and making an HTTP request, and I believe I am disposing all of them.

Reproduction Steps

https://gist.github.com/RobinsonWM/cae7244762173127d85f9b842cc3c1ba

The code in this gist reproduces the issue reliably for me. I was running it by starting a container, docker run mcr.microsoft.com/dotnet/sdk:6.0, copying in the code, and then running dotnet run.

The code starts an HTTPS server and then makes requests against it very quickly, each from a new HttpClient with a unique certificate. The test app periodically logs out the result of GC.GetTotalMemory(true) and SslSessionsCache.s_cachedCreds.Count, showing that both increase linearly on Linux but not on Windows.

This test application does muddy up the water a bit since it's running both the client and the server in the same process. I did that to make it easy to reproduce this issue. When I originally encountered this issue, it was in an application that was running only the client; the server was a separate process not written in .NET.

Expected behavior

Over time, if load is steady, the application's resident memory usage is also steady.

Actual behavior

The application works fine for a long time, but its resident memory usage grows linearly.

Regression?

I don't know if this is a regression

Known Workarounds

The same workaround for #65563 works for this issue, except for this issue it would need to be run on a timer.

private static void ClearCache()
{
    var sslAssembly = Assembly.GetAssembly(typeof(SslStream));
    var sslSessionCacheClass = sslAssembly.GetType("System.Net.Security.SslSessionsCache");
    var cachedCredsInfo = sslSessionCacheClass.GetField("s_cachedCreds", BindingFlags.NonPublic | BindingFlags.Static);
    var cachedCreds = cachedCredsInfo.GetValue(null);
    cachedCreds.GetType().GetMethod("Clear", BindingFlags.Public | BindingFlags.Instance).Invoke(cachedCreds, null);
}

Configuration

.NET 6.0 on Linux x64, with the Docker image mcr.microsoft.com/dotnet/runtime-deps:6.0
I have observed this issue on Linux x64. The same code run on Windows x64 works and does not exhibit the problem. I believe this issue is specific to Linux.

Other information

I realize this use case may sound contrived. I ran into this issue while developing a load-generation application used as part of a performance test. In order to simulate multiple users when generating load, I needed my HTTPS requests to use certificates generated on-the-fly, one for each user.

Author: RobinsonWM
Assignees: -
Labels:

area-System.Net.Security

Milestone: -

@wfurt
Copy link
Member

wfurt commented Jun 29, 2022

There should be cap on the SslSessionsCache but I don't think we have any tests to verify that. That part changed a bit in 7.0 with TLS resume and it will change even more with #69527. I think we should look into it for 7.0

@karelz karelz added this to the 7.0.0 milestone Jun 30, 2022
@karelz karelz added bug and removed untriaged New issue has not been triaged by the area owner labels Jun 30, 2022
@karelz
Copy link
Member

karelz commented Jun 30, 2022

Triage: We should confirm if we grow indefinitely.

@wfurt wfurt self-assigned this Jul 12, 2022
@wfurt
Copy link
Member

wfurt commented Jul 17, 2022

Can you please try it with 7.0 preview build @RobinsonWM? I did little bit of digging and it seems like this comes from client side in 6.0. I don't see the growth with 7.0 but it would be nice to confirm it with your setup.

@wfurt wfurt added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jul 24, 2022
@ghost
Copy link

ghost commented Jul 24, 2022

This issue has been marked needs-author-action and may be missing some important information.

@karelz
Copy link
Member

karelz commented Aug 2, 2022

Triage: We do not have 7.0 repro - it seems to be solved there. Closing as Fixed in 7.0.

@karelz karelz closed this as completed Aug 2, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security bug needs-author-action An issue or pull request that requires more info or actions from the author.
Projects
None yet
Development

No branches or pull requests

3 participants