@@ -39,6 +39,9 @@ internal static partial class Ssl
39
39
[ LibraryImport ( Libraries . CryptoNative , EntryPoint = "CryptoNative_SslCtxSetCaching" ) ]
40
40
internal static unsafe partial int SslCtxSetCaching ( SafeSslContextHandle ctx , int mode , int cacheSize , int contextIdLength , Span < byte > contextId , delegate * unmanaged< IntPtr , IntPtr , int > neewSessionCallback , delegate * unmanaged< IntPtr , IntPtr , void > removeSessionCallback ) ;
41
41
42
+ [ LibraryImport ( Libraries . CryptoNative , EntryPoint = "CryptoNative_SslCtxRemoveSession" ) ]
43
+ internal static unsafe partial void SslCtxRemoveSession ( SafeSslContextHandle ctx , IntPtr session ) ;
44
+
42
45
internal static bool AddExtraChainCertificates ( SafeSslContextHandle ctx , ReadOnlyCollection < X509Certificate2 > chain )
43
46
{
44
47
// send pre-computed list of intermediates.
@@ -142,27 +145,38 @@ internal bool TryAddSession(IntPtr namePtr, IntPtr session)
142
145
// This will use strdup() so it is safe to pass in raw pointer.
143
146
Interop . Ssl . SessionSetHostname ( session , namePtr ) ;
144
147
148
+ IntPtr oldSession = IntPtr . Zero ;
149
+
145
150
lock ( _sslSessions )
146
151
{
147
152
if ( ! _sslSessions . TryAdd ( targetName , session ) )
148
153
{
149
- if ( _sslSessions . Remove ( targetName , out IntPtr oldSession ) )
150
- {
151
- Interop . Ssl . SessionFree ( oldSession ) ;
152
- }
153
-
154
+ // session to this target host exists, replace it
155
+ _sslSessions . Remove ( targetName , out oldSession ) ;
154
156
bool added = _sslSessions . TryAdd ( targetName , session ) ;
155
157
Debug . Assert ( added ) ;
156
158
}
157
159
}
158
160
161
+ if ( oldSession != IntPtr . Zero )
162
+ {
163
+ // remove old session also from the internal OpenSSL cache
164
+ // and drop reference count. Since SSL_CTX_remove_session
165
+ // will call session_remove_cb, we need to do this outside
166
+ // of _sslSessions lock to avoid deadlock with another thread
167
+ // which could be holding SSL_CTX lock and trying to acquire
168
+ // _sslSessions lock.
169
+ Interop . Ssl . SslCtxRemoveSession ( this , oldSession ) ;
170
+ Interop . Ssl . SessionFree ( oldSession ) ;
171
+ }
172
+
159
173
return true ;
160
174
}
161
175
162
176
return false ;
163
177
}
164
178
165
- internal void RemoveSession ( IntPtr namePtr )
179
+ internal void RemoveSession ( IntPtr namePtr , IntPtr session )
166
180
{
167
181
Debug . Assert ( _sslSessions != null ) ;
168
182
@@ -171,11 +185,14 @@ internal void RemoveSession(IntPtr namePtr)
171
185
172
186
if ( _sslSessions != null && targetName != null )
173
187
{
174
- IntPtr oldSession ;
175
- bool removed ;
188
+ IntPtr oldSession = IntPtr . Zero ;
189
+ bool removed = false ;
176
190
lock ( _sslSessions )
177
191
{
178
- removed = _sslSessions . Remove ( targetName , out oldSession ) ;
192
+ if ( _sslSessions . TryGetValue ( targetName , out IntPtr existingSession ) && existingSession == session )
193
+ {
194
+ removed = _sslSessions . Remove ( targetName , out oldSession ) ;
195
+ }
179
196
}
180
197
181
198
if ( removed )
@@ -209,7 +226,6 @@ internal bool TrySetSession(SafeSslHandle sslHandle, string name)
209
226
// This will increase reference count on the session as needed.
210
227
// We need to hold lock here to prevent session being deleted before the call is done.
211
228
Interop . Ssl . SslSetSession ( sslHandle , session ) ;
212
-
213
229
return true ;
214
230
}
215
231
}
0 commit comments