summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Benjamin <davidben@google.com>2018-05-20 17:24:30 -0400
committerDavid Benjamin <davidben@google.com>2018-05-23 17:34:54 -0400
commit2de108dfa343c3e06eb98beb122cd06306bb12fd (patch)
tree3e49b8444a3c29a076eac36a126b77d7891d2f5b
parente363534cfe0ae01503dde6963e0631ec5f7fef3f (diff)
Save and restore the Windows error around TlsGetValue.
TlsGetValue clears the last error even on success, so that callers may distinguish it successfully returning NULL or failing. This error-mangling behavior interferes with the caller's use of GetLastError. In particular SSL_get_error queries the error queue to determine whether the caller should look at the OS's errors. To avoid destroying state, save and restore the Windows error. Fixes #6299. Reviewed-by: Rich Salz <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/6316)
-rw-r--r--crypto/threads_win.c21
-rw-r--r--test/build.info6
-rw-r--r--test/errtest.c39
-rw-r--r--test/recipes/04-test_err.t12
4 files changed, 76 insertions, 2 deletions
diff --git a/crypto/threads_win.c b/crypto/threads_win.c
index 1e5cf82073..7fdbc1f67f 100644
--- a/crypto/threads_win.c
+++ b/crypto/threads_win.c
@@ -101,7 +101,26 @@ int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
{
- return TlsGetValue(*key);
+ DWORD last_error;
+ void *ret;
+
+ /*
+ * TlsGetValue clears the last error even on success, so that callers may
+ * distinguish it successfully returning NULL or failing. It is documented
+ * to never fail if the argument is a valid index from TlsAlloc, so we do
+ * not need to handle this.
+ *
+ * However, this error-mangling behavior interferes with the caller's use of
+ * GetLastError. In particular SSL_get_error queries the error queue to
+ * determine whether the caller should look at the OS's errors. To avoid
+ * destroying state, save and restore the Windows error.
+ *
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686812(v=vs.85).aspx
+ */
+ last_error = GetLastError();
+ ret = TlsGetValue(*key);
+ SetLastError(last_error);
+ return ret;
}
int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
diff --git a/test/build.info b/test/build.info
index c3a09048b5..000153d510 100644
--- a/test/build.info
+++ b/test/build.info
@@ -51,7 +51,7 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
recordlentest drbgtest drbg_cavs_test sslbuffertest \
time_offset_test pemtest ssl_cert_table_internal_test ciphername_test \
servername_test ocspapitest rsa_mp_test fatalerrtest tls13ccstest \
- sysdefaulttest
+ sysdefaulttest errtest
SOURCE[versions]=versions.c
INCLUDE[versions]=../include
@@ -535,6 +535,10 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
SOURCE[sysdefaulttest]=sysdefaulttest.c
INCLUDE[sysdefaulttest]=../include
DEPEND[sysdefaulttest]=../libcrypto ../libssl libtestutil.a
+
+ SOURCE[errtest]=errtest.c
+ INCLUDE[errtest]=../include
+ DEPEND[errtest]=../libcrypto libtestutil.a
ENDIF
{-
diff --git a/test/errtest.c b/test/errtest.c
new file mode 100644
index 0000000000..e464d08bc0
--- /dev/null
+++ b/test/errtest.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/opensslconf.h>
+#include <openssl/err.h>
+
+#include "testutil.h"
+
+#if defined(OPENSSL_SYS_WINDOWS)
+# include <windows.h>
+#else
+# include <errno.h>
+#endif
+
+/* Test that querying the error queue preserves the OS error. */
+static int preserves_system_error(void)
+{
+#if defined(OPENSSL_SYS_WINDOWS)
+ SetLastError(ERROR_INVALID_FUNCTION);
+ ERR_get_error();
+ return TEST_int_eq(GetLastError(), ERROR_INVALID_FUNCTION);
+#else
+ errno = EINVAL;
+ ERR_get_error();
+ return TEST_int_eq(errno, EINVAL);
+#endif
+}
+
+int setup_tests(void)
+{
+ ADD_TEST(preserves_system_error);
+ return 1;
+}
diff --git a/test/recipes/04-test_err.t b/test/recipes/04-test_err.t
new file mode 100644
index 0000000000..dd7681afa4
--- /dev/null
+++ b/test/recipes/04-test_err.t
@@ -0,0 +1,12 @@
+#! /usr/bin/env perl
+# Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use OpenSSL::Test::Simple;
+
+simple_test("test_err", "errtest");