RE: Static global - bug? (Re: Two valgrind warnings inOpenSSL-possible bug???)
There is no global variable named errno, it only exist in the TLS. You could say that because there is only 1 TLS, that it's global, and it acts that way. But it's not really the same as a normal global variable. You can't access the variables in the same manner you access other global variables. Is the following code legal: void foo(void) { static int *my_errno=NULL; if(my_errno==NULL) my_errno=errno; // code that uses 'my_errno' as if it were 'errno' } The answer is that if you're compiled single-threaded, it's perfectly legal. If you're multi-threaded, it's not. This might be a perfectly sensible optimization to condition on '_REENTRANT' not being defined. In principle, the compiler might do such a thing automatically if the overhead of TLS were higher than the overhead of the test. In case of errno on a glibc system with NPTL there is no difference in the compiled code. You always get the same function call. Exactly. The precise same code can have a very different semantic meaning depending upon whether it is used in a multi-threaded or a single-threaded process. That's why you *must* specify appropriate compilation flags. The code may need to change so that it can produce *different* code to get the *same* semantic meaning. Let's assume for a moment that openssl is not compiled for multi thread, whatever you mean with that exactly. Even when the application is using threads, when openssl tries to use errno it will get the one for the current thread. Really? Even if it's coded like my 'foo' above? It will magically always get the right 'errno'? How?! That these two different semantics are implemented by the same code doesn't change the fact that the semantics are different. In fact, it makes the important point that the semantics of code can be changed by whether the process is single-threaded or multi-threaded. I'm not at all saying that the semantics are the same. All I'm saying is that in case of errno on a recent glibc there is no difference in the compiled code depending on wether your application or library is multi-threaded or not. There is a huge difference. In the case of a single-threaded process, it will get a process global 'errno' and optimizations such as my 'foo' function above are perfectly legal. In the case of a multi-threaded process, they're not. On many Linux distributions, it is perfectly acceptable to do the following: #ifdef _REENTRANT // inefficient thread-safe code #else // more efficient code that assumes 'errno' is process-global #endif On many Linux distributions it's required that if such code is in a library that it's compiled using -D_REENTRANT for the packages they ship. With other words, if the library can be compiled reentrant, that's what we want. Right, and the OP is saying he doesn't have to compile with -D_REENTRANT (or whatever else is specified on his platform as required for multi-threaded code) and he can still use the library in a multi-threaded process. I'm saying he can't do this even if he thinks the code will be the same because the same code can mean something different. Different code may be needed to get the same *effect*. Compiling code without the compilation flags your platform documents as required to get thread-safety guarantees and then using that compiled code in a multi-threaded process is simply off the map. Anything can happen. Here be dragons. This is so even in the case of Linux, where all the flags do (at least, on most Linux platforms, AFAICT) is add a single define to the compilation and a library to the linking. And it's a bad idea even if you know it's safe on a particular platform. 9 times out of 10, code winds up running on platforms you never expected. If these assumptions are no longer true on that platform, you may get code that appears to work but has subtle bugs that cause it to do the wrong thing under load. This is precisely the type of issue that can cause information intended for one client to go to another. Not a good think in an encryption/security context. DS __ OpenSSL Project http://www.openssl.org Development Mailing List openssl-dev@openssl.org Automated List Manager [EMAIL PROTECTED]
RE: Static global - bug? (Re: Two valgrind warnings inOpenSSL-possible bug???)
On Tue, 2008-01-29 at 07:54 -0800, David Schwartz wrote: There is no global variable named errno, it only exist in the TLS. You could say that because there is only 1 TLS, that it's global, and it acts that way. But it's not really the same as a normal global variable. You can't access the variables in the same manner you access other global variables. Is the following code legal: void foo(void) { static int *my_errno=NULL; if(my_errno==NULL) my_errno=errno; // code that uses 'my_errno' as if it were 'errno' } The answer is that if you're compiled single-threaded, it's perfectly legal. If you're multi-threaded, it's not. No, this is not legal code under the POSIX standard at all. -- Tomas Mraz No matter how far down the wrong road you've gone, turn back. Turkish proverb __ OpenSSL Project http://www.openssl.org Development Mailing List openssl-dev@openssl.org Automated List Manager [EMAIL PROTECTED]
Re: Static global - bug? (Re: Two valgrind warnings inOpenSSL-possible bug???)
The answer is that if you're compiled single-threaded, it's perfectly legal. If you're multi-threaded, it's not. err, nobody codes like this find me an example in real-life code that is being used. better yet, find me an example in OpenSSL. Your other points I agree with however. Let's assume for a moment that openssl is not compiled for multi thread, whatever you mean with that exactly. Even when the application is using threads, when openssl tries to use errno it will get the one for the current thread. Err? No. Between the time that errno is set, and errno is read by the application, ANOTHER thread could have changed errrno!! The very definition of a race condition. Furthermore, you might be reading 'errno', but the library call in libpthread that replaced the library call you thought you were using was actually setting (*__the_errno()). So you MUST link with -lpthread when you define _REENTRANT and visa versa to ensure the standard library and the app are talking about the same errno! My (possibly insane) usage of OpenSSL plugs in all my own OS routines, just to avoid this problem. -paul On Jan 29, 2008 5:54 PM, David Schwartz [EMAIL PROTECTED] wrote: There is no global variable named errno, it only exist in the TLS. You could say that because there is only 1 TLS, that it's global, and it acts that way. But it's not really the same as a normal global variable. You can't access the variables in the same manner you access other global variables. Is the following code legal: void foo(void) { static int *my_errno=NULL; if(my_errno==NULL) my_errno=errno; // code that uses 'my_errno' as if it were 'errno' } The answer is that if you're compiled single-threaded, it's perfectly legal. If you're multi-threaded, it's not. This might be a perfectly sensible optimization to condition on '_REENTRANT' not being defined. In principle, the compiler might do such a thing automatically if the overhead of TLS were higher than the overhead of the test. In case of errno on a glibc system with NPTL there is no difference in the compiled code. You always get the same function call. Exactly. The precise same code can have a very different semantic meaning depending upon whether it is used in a multi-threaded or a single-threaded process. That's why you *must* specify appropriate compilation flags. The code may need to change so that it can produce *different* code to get the *same* semantic meaning. Let's assume for a moment that openssl is not compiled for multi thread, whatever you mean with that exactly. Even when the application is using threads, when openssl tries to use errno it will get the one for the current thread. Really? Even if it's coded like my 'foo' above? It will magically always get the right 'errno'? How?! That these two different semantics are implemented by the same code doesn't change the fact that the semantics are different. In fact, it makes the important point that the semantics of code can be changed by whether the process is single-threaded or multi-threaded. I'm not at all saying that the semantics are the same. All I'm saying is that in case of errno on a recent glibc there is no difference in the compiled code depending on wether your application or library is multi-threaded or not. There is a huge difference. In the case of a single-threaded process, it will get a process global 'errno' and optimizations such as my 'foo' function above are perfectly legal. In the case of a multi-threaded process, they're not. On many Linux distributions, it is perfectly acceptable to do the following: #ifdef _REENTRANT // inefficient thread-safe code #else // more efficient code that assumes 'errno' is process-global #endif On many Linux distributions it's required that if such code is in a library that it's compiled using -D_REENTRANT for the packages they ship. With other words, if the library can be compiled reentrant, that's what we want. Right, and the OP is saying he doesn't have to compile with -D_REENTRANT (or whatever else is specified on his platform as required for multi-threaded code) and he can still use the library in a multi-threaded process. I'm saying he can't do this even if he thinks the code will be the same because the same code can mean something different. Different code may be needed to get the same *effect*. Compiling code without the compilation flags your platform documents as required to get thread-safety guarantees and then using that compiled code in a multi-threaded process is simply off the map. Anything can happen. Here be dragons. This is so even in the case of Linux, where all the flags do (at least, on most Linux platforms, AFAICT) is add a single define to the compilation and a library to the linking. And it's a bad idea even if you know it's safe on a particular platform. 9 times out of 10, code
Re: Static global - bug? (Re: Two valgrind warnings inOpenSSL-possible bug???)
On Tue, Jan 29, 2008 at 07:54:54AM -0800, David Schwartz wrote: There is no global variable named errno, it only exist in the TLS. You could say that because there is only 1 TLS, that it's global, and it acts that way. But it's not really the same as a normal global variable. You can't access the variables in the same manner you access other global variables. Is the following code legal: void foo(void) { static int *my_errno=NULL; if(my_errno==NULL) my_errno=errno; // code that uses 'my_errno' as if it were 'errno' } The answer is that if you're compiled single-threaded, it's perfectly legal. If you're multi-threaded, it's not. I guess by legal you mean that it has defined behaviour. This might be a perfectly sensible optimization to condition on '_REENTRANT' not being defined. In principle, the compiler might do such a thing automatically if the overhead of TLS were higher than the overhead of the test. Both the C99 standard and SUS have this nice warning in it. In C99: errno which expands to a modifiable lvalue that has type int, the value of which is set to a positive error number by several library functions. It is unspecified whether errno is a macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual object, or a program defines an identifier with the name errno, the behavior is undefined. SUS v3: It is unspecified whether errno is a macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual object, or a program defines an identifier with the name errno, the behavior is undefined. I read your example, after fixing it's problems, as suppressing the macro definition, and so would have undefined behaviour. Compiler people are very good in finding things that are undefined, and they might do things with it you don't expect. That doesn't mean everything is undefined. In the case the macro is expanded to a function call, there is nothing that tell the compiler that that function always returns the same value. I don't see how the compiler could do such an optimization. Really, it's an extraneous example. The point I'm trying to make is really simple -- you cannot expect threat safety if the platform provides a way to ask for it and you choose not to do so. As far as I know there actually isn't a _compiler_ (cc1) option to make something thread safe. There are only preprocessor and linker options. In case of errno on a glibc system with NPTL there is no difference in the compiled code. You always get the same function call. Exactly. The precise same code can have a very different semantic meaning depending upon whether it is used in a multi-threaded or a single-threaded process. That's why you *must* specify appropriate compilation flags. The code may need to change so that it can produce *different* code to get the *same* semantic meaning. Let's assume for a moment that openssl is not compiled for multi thread, whatever you mean with that exactly. Even when the application is using threads, when openssl tries to use errno it will get the one for the current thread. Really? Even if it's coded like my 'foo' above? It will magically always get the right 'errno'? How?! Not in case of your example, which you said yourself wasn't legal in multi-threaded applications. It's also not using errno, it's using some other variable. If you go and add something that has undefined behaviour, lots of things might not work as you expect. Like I said before, on a glibc system with NPTL errno is expanded to a function call that that returns (the location of) errno. That value is stored per thread in the TLS. That functions returns the value for the current thread. Right, and the OP is saying he doesn't have to compile with -D_REENTRANT (or whatever else is specified on his platform as required for multi-threaded code) and he can still use the library in a multi-threaded process. I'm saying he can't do this even if he thinks the code will be the same because the same code can mean something different. Different code may be needed to get the same *effect*. Compiling code without the compilation flags your platform documents as required to get thread-safety guarantees and then using that compiled code in a multi-threaded process is simply off the map. Anything can happen. Here be dragons. This is so even in the case of Linux, where all the flags do (at least, on most Linux platforms, AFAICT) is add a single define to the compilation and a library to the linking. As far as I know I've never said anything which suggest something else. About the only thing I've claimed is that using errno on a recent glibc with NPTL always gets your the errno for the current thread. Kurt