]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Make zap() more reliable
authorGreg Hudson <ghudson@mit.edu>
Mon, 31 Oct 2016 15:48:54 +0000 (11:48 -0400)
committerGreg Hudson <ghudson@mit.edu>
Wed, 9 Nov 2016 05:14:23 +0000 (00:14 -0500)
The gcc assembly version of zap() could still be optimized out under
gcc 5.1 or later, and the krb5int_zap() function could be optimized
out with link-time optimization.  Based on work by Zhaomo Yang and
Brian Johannesmeyer, use the C11 memset_s() when available, then fall
back to a memory barrier with gcc or clang, and finally fall back to
using krb5int_zap().  Modify krb5int_zap() to use a volatile pointer
in case link-time optimization is used.

ticket: 8514 (new)
target_version: 1.15-next
target_version: 1.14-next
tags: pullup

src/aclocal.m4
src/include/k5-int.h
src/util/support/zap.c

index bd2eb48d96d978c797ce14eb942c6b4a4df2da96..9c46da4b595601480b17090a2bb8240463cca0c3 100644 (file)
@@ -53,6 +53,8 @@ AC_SUBST(EXTRA_FILES)
 dnl Consider using AC_USE_SYSTEM_EXTENSIONS when we require autoconf
 dnl 2.59c or later, but be sure to test on Solaris first.
 AC_DEFINE([_GNU_SOURCE], 1, [Define to enable extensions in glibc])
+AC_DEFINE([__STDC_WANT_LIB_EXT1__], 1, [Define to enable C11 extensions])
+
 WITH_CC dnl
 AC_REQUIRE_CPP
 if test -z "$LD" ; then LD=$CC; fi
index 3cc32c36d2b347130935eca03af2a8f354a2911f..64991738a3e2f7d6b292a19eaea596ddcb6bc6cc 100644 (file)
@@ -652,30 +652,33 @@ k5_sha256(const krb5_data *in, uint8_t out[K5_SHA256_HASHLEN]);
  */
 #ifdef _WIN32
 # define zap(ptr, len) SecureZeroMemory(ptr, len)
-#elif defined(__GNUC__)
+#elif defined(__STDC_LIB_EXT1__)
+/*
+ * Use memset_s() which cannot be optimized out.  Avoid memset_s(NULL, 0, 0, 0)
+ * which would cause a runtime constraint violation.
+ */
 static inline void zap(void *ptr, size_t len)
 {
-    memset(ptr, 0, len);
-    /*
-     * Some versions of gcc have gotten clever enough to eliminate a
-     * memset call right before the block in question is released.
-     * This (empty) asm requires it to assume that we're doing
-     * something interesting with the stored (zero) value, so the
-     * memset can't be eliminated.
-     *
-     * An optimizer that looks at assembly or object code may not be
-     * fooled, and may still cause the memset to go away.  Address
-     * that problem if and when we encounter it.
-     *
-     * This also may not be enough if free() does something
-     * interesting like purge memory locations from a write-back cache
-     * that hasn't written back the zero bytes yet.  A memory barrier
-     * instruction would help in that case.
-     */
-    asm volatile ("" : : "g" (ptr), "g" (len));
+    if (len > 0)
+        memset_s(ptr, len, 0, len);
+}
+#elif defined(__GNUC__) || defined(__clang__)
+/*
+ * Use an asm statement which declares a memory clobber to force the memset to
+ * be carried out.  Avoid memset(NULL, 0, 0) which has undefined behavior.
+ */
+static inline void zap(void *ptr, size_t len)
+{
+    if (len > 0)
+        memset(ptr, 0, len);
+    __asm__ __volatile__("" : : "r" (ptr) : "memory");
 }
 #else
-/* Use a function from libkrb5support to defeat inlining. */
+/*
+ * Use a function from libkrb5support to defeat inlining unless link-time
+ * optimization is used.  The function uses a volatile pointer, which prevents
+ * current compilers from optimizing out the memset.
+ */
 # define zap(ptr, len) krb5int_zap(ptr, len)
 #endif
 
index 48512a903c0e46b73d4f48286873981efd6a4bed..ed31630db1b9958cec6fbf2ef77955af027c6cb6 100644 (file)
@@ -34,5 +34,8 @@
 
 void krb5int_zap(void *ptr, size_t len)
 {
-    memset(ptr, 0, len);
+    volatile char *p = ptr;
+
+    while (len--)
+        *p++ = '\0';
 }