Generate additional secret keys for the heap protector
2016-10-28 Florian Weimer <fweimer@redhat.com>
* elf/dl-keysetup.h: New file.
* elf/dl-keysetup.c: Likewise.
* elf/Makefile (dl-routines): Add dl-keysetup.
* csu/libc-start.c (LIBC_START_MAIN): Call __compute_keys to
obtain key material.
* elf/rtld.c (security_init): Likewise.
* crypt/sha256.c: Use relative #include for sha256-block.c
* sysdeps/generic/dl-osinfo.h (_dl_setup_stack_chk_guard)
(_dl_setup_pointer_guard): Remove.
* sysdeps/unix/sysv/linux/dl-osinfo.h (_dl_setup_stack_chk_guard)
(_dl_setup_pointer_guard): Likewise.
@@ -211,4 +211,4 @@ __sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx)
}
}
-#include <sha256-block.c>
+#include "sha256-block.c"
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <ldsodefs.h>
#include <exit-thread.h>
+#include <elf/dl-keysetup.h>
extern void __libc_init_first (int argc, char **argv, char **envp);
@@ -192,21 +193,21 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
we need to setup errno. */
__pthread_initialize_minimal ();
+ struct key_setup keys;
+ __compute_keys (_dl_random, &keys);
+
/* Set up the stack checker's canary. */
- uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
- THREAD_SET_STACK_GUARD (stack_chk_guard);
+ THREAD_SET_STACK_GUARD (keys.stack);
# else
- __stack_chk_guard = stack_chk_guard;
+ __stack_chk_guard = keys.stack;
# endif
/* Set up the pointer guard value. */
- uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random,
- stack_chk_guard);
# ifdef THREAD_SET_POINTER_GUARD
- THREAD_SET_POINTER_GUARD (pointer_chk_guard);
+ THREAD_SET_POINTER_GUARD (keys.pointer);
# else
- __pointer_chk_guard_local = pointer_chk_guard;
+ __pointer_chk_guard_local = keys.pointer;
# endif
#endif
@@ -31,7 +31,8 @@ routines = $(all-dl-routines) dl-support dl-iteratephdr \
dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
runtime error init fini debug misc \
version profile conflict tls origin scope \
- execstack caller open close trampoline)
+ execstack caller open close trampoline \
+ keysetup)
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
new file mode 100644
@@ -0,0 +1,77 @@
+/* Compute secret keys used for protection heuristics.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <dl-keysetup.h>
+#include <string.h>
+
+enum { at_random_size = 16 };
+
+#if __WORDSIZE == 64
+enum { sha256_output_size = 32 };
+static void compute_sha256_of_random (const void *random, void *result);
+#endif
+
+void
+__compute_keys (const void *random, struct key_setup *result)
+{
+#if __WORDSIZE == 32
+ _Static_assert (sizeof (*result) == at_random_size,
+ "no key expansion required");
+ memcpy (random, result, sizeof (result));
+#else
+ /* We use SHA-256 to expand the 16 bytes of randomness into 32
+ bytes, so that it is hard to guess the remaining keys once a
+ subset of them is known. */
+ _Static_assert (sizeof (*result) == sha256_output_size,
+ "SHA-256 provides required size");
+ compute_sha256_of_random (random, result);
+#endif
+
+ /* Prevent leakage of the stack canary through a read buffer
+ overflow of a NUL-terminated string. */
+ *(char *) &result->stack = '\0';
+
+ /* Clear the lowest three bits in the heap header guard value, so
+ that the flag bits remain unchanged. */
+ result->heap_header <<= 3;
+}
+
+#if __WORDSIZE == 64
+
+#pragma GCC visibility push (hidden)
+
+/* Avoid symbol collisions with libcrypt. */
+#define __sha256_process_block __dl_sha256_process_block
+#define __sha256_init_ctx __dl_sha256_init_ctx
+#define __sha256_process_bytes __dl_sha256_process_bytes
+#define __sha256_finish_ctx __dl_sha256_finish_ctx
+
+#include "../crypt/sha256.h"
+#include "../crypt/sha256.c"
+
+#pragma GCC visibility pop
+
+static void
+compute_sha256_of_random (const void *random, void *result)
+{
+ struct sha256_ctx ctx;
+ __sha256_init_ctx (&ctx);
+ __sha256_process_bytes (random, at_random_size, &ctx);
+ __sha256_finish_ctx (&ctx, result);
+}
+#endif
new file mode 100644
@@ -0,0 +1,45 @@
+/* Compute secret keys used for protection heuristics.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef KEY_SETUP_H
+#define KEY_SETUP_H
+
+#include <stdint.h>
+
+/* The set of protection keys used by glibc. */
+struct key_setup
+{
+ /* Canary for the stack-smashing protector. */
+ uintptr_t stack;
+
+ /* Pointer guard, protecting selected function pointers. */
+ uintptr_t pointer;
+
+ /* Heap guard, protecting the malloc chunk header. */
+ uintptr_t heap_header;
+
+ /* Heap guard part two, protecting the previous chunk size field. */
+ uintptr_t heap_footer;
+};
+
+/* Derive the keys in *RESULT from RANDOM, which comes from the
+ auxiliary vector and points to 16 bytes of randomness. */
+void __compute_keys (const void *random, struct key_setup *result)
+ attribute_hidden;
+
+#endif /* KEY_SETUP_H */
@@ -41,6 +41,7 @@
#include <tls.h>
#include <stap-probe.h>
#include <stackinfo.h>
+#include <dl-keysetup.h>
#include <assert.h>
@@ -699,21 +700,21 @@ rtld_lock_default_unlock_recursive (void *lock)
static void
security_init (void)
{
+ struct key_setup keys;
+ __compute_keys (_dl_random, &keys);
+
/* Set up the stack checker's canary. */
- uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
#ifdef THREAD_SET_STACK_GUARD
- THREAD_SET_STACK_GUARD (stack_chk_guard);
+ THREAD_SET_STACK_GUARD (keys.stack);
#else
- __stack_chk_guard = stack_chk_guard;
+ __stack_chk_guard = keys.stack;
#endif
/* Set up the pointer guard as well, if necessary. */
- uintptr_t pointer_chk_guard
- = _dl_setup_pointer_guard (_dl_random, stack_chk_guard);
#ifdef THREAD_SET_POINTER_GUARD
- THREAD_SET_POINTER_GUARD (pointer_chk_guard);
+ THREAD_SET_POINTER_GUARD (keys.pointer);
#endif
- __pointer_chk_guard_local = pointer_chk_guard;
+ __pointer_chk_guard_local = keys.pointer;
/* We do not need the _dl_random value anymore. The less
information we leave behind, the better, so clear the
@@ -16,44 +16,4 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
-#include <endian.h>
-#include <stdint.h>
-
-static inline uintptr_t __attribute__ ((always_inline))
-_dl_setup_stack_chk_guard (void *dl_random)
-{
- union
- {
- uintptr_t num;
- unsigned char bytes[sizeof (uintptr_t)];
- } ret = { 0 };
-
- if (dl_random == NULL)
- {
- ret.bytes[sizeof (ret) - 1] = 255;
- ret.bytes[sizeof (ret) - 2] = '\n';
- }
- else
- {
- memcpy (ret.bytes, dl_random, sizeof (ret));
-#if BYTE_ORDER == LITTLE_ENDIAN
- ret.num &= ~(uintptr_t) 0xff;
-#elif BYTE_ORDER == BIG_ENDIAN
- ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1)));
-#else
-# error "BYTE_ORDER unknown"
-#endif
- }
- return ret.num;
-}
-
-static inline uintptr_t __attribute__ ((always_inline))
-_dl_setup_pointer_guard (void *dl_random, uintptr_t stack_chk_guard)
-{
- uintptr_t ret;
- if (dl_random == NULL)
- ret = stack_chk_guard;
- else
- memcpy (&ret, (char *) dl_random + sizeof (ret), sizeof (ret));
- return ret;
-}
+/* No operating-system specific code in the default version. */
@@ -46,34 +46,3 @@
else if (__LINUX_KERNEL_VERSION > 0) \
FATAL ("FATAL: cannot determine kernel version\n"); \
} while (0)
-
-static inline uintptr_t __attribute__ ((always_inline))
-_dl_setup_stack_chk_guard (void *dl_random)
-{
- union
- {
- uintptr_t num;
- unsigned char bytes[sizeof (uintptr_t)];
- } ret;
-
- /* We need in the moment only 8 bytes on 32-bit platforms and 16
- bytes on 64-bit platforms. Therefore we can use the data
- directly and not use the kernel-provided data to seed a PRNG. */
- memcpy (ret.bytes, dl_random, sizeof (ret));
-#if BYTE_ORDER == LITTLE_ENDIAN
- ret.num &= ~(uintptr_t) 0xff;
-#elif BYTE_ORDER == BIG_ENDIAN
- ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1)));
-#else
-# error "BYTE_ORDER unknown"
-#endif
- return ret.num;
-}
-
-static inline uintptr_t __attribute__ ((always_inline))
-_dl_setup_pointer_guard (void *dl_random, uintptr_t stack_chk_guard)
-{
- uintptr_t ret;
- memcpy (&ret, (char *) dl_random + sizeof (ret), sizeof (ret));
- return ret;
-}