David Schwartz wrote:
Mark Phalan wrote:

In this case, I presume 'pkinit' only supports one threading model
(or one
set of compatible threading models). So it can set the callbacks.
It can set the callbacks but it can't set them in a way which is safe
from races.

It can set them before it performs any OpenSSL operations. How is that not
safe from races?

Still don't get it.  Lets use Linux as an example.

You have a top level executable whose only runtime linked libraries are "libc", "libdl", "libpthread". This library from 3 independent threads decides it needs to dynamically load via the libdl API call "dlopen()" the following libraries:

"libldap_r" (this in turn has a runtime library dependency on "libcrypto" "libssl" amongst other DSOs).

"libcurl" (this in turn has a runtime library dependency on "libcrypto" "libssl" amongst other DSOs).

"libstunnel" (this in turn has a runtime library dependency on "libcrypto" "libssl" amongst other DSOs).

There are less contrived cases; Perl and Python built into executables that in turn link with OpenSSL as well.

The top level application doesn't even know OpenSSL exists. It doesn't care that is exists, since the API contract is has relates to some other functionality.


The dynamic linker will de-duplicate the DSO being loaded, so when "libcrypto" is loaded for the 2nd time it will realize it is already loaded and reference count it up one and hand the same DSO address to each of the 3 callers to share. Since that is the point of DSOs.

How does each of the 3 threads arbitrate in a thread-safe manner the initialization of the OpenSSL related libraries. How does any one of them know it is the "first user" ?

Once each of the 3 users has finished using their copy of OpenSSL they can request the dynamic linker unload it "dlclose()". So it is also necessary for OpenSSL to know when the last user has closed the library and it needs to perform memory freeing and cleanup because there are no users left. How does any one of them know it is the "last user" ?

I think these cases should have equal merit, if you are going to fix the load case you must also fix the unload case.


Now you can re look at the problem and simplify it on the basis what can be do if we know its a single thread. Then from that vantage point work out what small areas of the process need to be protected.

If you ask me the dynamic linker should provide support for this issue itself, the ability to embed a control block in the executable that provides callbacks with the dynamic linker providing serialization of running those callback and exposing the most basic locking primitives (mutex) for use only from inside those callbacks.

Windows provides this infrastructure already, _DllMain symbol and the fact that all platform provide access to the Windows API and the mutex system calls are available. Maybe open source systems do as well but we are just not aware of the hooking mechanism in this list.

The issue is we don't want the thread-aware code to manage this in the OpenSSL DSOs since that will mess up the single-thread (thread unaware) users of OpenSSL. This also means we don't want a runtime linker dependancy on libpthread just for this purpose (libcrypto currently doesn't have one, libssl does).

Since the thread locking primitives needs to be provided by the platform and this is a linker caused issue, it is the dynamic link that should provide the mechanism to clean up this mess.

This effectively becomes imposed baggage by the dynamic linker due to _SOME_ users being multi-threaded users (as well as allowing the linker freedom to load dependancies on a whim).




Perhaps I don't understand exactly what you mean by "threading model".
Are you referring to 1:1, n:1 etc?

That's part of it. It's also what threading library you are using.

You can still have a "default theading model" and still support all "known threading models" via reconfiguration before first use. There is no conflict here please stop implying there is one.


What I could do is say that when delivered as part of OpenSolaris
OpenSSL assumes the OS's threading model. If an application wants to
change that they the application should set its own callbacks.

If that's what you want, it's trivial to implement. Simply set callbacks
that are safe for the OS' threading model before you call any OpenSSL
functions.

This is exactly where the issue lies. We know we need to do this (please stop repeating back to us that this is what we need to do).

The issue is we can not do this in a thread-safe manner since:
 * The dynamic linker provides no arbitration (this is who I really blame)
* OpenSSL the library provides no arbitration (due to it having a single thread / thread unaware use case).


Just to be totally clear. All I'd like to see is a compile-time option
(it can be disabled by default) to set a default set of locking
callbacks native to the OS it's compiling on. I'm not suggesting that
the ability to set locking callbacks be removed.

The real resistance to this Mark, is the fact you are now forcing a dependency on libpthread for libcrypto which didn't exist before.

Yes I know the single threaded users (thread unaware users) can recompile a special version for their own use but this still poses a problem relating to the DSO name of the library and that fact you are impacting their support issues in the future (for your gain).

Maybe what would be better is a libcrypto_r and a libssl_r de-facto standard where multi-threaded support is a requirement and override-able default implementation was already setup to go ?

This does not mean we should not fix the real issue, it is just a naming work around until that point in time. This is more a packaging issue than a OpenSSL developer issue.



It can do this but only by potentially introducing a race.

What is the race exactly? If they're already set, setting them again to
precisely the same callbacks is harmless.

Just because you have the "same threading model" doesn't mean you are using the same threading primitive symbols.

Managing to publish and agree across many projects the exist symbols and use of OpenSSL is a problem that is completely avoided by better engineering. Trying to push out the solution to a "social" issue will not guarantee a solution.

And if the problem is an
application setting them to different callbacks, OpenSSL can't help you --
if an application changes the callbacks from the default to unsafe
callbacks, you're still hosed.

This appears to be the point where we disagree. Many including mysely are claiming that OpenSSL (or rather I am claiming the "dynamic linker") _SHOULD_ be helping us.

You are correct in that currently OpenSSL can't help us, but this is why we have a discussion here to resolve that.


Existing code that uses OpenSSL will still change those callbacks from the
defaults. New applications that don't use OpenSSL at all won't be affected
by the change. New libraries could just as easily be coded to always set
known safe callbacks.

There is only a race if some application either reverts to no callbacks or
sets unsafe callbacks. Both of those operations will still be fatal with the
suggested change.

Setting the callback(s) is unsafe. Since another thread might be using them. Do you not get that ? There is no locking around the setting of them so therefore they are thread-unsafe, the method to set locking is not re-entrant. I harp back to my request for "re-entrant thread-safe OpenSSL APIs" for setting up the threading callbacks.

Of course if the compiler generates code that does a single instruction store (of the native platform bit-width) of the same value over and over, no other thread will ever observe any change in that memory location. The point of the matter is there is no guarantee's provided by the compiler over how it goes about doing that so in order to get an engineering guarantee as required to be thread-safe a mutex would provide assurance.

This ignores other issues that are implementation details to outside users of OpenSSL what else is going on inside those OpenSSL API calls that isn't expected to be called from other threads at the same time.

The API could work like a hybrid sigaction() allowing get-and-test-and-set in one atomic operation. This will require libpthread runtime linkage in libcrypto.


Darryl
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       openssl-dev@openssl.org
Automated List Manager                           majord...@openssl.org

Reply via email to