linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Introduce the initify gcc plugin
@ 2016-12-16 22:06 Kees Cook
  2016-12-16 22:06 ` [PATCH v4 1/4] gcc-plugins: Add " Kees Cook
                   ` (5 more replies)
  0 siblings, 6 replies; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:06 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, linux-kernel, Arnd Bergmann, Emese Revfy,
	Josh Triplett, pageexec, spender, mmarek, yamada.masahiro,
	linux-kbuild, minipli, linux, catalin.marinas, linux,
	david.brown, benh, tglx, akpm, jlayton, sam

Hi,

This is a continuation of Emese Revfy's initify plugin upstreaming. This
is based on her v3, but updated with various fixes from her github tree.
Additionally, I split off the printf attribute fixes and sent those
separately.

This is the initify gcc plugin. The kernel already has a mechanism to
free up code and data memory that is only used during kernel or module
initialization.  This plugin will teach the compiler to find more such
code and data that can be freed after initialization. It reduces memory
usage.  The initify gcc plugin can be useful for embedded systems.

Originally it was a CII project supported by the Linux Foundation.

This plugin is the part of grsecurity/PaX.

The plugin supports all gcc versions from 4.5 to 7.0.

Changes on top of the PaX version (since March 6.). These are the important
ones:
 * move all local strings to init.rodata.str and exit.rodata.str
   (not just __func__)
 * report all initified strings and functions
   (GCC_PLUGIN_INITIFY_VERBOSE config option)
 * automatically discover init/exit functions and apply the __init or
   __exit attributes on them

You can find more about the changes here:
https://github.com/ephox-gcc-plugins/initify

This patch set is based on v4.9-rc2.

Some build statistics about the plugin:

On allyes config (amd64, gcc-6):
* 8412 initified strings
*  167 initified functions

On allmod config (i386, gcc-6):
* 8597 initified strings
*  159 initified functions

On allyes config (amd64, gcc-6):

section         vanilla                 vanilla + initify        change
-----------------------------------------------------------------------
.rodata         21746728 (0x14bd428)    21488680 (0x147e428)    -258048
.init.data       1338376  (0x146c08)     1683016  (0x19ae48)    +344640
.text           78270904 (0x4aa51b8)    78228280 (0x4a9ab38)     -42624
.init.text       1184725  (0x1213d5)     1223257  (0x12aa59)     +38532
.exit.data           104  (0x000068)       17760  (0x004560)     +17656
.exit.text        174473  (0x02a989)      175763  (0x02ae93)      +1290

        FileSiz (vanilla)       FileSiz (vanilla + initify)      change
------------------------------------------------------------------------
00      102936576 (0x622b000)   102678528 (0x61ec000)           -258048
03       28680192 (0x1b5a000)    29081600 (0x1bbc000)           +401408

00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
       .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
       __param __modver
03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
       .parainstructions .altinstructions .altinstr_replacement
       .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk


On defconfig (amd64, gcc-6):
* 1957 initified strings
*   29 initified functions

On defconfig (amd64, gcc-6):

section         vanilla                 vanilla + initify        change
-----------------------------------------------------------------------
.rodata         2524240 (0x268450)      2462800 (0x259450)      -61440
.init.data       560256 (0x088c80)       644000 (0x09d3a0)      +83744
.text           9377367 (0x8f1657)      9373079 (0x8f0597)       -4288
.init.text       438586 (0x06b13a)       441828 (0x06bde4)       +3242
.exit.data            0                     832 (0x000340)        +832
.exit.text         8857 (0x002299)          8857 (0x002299)          0

        FileSiz (vanilla)       FileSiz (vanilla + initify)      change
------------------------------------------------------------------------
00      13398016 (0xcc7000)     13336576 (0xcb8000)             -61440
03       2203648 (0x21a000)      2293760 (0x230000)             +90112

00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
       .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
       __param __modver
03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
       .parainstructions .altinstructions .altinstr_replacement
       .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk

One thing of note is that this plugin triggers false positive warnings
from the modpost section mismatch detector. Further work is needed to
deal with this.


Changed from v3:
 * Refresh from Emese's latest version.

Changed from v2:
 * Check all uses when walking a use-def chain.
 * Check all uses of initialized local variables and initify them if they
   have only nocapture uses. Previously only uses in call arguments
   determined whether the initializer value could be initified.
 * Handle the format gcc attribute from the plugin too.
 * Verify nocapture parameters of calls. Track uses of these parameters
   and verify that all uses are not captured. Verify only the nocapture
   attribute (The format attribute should be verified too.).
 * Fixed wrong indexing of function arguments.
 * Fixed decl comparison. When comparing two decls the tree codes must be
   the same.
 * Search capture uses of the return value. Use negative nocapture
   attribute parameter on a function argument to verify that the return
   value is not captured.
 * Stop the search for capture uses if there is a cast to integer type.
 * Removed unnecessary duplication hook.
 * Handle cloned functions with a changed argument list.
 * Check visited tree nodes to avoid an infinite loop.
 * Add a new initify plugin option: enable_init_to_exit_moves. Move a
   function to the exit section if it is called by __init and __exit
   functions too.
 * Added plugin option to disable the search of capture uses in nocapture
   functions. We must be able to disable verification of nocapture
   functions because there is a lot of asm code in the str* and mem*
   functions on i386.
 * Added some more nocapture attributes.
 * Added some more printf attributes.
 * Added some unverified_nocapture attributes.
 * Make is_kernel_rodata() nocapture.
 * Added comment for the nocapture attribute from Kees.

Changes from v1:
 * Removed unnecessary nocapture attributes from boot code
   (Reported-by: PaX Team <pageexec@freemail.hu>)
 * Removed nocapture attributes from functions that return
   the marked parameter
   (Reported-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>)
 * Added nocapture attribute to strlen()
 * Updated gcc-common.h from PaX
 * Don't forcibly constify initified string types
   this caused the size reduction of the .data section
   (initify_plugin.c)
 * Added the section mismatch problem in the commit message

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v4 1/4] gcc-plugins: Add the initify gcc plugin
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
@ 2016-12-16 22:06 ` Kees Cook
  2016-12-16 22:45   ` PaX Team
  2016-12-16 22:06 ` [PATCH v4 2/4] util: Move type casts into is_kernel_rodata Kees Cook
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:06 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, Emese Revfy, linux-kernel, Arnd Bergmann,
	Josh Triplett, pageexec, spender, mmarek, yamada.masahiro,
	linux-kbuild, minipli, linux, catalin.marinas, linux,
	david.brown, benh, tglx, akpm, jlayton, sam

From: Emese Revfy <re.emese@gmail.com>

The kernel already has a mechanism to free up code and data memory that
is only used during kernel or module initialization.  This plugin will
teach the compiler to find more such code and data that can be freed
after initialization.

The plugin runs in two passes. The first one tries to find all functions
that can be become __init/__exit. The second one moves string constants
(local variables and function string arguments marked by the nocapture
attribute) only referenced in __init/__exit functions to the
__initconst/__exitconst sections.

The result is a reduction in memory usage, since more code is moved into
__init and freed during boot. This plugin can be useful for embedded
systems.

If a function is called by __init and __exit functions as well then
the plugin moves it to the __exit section. This causes false positive
section mismatch errors/warnings that I don't know how to handle yet.

The instrumentation pass of the latent_entropy plugin must run after
the initify plugin to increase coverage.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
[kees: expanded commit message]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/Kconfig                         |   38 +
 arch/x86/Kconfig                     |    1 +
 arch/x86/lib/Makefile                |    4 +
 include/asm-generic/vmlinux.lds.h    |    2 +
 include/linux/compiler-gcc.h         |   14 +
 include/linux/compiler.h             |    4 +
 scripts/Makefile.gcc-plugins         |    9 +
 scripts/gcc-plugins/gcc-common.h     |   41 +
 scripts/gcc-plugins/initify_plugin.c | 1883 ++++++++++++++++++++++++++++++++++
 9 files changed, 1996 insertions(+)
 create mode 100644 scripts/gcc-plugins/initify_plugin.c

diff --git a/arch/Kconfig b/arch/Kconfig
index 659bdd079277..f666491deb31 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -401,6 +401,44 @@ config GCC_PLUGIN_LATENT_ENTROPY
 	   * https://grsecurity.net/
 	   * https://pax.grsecurity.net/
 
+config GCC_PLUGIN_INITIFY
+	bool "Free more kernel memory after init"
+	depends on GCC_PLUGINS
+	help
+	  The kernel has a mechanism to free up code and data memory that is
+	  only used during kernel or module initialization.  Enabling this
+	  feature will teach the compiler to find more such code and data
+	  that can be freed after initialization.
+
+	  This plugin currently triggers false positive warnings from the
+	  section mismatch checker.
+
+	  This plugin is the part of grsecurity/PaX. More information at:
+	   * https://grsecurity.net/
+	   * https://pax.grsecurity.net/
+
+config HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT
+	bool
+	depends on GCC_PLUGINS
+	help
+	  Move functions to the exit section if they are called by __init and
+	  __exit functions too.
+
+config GCC_PLUGIN_INITIFY_INIT_EXIT
+	bool
+	depends on GCC_PLUGIN_INITIFY && HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT
+
+config GCC_PLUGIN_INITIFY_VERBOSE
+	bool "Verbose"
+	depends on GCC_PLUGINS
+	depends on GCC_PLUGIN_INITIFY
+	help
+	  Print all initified strings and all functions which should be
+	  __init/__exit.
+	  Note that the candidates identified for __init/__exit markings
+	  depend on the current kernel configuration and thus should be verified
+	  manually before the source code is patched.
+
 config HAVE_CC_STACKPROTECTOR
 	bool
 	help
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bada636d1065..5b6e9b963940 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -113,6 +113,7 @@ config X86
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_TRACER
 	select HAVE_GCC_PLUGINS
+	select HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT if GCC_PLUGINS
 	select HAVE_GENERIC_DMA_COHERENT	if X86_32
 	select HAVE_HW_BREAKPOINT
 	select HAVE_IDE
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 34a74131a12c..b98b8fdb7aaf 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -29,6 +29,10 @@ lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
 obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
 
 ifeq ($(CONFIG_X86_32),y)
+        CFLAGS_strstr_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+        CFLAGS_string_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+        CFLAGS_memcpy_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+
         obj-y += atomic64_32.o
         lib-y += atomic64_cx8_32.o
         lib-y += checksum_32.o
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 31e1d639abed..a3fc9a0acb5e 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -551,6 +551,7 @@
 	MEM_DISCARD(init.data)						\
 	KERNEL_CTORS()							\
 	MCOUNT_REC()							\
+	*(.init.rodata.str)						\
 	*(.init.rodata)							\
 	FTRACE_EVENTS()							\
 	TRACE_SYSCALLS()						\
@@ -578,6 +579,7 @@
 	*(.fini_array)							\
 	*(.dtors)							\
 	MEM_DISCARD(exit.data)						\
+	*(.exit.rodata.str)						\
 	MEM_DISCARD(exit.rodata)
 
 #define EXIT_TEXT							\
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 928e5ca0caee..1e11ee911c3e 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -196,6 +196,20 @@
 #endif
 
 /*
+ * The initify gcc-plugin attempts to identify const arguments that are only
+ * used during init (see __init and __exit), so they can be moved to the
+ * .init.rodata/.exit.rodata section. If an argument is passed to a non-init
+ * function, it must normally be assumed that such an argument has been
+ * captured by that function and may be used in the future when .init/.exit has
+ * been unmapped from memory. In order to identify functions that are confirmed
+ * to not capture their arguments, the __nocapture() attribute is used so that
+ * initify can better identify candidate variables.
+ */
+#ifdef INITIFY_PLUGIN
+#define __nocapture(...) __attribute__((nocapture(__VA_ARGS__)))
+#endif
+
+/*
  * Mark a position in code as unreachable.  This can be used to
  * suppress control flow warnings after asm blocks that transfer
  * control elsewhere.
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index cf0fa5d86059..8b3dcc790bb6 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -433,6 +433,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
 # define __latent_entropy
 #endif
 
+#ifndef __nocapture
+# define __nocapture(...)
+#endif
+
 /*
  * Tell gcc if a function is cold. The compiler will assume any path
  * directly leading to the call is unlikely.
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 060d2cb373db..f717ee67a6e7 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -12,6 +12,14 @@ ifdef CONFIG_GCC_PLUGINS
     DISABLE_LATENT_ENTROPY_PLUGIN			+= -fplugin-arg-latent_entropy_plugin-disable
   endif
 
+  gcc-plugin-$(CONFIG_GCC_PLUGIN_INITIFY)		+= initify_plugin.so
+  gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY)	+= -DINITIFY_PLUGIN -fplugin-arg-initify_plugin-search_init_exit_functions
+  gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY_VERBOSE)+= -fplugin-arg-initify_plugin-verbose -fplugin-arg-initify_plugin-print_missing_attr
+  gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY_INIT_EXIT)	+= -fplugin-arg-initify_plugin-enable_init_to_exit_moves
+  ifdef CONFIG_GCC_PLUGIN_INITIFY
+    INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS		+= -fplugin-arg-initify_plugin-disable_verify_nocapture_functions
+  endif
+
   ifdef CONFIG_GCC_PLUGIN_SANCOV
     ifeq ($(CFLAGS_KCOV),)
       # It is needed because of the gcc-plugin.sh and gcc version checks.
@@ -29,6 +37,7 @@ ifdef CONFIG_GCC_PLUGINS
 
   export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
   export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
+  export INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS
 
   ifneq ($(PLUGINCC),)
     # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 950fd2e64bb7..369bfb471e58 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -287,6 +287,26 @@ static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct c
 	return NULL;
 }
 
+static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
+				bool (*callback)(cgraph_node_ptr, void *),
+				void *data, bool include_overwritable)
+{
+	cgraph_node_ptr alias;
+
+	if (callback(node, data))
+		return true;
+
+	for (alias = node->same_body; alias; alias = alias->next) {
+		if (include_overwritable ||
+			cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE)
+			if (cgraph_for_node_and_aliases(alias, callback, data,
+							include_overwritable))
+				return true;
+	}
+
+	return false;
+}
+
 #define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \
 	for ((node) = cgraph_first_function_with_gimple_body(); (node); \
 		(node) = cgraph_next_function_with_gimple_body(node))
@@ -496,6 +516,14 @@ static inline const greturn *as_a_const_greturn(const_gimple stmt)
 
 typedef struct rtx_def rtx_insn;
 
+static inline const char *get_decl_section_name(const_tree decl)
+{
+	if (DECL_SECTION_NAME(decl) == NULL_TREE)
+		return NULL;
+
+	return TREE_STRING_POINTER(DECL_SECTION_NAME(decl));
+}
+
 static inline void set_decl_section_name(tree node, const char *value)
 {
 	if (value)
@@ -611,6 +639,11 @@ inline bool is_a_helper<const gassign *>::test(const_gimple gs)
 
 #define INSN_DELETED_P(insn) (insn)->deleted()
 
+static inline const char *get_decl_section_name(const_tree decl)
+{
+	return DECL_SECTION_NAME(decl);
+}
+
 /* symtab/cgraph related */
 #define debug_cgraph_node(node) (node)->debug()
 #define cgraph_get_node(decl) cgraph_node::get(decl)
@@ -674,6 +707,14 @@ static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node)
 	return node->get_alias_target();
 }
 
+static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
+				bool (*callback)(cgraph_node_ptr, void *),
+				void *data, bool include_overwritable)
+{
+	return node->call_for_symbol_thunks_and_aliases(callback, data,
+							include_overwritable);
+}
+
 static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data)
 {
 	return symtab->add_cgraph_insertion_hook(hook, data);
diff --git a/scripts/gcc-plugins/initify_plugin.c b/scripts/gcc-plugins/initify_plugin.c
new file mode 100644
index 000000000000..354caf2a724a
--- /dev/null
+++ b/scripts/gcc-plugins/initify_plugin.c
@@ -0,0 +1,1883 @@
+/*
+ * Copyright 2015-2016 by Emese Revfy <re.emese@gmail.com>
+ * Licensed under the GPL v2
+ *
+ * Homepage:
+ * https://github.com/ephox-gcc-plugins/initify
+ *
+ * This plugin has two passes. The first one tries to find all functions that
+ * can be become __init/__exit. The second one moves string constants
+ * (local variables and function string arguments marked by
+ * the nocapture attribute) only referenced in __init/__exit functions
+ * to __initconst/__exitconst sections.
+ * Based on an idea from Mathias Krause <minipli@ld-linux.so>.
+ *
+ * The instrumentation pass of the latent_entropy plugin must run after
+ * the initify plugin to increase coverage.
+ *
+ * Options:
+ * -fplugin-arg-initify_plugin-disable
+ * -fplugin-arg-initify_plugin-verbose
+ * -fplugin-arg-initify_plugin-print_missing_attr
+ * -fplugin-arg-initify_plugin-search_init_exit_functions
+ * -fplugin-arg-initify_plugin-enable_init_to_exit_moves
+ * -fplugin-arg-initify_plugin-disable_verify_nocapture_functions
+ *
+ * Attribute: __attribute__((nocapture(x, y ...)))
+ *  The nocapture gcc attribute can be on functions only.
+ *  The attribute takes one or more unsigned integer constants as parameters
+ *  that specify the function argument(s) of const char* type to initify.
+ *  If the marked argument is a vararg then the plugin initifies
+ *  all vararg arguments.
+ *  There can be one negative value which means that the return of the function
+ *  will be followed to find it is a nocapture attribute or not.
+ *
+ * Attribute: __attribute__((unverified_nocapture(x, y ...)))
+ *  This attribute disables the compile data flow verification of the designated
+ *  nocapture parameters of the function. Use it only on function parameters
+ *  that are difficult for the plugin to analyze.
+ *
+ * Usage:
+ * $ make
+ * $ make run
+ */
+
+#include "gcc-common.h"
+
+int plugin_is_GPL_compatible;
+
+static struct plugin_info initify_plugin_info = {
+	.version	=	"20161208vanilla",
+	.help		=	"disable\tturn off the initify plugin\n"
+				"verbose\tprint all initified strings and all"
+				" functions which should be __init/__exit\n"
+				"print_missing_attr\tprint functions which"
+				" can be marked by nocapture attribute\n"
+				"search_init_exit_functions\tfind functions"
+				" which should be marked by __init or __exit"
+				" attribute\n"
+				"enable_init_to_exit_moves\tmove a function"
+				" to the exit section if it is called by __init"
+				" and __exit functions too\n"
+				"disable_verify_nocapture_functions\tdisable"
+				" the search of capture uses in nocapture"
+				" functions\n"
+};
+
+#define ARGNUM_NONE 0
+static bool verbose, print_missing_attr, search_init_exit_functions;
+static bool enable_init_to_exit_moves, disable_verify_nocapture_functions;
+
+enum section_type {
+	INIT, EXIT, BOTH, NONE
+};
+
+enum attribute_type {
+	UNVERIFIED, NOCAPTURE, PRINTF, BUILTINS, SYSCALL, NONE_ATTRIBUTE
+};
+
+
+#if BUILDING_GCC_VERSION >= 5000
+typedef struct hash_set<const_gimple> gimple_set;
+
+static inline bool pointer_set_insert(gimple_set *visited, const_gimple stmt)
+{
+	return visited->add(stmt);
+}
+
+static inline gimple_set* pointer_set_create(void)
+{
+	return new hash_set<const_gimple>;
+}
+
+static inline void pointer_set_destroy(gimple_set *visited)
+{
+	delete visited;
+}
+
+typedef struct hash_set<const_tree> tree_set;
+
+static inline bool pointer_set_insert(tree_set *visited, const_tree node)
+{
+	return visited->add(node);
+}
+
+static inline tree_set* tree_pointer_set_create(void)
+{
+	return new hash_set<const_tree>;
+}
+
+static inline void pointer_set_destroy(tree_set *visited)
+{
+	delete visited;
+}
+#else
+typedef struct pointer_set_t gimple_set;
+typedef struct pointer_set_t tree_set;
+
+static inline tree_set *tree_pointer_set_create(void)
+{
+	return pointer_set_create();
+}
+#endif
+
+static gimple initify_get_def_stmt(const_tree node)
+{
+	gcc_assert(node != NULL_TREE);
+
+	if (TREE_CODE(node) != SSA_NAME)
+		return NULL;
+	return SSA_NAME_DEF_STMT(node);
+}
+
+static void search_constant_strings(bool *has_str_cst, gimple_set *visited,
+					tree node);
+static bool has_capture_use_local_var(const_tree vardecl);
+static bool search_capture_ssa_use(gimple_set *visited_defs, tree node);
+
+#define FUNCTION_PTR_P(node) \
+	(TREE_CODE(TREE_TYPE(node)) == POINTER_TYPE && \
+	(TREE_CODE(TREE_TYPE(TREE_TYPE(node))) == FUNCTION_TYPE || \
+	TREE_CODE(TREE_TYPE(TREE_TYPE(node))) == METHOD_TYPE))
+
+static bool is_vararg_arg(tree arg_list, unsigned int num)
+{
+	if (tree_last(arg_list) == void_list_node)
+		return false;
+
+	return num >= (unsigned int)list_length(arg_list);
+}
+
+static const_tree get_ptr_type(const_tree type)
+{
+	gcc_assert(type != NULL_TREE);
+
+	if (TREE_CODE(type) != POINTER_TYPE)
+		return type;
+	return get_ptr_type(TREE_TYPE(type));
+}
+
+static bool check_parameter(tree *node, tree type_args, int idx)
+{
+	const_tree type_arg, type, type_type, type_name, ptr_type;
+
+	if (is_vararg_arg(type_args, idx))
+		return true;
+
+	type_arg = chain_index(idx - 1, type_args);
+	type = TREE_VALUE(type_arg);
+	gcc_assert(type != NULL_TREE);
+	type_type = TREE_TYPE(type);
+	gcc_assert(type_type != NULL_TREE);
+
+	type_name = TYPE_NAME(type_type);
+	if (type_name != NULL_TREE && TREE_CODE(type_name) == IDENTIFIER_NODE
+		&& !strcmp(TYPE_NAME_POINTER(type_type), "va_format"))
+		return true;
+
+	if (TREE_CODE(type) != POINTER_TYPE) {
+		error("%u. parameter of the %qE function must be a pointer",
+								idx, *node);
+		return false;
+	}
+
+	ptr_type = get_ptr_type(type_type);
+	if (!TYPE_READONLY(ptr_type)) {
+		error("%u. parameter of the %qE function must be readonly",
+								idx, *node);
+		return false;
+	}
+
+	if (TREE_THIS_VOLATILE(ptr_type)) {
+		error("%u. parameter of the %qE function can't be volatile",
+								idx, *node);
+		return false;
+	}
+
+	return true;
+}
+
+static bool check_marked_parameters(tree *node, tree type_args, const_tree args,
+								const_tree name)
+{
+	const_tree arg;
+	bool negative_val;
+
+	negative_val = false;
+	for (arg = args; arg; arg = TREE_CHAIN(arg)) {
+		int idx;
+		unsigned int abs_idx;
+		tree position = TREE_VALUE(arg);
+
+		if (TREE_CODE(position) != INTEGER_CST) {
+			error("%qE parameter of the %qE attribute isn't an integer (fn: %qE)",
+							position, name, *node);
+			return false;
+		}
+
+		idx = (int)tree_to_shwi(position);
+		if (negative_val && idx < 0) {
+			error("Only one negative attribute value is supported (attribute: %qE fn: %qE)",
+								name, *node);
+			return false;
+		}
+
+		if (idx < 0)
+			negative_val = true;
+
+		abs_idx = abs(idx);
+		if (abs_idx == 0)
+			continue;
+
+		if (!check_parameter(node, type_args, abs_idx))
+			return false;
+	}
+	return true;
+}
+
+static bool check_all_parameters(tree *node, tree type_args)
+{
+	int arg, len = list_length(type_args);
+
+	if (tree_last(type_args) == void_list_node)
+		len -= 1;
+
+	for (arg = 1; arg <= len; arg++) {
+		if (!check_parameter(node, type_args, arg))
+			return false;
+	}
+	return true;
+}
+
+/* nocapture attribute:
+ *  * to mark nocapture function arguments. If used on a vararg argument
+ *    it applies to all of them that have no other uses.
+ *  * attribute value 0 is ignored to allow reusing print attribute arguments
+ */
+static bool handle_initify_attributes(tree *node, tree name, tree args)
+{
+	tree type_args = NULL_TREE;
+
+	switch (TREE_CODE(*node)) {
+	case FUNCTION_DECL:
+		type_args = TYPE_ARG_TYPES(TREE_TYPE(*node));
+		break;
+
+	case FUNCTION_TYPE:
+	case METHOD_TYPE:
+		type_args = TYPE_ARG_TYPES(*node);
+		break;
+
+	case TYPE_DECL: {
+		enum tree_code fn_code;
+		const_tree fntype = TREE_TYPE(*node);
+
+		fn_code = TREE_CODE(fntype);
+		if (fn_code == POINTER_TYPE)
+			fntype = TREE_TYPE(fntype);
+		fn_code = TREE_CODE(fntype);
+		if (fn_code == FUNCTION_TYPE || fn_code == METHOD_TYPE) {
+			type_args = TYPE_ARG_TYPES(fntype);
+			break;
+		}
+		/* FALLTHROUGH */
+	}
+
+	default:
+		debug_tree(*node);
+		error("%s: %qE attribute only applies to functions",
+			__func__, name);
+		return false;
+	}
+
+	gcc_assert(type_args != NULL_TREE);
+
+	if (!check_marked_parameters(node, type_args, args, name))
+		return false;
+	return args != NULL_TREE || check_all_parameters(node, type_args);
+}
+
+static tree handle_nocapture_attribute(tree *node, tree name, tree args,
+					int __unused flags, bool *no_add_attrs)
+{
+	tree nocapture_attr;
+
+	*no_add_attrs = true;
+
+	if (!handle_initify_attributes(node, name, args))
+		return NULL_TREE;
+
+	nocapture_attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(*node));
+	if (nocapture_attr)
+		chainon(TREE_VALUE(nocapture_attr), args);
+	else
+		*no_add_attrs = false;
+
+	return NULL_TREE;
+}
+
+static tree handle_unverified_nocapture_attribute(tree *node, tree name,
+			tree args, int __unused flags, bool *no_add_attrs)
+{
+	tree unverified_attr;
+
+	*no_add_attrs = true;
+
+	if (!handle_initify_attributes(node, name, args))
+		return NULL_TREE;
+
+	unverified_attr = lookup_attribute("unverified_nocapture", DECL_ATTRIBUTES(*node));
+	if (unverified_attr)
+		chainon(TREE_VALUE(unverified_attr), args);
+	else
+		*no_add_attrs = false;
+
+	return NULL_TREE;
+}
+
+static struct attribute_spec nocapture_attr = {
+	.name				= "nocapture",
+	.min_length			= 0,
+	.max_length			= -1,
+	.decl_required			= true,
+	.type_required			= false,
+	.function_type_required		= false,
+	.handler			= handle_nocapture_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+	.affects_type_identity		= false
+#endif
+};
+
+static struct attribute_spec unverified_nocapture_attr = {
+	.name				= "unverified_nocapture",
+	.min_length			= 0,
+	.max_length			= -1,
+	.decl_required			= true,
+	.type_required			= false,
+	.function_type_required		= false,
+	.handler			= handle_unverified_nocapture_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+	.affects_type_identity		= false
+#endif
+};
+
+static void register_attributes(void __unused *event_data, void __unused *data)
+{
+	register_attribute(&nocapture_attr);
+	register_attribute(&unverified_nocapture_attr);
+}
+
+/* Determine whether the function is in the init or exit sections. */
+static enum section_type get_init_exit_section(const_tree decl)
+{
+	const char *str;
+	const_tree section, attr_value;
+
+	section = lookup_attribute("section", DECL_ATTRIBUTES(decl));
+	if (!section)
+		return NONE;
+
+	attr_value = TREE_VALUE(section);
+	gcc_assert(attr_value != NULL_TREE);
+	gcc_assert(list_length(attr_value) == 1);
+
+	str = TREE_STRING_POINTER(TREE_VALUE(attr_value));
+
+	if (!strncmp(str, ".init.", 6))
+		return INIT;
+	if (!strncmp(str, ".exit.", 6))
+		return EXIT;
+	return NONE;
+}
+
+static tree get_string_cst(tree var)
+{
+	if (var == NULL_TREE)
+		return NULL_TREE;
+
+	if (TREE_CODE(var) == STRING_CST)
+		return var;
+
+	switch (TREE_CODE_CLASS(TREE_CODE(var))) {
+	case tcc_expression:
+	case tcc_reference: {
+		int i;
+
+		for (i = 0; i < TREE_OPERAND_LENGTH(var); i++) {
+			tree ret = get_string_cst(TREE_OPERAND(var, i));
+			if (ret != NULL_TREE)
+				return ret;
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	return NULL_TREE;
+}
+
+static bool set_init_exit_section(tree decl)
+{
+	gcc_assert(DECL_P(decl));
+
+	if (get_init_exit_section(decl) != NONE)
+		return false;
+
+	if (get_init_exit_section(current_function_decl) == INIT)
+		set_decl_section_name(decl, ".init.rodata.str");
+	else
+		set_decl_section_name(decl, ".exit.rodata.str");
+	return true;
+}
+
+/* Syscalls are always nocapture functions. */
+static bool is_syscall(const_tree fn)
+{
+	const char *name = DECL_NAME_POINTER(fn);
+
+	if (!strncmp(name, "sys_", 4))
+		return true;
+
+	if (!strncmp(name, "sys32_", 6))
+		return true;
+
+	if (!strncmp(name, "compat_sys_", 11))
+		return true;
+
+	return false;
+}
+
+/* These builtins are nocapture functions. */
+static bool allowed_builtins(const_tree fn)
+{
+	const char *name = DECL_NAME_POINTER(fn);
+
+	if (!strcmp(name, "__builtin_va_start"))
+		return true;
+	if (!strcmp(name, "__builtin_expect"))
+		return true;
+	if (!strcmp(name, "__builtin_memcpy"))
+		return true;
+	return false;
+}
+
+static enum attribute_type search_argnum_in_attribute_params(const_tree attr,
+					int fn_arg_num, int fntype_arg_len)
+{
+	const_tree attr_val;
+
+	for (attr_val = TREE_VALUE(attr); attr_val;
+					attr_val = TREE_CHAIN(attr_val)) {
+		int attr_arg_val;
+
+		if (TREE_CODE(TREE_VALUE(attr_val)) == IDENTIFIER_NODE)
+			continue;
+
+		attr_arg_val = (int)abs(tree_to_shwi(TREE_VALUE(attr_val)));
+		if (attr_arg_val == fn_arg_num)
+			return NOCAPTURE;
+		if (attr_arg_val > fntype_arg_len && fn_arg_num >= attr_arg_val)
+			return NOCAPTURE;
+	}
+	return NONE_ATTRIBUTE;
+}
+
+/* Check that fn_arg_num is a nocapture argument, handle cloned functions too.*/
+static enum attribute_type lookup_nocapture_argument(const_tree fndecl,
+		const_tree attr, int fn_arg_num, int fntype_arg_len)
+{
+	const_tree orig_decl, clone_arg, orig_arg;
+	tree decl_list, orig_decl_list;
+	enum attribute_type orig_attribute;
+	struct cgraph_node *node = cgraph_get_node(fndecl);
+
+	orig_attribute = search_argnum_in_attribute_params(attr, fn_arg_num,
+							fntype_arg_len);
+	if (orig_attribute == NONE_ATTRIBUTE)
+		return orig_attribute;
+
+	gcc_assert(node);
+	if (node->clone_of && node->clone.tree_map)
+		gcc_assert(!node->clone.args_to_skip);
+
+	if (!DECL_ARTIFICIAL(fndecl)
+		&& DECL_ABSTRACT_ORIGIN(fndecl) == NULL_TREE)
+		return orig_attribute;
+
+	orig_decl = DECL_ABSTRACT_ORIGIN(fndecl);
+	gcc_assert(orig_decl != NULL_TREE);
+
+	decl_list = DECL_ARGUMENTS(fndecl);
+	orig_decl_list = DECL_ARGUMENTS(orig_decl);
+
+	if (decl_list == NULL_TREE || orig_decl_list == NULL_TREE)
+		return NONE_ATTRIBUTE;
+
+	if (list_length(decl_list) == list_length(orig_decl_list))
+		return orig_attribute;
+
+	clone_arg = chain_index(fn_arg_num - 1, decl_list);
+	gcc_assert(clone_arg != NULL_TREE);
+
+	orig_arg = chain_index(fn_arg_num - 1, orig_decl_list);
+	gcc_assert(orig_arg != NULL_TREE);
+
+	if (!strcmp(DECL_NAME_POINTER(clone_arg), DECL_NAME_POINTER(orig_arg)))
+		return orig_attribute;
+	return NONE_ATTRIBUTE;
+}
+
+/* Check whether the function argument is nocapture. */
+static enum attribute_type is_fndecl_nocapture_arg(const_tree fndecl,
+							int fn_arg_num)
+{
+	int fntype_arg_len;
+	const_tree type, attr = NULL_TREE;
+	bool fnptr = FUNCTION_PTR_P(fndecl);
+
+	if (!fnptr && is_syscall(fndecl))
+		return SYSCALL;
+
+	if (!fnptr && DECL_BUILT_IN(fndecl) && allowed_builtins(fndecl))
+		return BUILTINS;
+
+	if (fnptr)
+		type = TREE_TYPE(TREE_TYPE(fndecl));
+	else
+		type = TREE_TYPE(fndecl);
+
+	fntype_arg_len = type_num_arguments(type);
+
+	if (!fnptr)
+		attr = lookup_attribute("unverified_nocapture",
+						DECL_ATTRIBUTES(fndecl));
+	if (attr != NULL_TREE && lookup_nocapture_argument(fndecl, attr,
+				fn_arg_num, fntype_arg_len) != NONE_ATTRIBUTE)
+		return UNVERIFIED;
+
+	attr = lookup_attribute("format", TYPE_ATTRIBUTES(type));
+	if (attr != NULL_TREE &&
+		lookup_nocapture_argument(fndecl, attr, fn_arg_num,
+					fntype_arg_len) != NONE_ATTRIBUTE)
+		return PRINTF;
+
+	if (fnptr)
+		return NONE_ATTRIBUTE;
+
+	attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+	if (attr == NULL_TREE)
+		return NONE_ATTRIBUTE;
+
+	if (TREE_VALUE(attr) == NULL_TREE)
+		return NOCAPTURE;
+
+	return lookup_nocapture_argument(fndecl, attr, fn_arg_num,
+							fntype_arg_len);
+}
+
+/* Check whether arg_num is a nocapture argument that can be returned. */
+static bool is_negative_nocapture_arg(const_tree fndecl, int arg_num)
+{
+	const_tree attr, attr_val;
+
+	gcc_assert(arg_num <= 0);
+
+	if (FUNCTION_PTR_P(fndecl))
+		return false;
+
+	attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+	if (attr == NULL_TREE)
+		return false;
+
+	for (attr_val = TREE_VALUE(attr); attr_val;
+					attr_val = TREE_CHAIN(attr_val)) {
+		int attr_arg_val;
+
+		if (arg_num == 0 &&
+			tree_int_cst_lt(TREE_VALUE(attr_val),
+							integer_zero_node))
+			return true;
+
+		attr_arg_val = (int)tree_to_shwi(TREE_VALUE(attr_val));
+		if (attr_arg_val == arg_num)
+			return true;
+	}
+
+	return false;
+}
+
+static bool is_same_vardecl(const_tree op, const_tree vardecl)
+{
+	const_tree decl;
+
+	if (op == vardecl)
+		return true;
+	if (TREE_CODE(op) == SSA_NAME)
+		decl = SSA_NAME_VAR(op);
+	else
+		decl = op;
+
+	if (decl == NULL_TREE || !DECL_P(decl))
+		return false;
+
+	if (TREE_CODE(decl) != TREE_CODE(vardecl))
+		return false;
+
+	return DECL_NAME(decl) &&
+		!strcmp(DECL_NAME_POINTER(decl), DECL_NAME_POINTER(vardecl));
+}
+
+static bool search_same_vardecl(const_tree value, const_tree vardecl)
+{
+	int i;
+
+	for (i = 0; i < TREE_OPERAND_LENGTH(value); i++) {
+		const_tree op = TREE_OPERAND(value, i);
+
+		if (op == NULL_TREE)
+			continue;
+		if (is_same_vardecl(op, vardecl))
+			return true;
+		if (search_same_vardecl(op, vardecl))
+			return true;
+	}
+	return false;
+}
+
+static bool check_constructor(const_tree constructor, const_tree vardecl)
+{
+	unsigned HOST_WIDE_INT cnt __unused;
+	tree value;
+
+	FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(constructor), cnt, value) {
+		if (TREE_CODE(value) == CONSTRUCTOR)
+			return check_constructor(value, vardecl);
+		if (is_gimple_constant(value))
+			continue;
+
+		gcc_assert(TREE_OPERAND_LENGTH(value) > 0);
+		if (search_same_vardecl(value, vardecl))
+			return true;
+	}
+	return false;
+}
+
+static bool compare_ops(const_tree vardecl, tree op)
+{
+	if (TREE_CODE(op) == TREE_LIST)
+		op = TREE_VALUE(op);
+	if (TREE_CODE(op) == SSA_NAME)
+		op = SSA_NAME_VAR(op);
+	if (op == NULL_TREE)
+		return false;
+
+	switch (TREE_CODE_CLASS(TREE_CODE(op))) {
+	case tcc_declaration:
+		return is_same_vardecl(op, vardecl);
+
+	case tcc_exceptional:
+		return check_constructor(op, vardecl);
+
+	case tcc_constant:
+	case tcc_statement:
+	case tcc_comparison:
+		return false;
+
+	default:
+		break;
+	}
+
+	gcc_assert(TREE_OPERAND_LENGTH(op) > 0);
+	return search_same_vardecl(op, vardecl);
+}
+
+static bool is_stmt_nocapture_arg(const gcall *stmt, int arg_num)
+{
+	tree fndecl;
+
+	fndecl = gimple_call_fndecl(stmt);
+	if (fndecl == NULL_TREE)
+		fndecl = gimple_call_fn(stmt);
+
+	gcc_assert(fndecl != NULL_TREE);
+	if (is_fndecl_nocapture_arg(fndecl, arg_num) != NONE_ATTRIBUTE)
+		return true;
+
+	/*
+	 * These are potentially nocapture functions that must be checked
+	 *  manually.
+	 */
+	if (print_missing_attr)
+		inform(gimple_location(stmt), "nocapture attribute is missing (fn: %E, arg: %u)\n",
+							fndecl, arg_num);
+	return false;
+}
+
+/* Find the argument position of arg. */
+static int get_arg_num(const gcall *call, const_tree arg)
+{
+	int idx;
+
+	for (idx = 0; idx < (int)gimple_call_num_args(call); idx++) {
+		const_tree cur_arg = gimple_call_arg(call, idx);
+
+		if (cur_arg == arg)
+			return idx + 1;
+	}
+
+	debug_tree(arg);
+	debug_gimple_stmt(call);
+	gcc_unreachable();
+}
+
+/* Determine if the variable uses are only in nocapture functions. */
+static bool only_nocapture_call(const_tree decl)
+{
+	struct cgraph_edge *e;
+	struct cgraph_node *caller;
+	bool has_call = false;
+
+	gcc_assert(TREE_CODE(decl) == VAR_DECL);
+
+	caller = cgraph_get_node(current_function_decl);
+	for (e = caller->callees; e; e = e->next_callee) {
+		int idx;
+		const gcall *call = as_a_const_gcall(e->call_stmt);
+
+		for (idx = 0; idx < (int)gimple_call_num_args(call); idx++) {
+			const_tree arg = gimple_call_arg(call, idx);
+
+			if (TREE_CODE(arg) != ADDR_EXPR)
+				continue;
+			if (TREE_OPERAND(arg, 0) != decl)
+				continue;
+
+			has_call = true;
+			if (!is_stmt_nocapture_arg(call, idx + 1))
+				return false;
+		}
+	}
+
+	gcc_assert(has_call);
+	return has_call;
+}
+
+/* Determine if all uses of a va_format typed variable are nocapture. */
+static bool is_va_format_use_nocapture(const_tree node)
+{
+	const_tree decl, type;
+
+	if (TREE_CODE(node) != COMPONENT_REF)
+		return false;
+
+	decl = TREE_OPERAND(node, 0);
+	type = TREE_TYPE(decl);
+	gcc_assert(TREE_CODE(type) == RECORD_TYPE);
+
+	if (!TYPE_NAME(type) || strcmp(TYPE_NAME_POINTER(type), "va_format"))
+		return false;
+
+	return only_nocapture_call(decl);
+}
+
+/*
+ * If there is a cast to integer (from const char) then
+ * it is a nocapture data flow.
+ */
+static bool is_cast_to_integer_type(gassign *assign)
+{
+	const_tree lhs_type, lhs;
+
+	if (!gimple_assign_cast_p(assign))
+		return false;
+
+	lhs = gimple_assign_rhs1(assign);
+	lhs_type = TREE_TYPE(lhs);
+	return TYPE_MODE(lhs_type) != QImode;
+}
+
+/* Search the uses of a return value. */
+static bool is_return_value_captured(gimple_set *visited_defs,
+							const gcall *call)
+{
+	tree ret = gimple_call_lhs(call);
+
+	gcc_assert(ret != NULL_TREE);
+	return search_capture_ssa_use(visited_defs, ret);
+}
+
+/* Check if arg_num is a nocapture argument. */
+static bool is_call_arg_nocapture(gimple_set *visited_defs, const gcall *call,
+								int arg_num)
+{
+	tree fndecl = gimple_call_fndecl(call);
+
+	if (fndecl == NULL_TREE)
+		fndecl = gimple_call_fn(call);
+	gcc_assert(fndecl != NULL_TREE);
+
+	if (is_negative_nocapture_arg(fndecl, -arg_num) &&
+				is_return_value_captured(visited_defs, call))
+		return false;
+
+	return is_stmt_nocapture_arg(call, arg_num);
+}
+
+/* Determine whether the function has at least one nocapture argument. */
+static bool has_nocapture_param(const_tree fndecl)
+{
+	const_tree attr;
+
+	if (fndecl == NULL_TREE)
+		return false;
+
+	if (is_syscall(fndecl))
+		return true;
+
+	attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+	if (attr == NULL_TREE) {
+		const_tree type = TREE_TYPE(fndecl);
+
+		attr = lookup_attribute("format", TYPE_ATTRIBUTES(type));
+	}
+	return attr != NULL_TREE;
+}
+
+static void walk_def_stmt(bool *has_capture_use, gimple_set *visited, tree node)
+{
+	gimple def_stmt;
+	const_tree parm_decl;
+
+	if (*has_capture_use)
+		return;
+
+	if (TREE_CODE(node) != SSA_NAME)
+		goto true_out;
+
+	parm_decl = SSA_NAME_VAR(node);
+	if (parm_decl != NULL_TREE && TREE_CODE(parm_decl) == PARM_DECL)
+		return;
+
+	def_stmt = initify_get_def_stmt(node);
+	if (pointer_set_insert(visited, def_stmt))
+		return;
+
+	switch (gimple_code(def_stmt)) {
+	case GIMPLE_CALL: {
+		tree fndecl = gimple_call_fndecl(def_stmt);
+
+		if (fndecl == NULL_TREE)
+			fndecl = gimple_call_fn(def_stmt);
+
+		gcc_assert(fndecl != NULL_TREE);
+		if (has_nocapture_param(fndecl))
+			goto true_out;
+		return;
+	}
+
+	case GIMPLE_ASM:
+	case GIMPLE_ASSIGN:
+		goto true_out;
+
+	case GIMPLE_NOP:
+		return;
+
+	case GIMPLE_PHI: {
+		unsigned int i;
+
+		for (i = 0; i < gimple_phi_num_args(def_stmt); i++) {
+			tree arg = gimple_phi_arg_def(def_stmt, i);
+
+			walk_def_stmt(has_capture_use, visited, arg);
+		}
+		return;
+	}
+
+	default:
+		debug_gimple_stmt(def_stmt);
+		error("%s: unknown gimple code", __func__);
+		gcc_unreachable();
+	}
+	gcc_unreachable();
+
+true_out:
+	*has_capture_use = true;
+}
+
+static bool search_return_capture_use(const greturn *ret_stmt)
+{
+	gimple_set *def_visited;
+	tree ret;
+	bool has_capture_use;
+
+	if (is_negative_nocapture_arg(current_function_decl, 0))
+		return false;
+
+	def_visited = pointer_set_create();
+	ret = gimple_return_retval(ret_stmt);
+	has_capture_use = false;
+	walk_def_stmt(&has_capture_use, def_visited, ret);
+	pointer_set_destroy(def_visited);
+
+	return has_capture_use;
+}
+
+static bool lhs_is_a_nocapture_parm_decl(const_tree lhs)
+{
+	int arg_idx, len;
+	tree arg_list;
+
+	if (TREE_CODE(lhs) != PARM_DECL)
+		return false;
+
+	arg_list = DECL_ARGUMENTS(current_function_decl);
+	len = list_length(arg_list);
+
+	for (arg_idx = 0; arg_idx < len; arg_idx++) {
+		const_tree arg = chain_index(arg_idx, arg_list);
+
+		if (arg == lhs)
+			return is_fndecl_nocapture_arg(current_function_decl,
+						arg_idx + 1) != NONE_ATTRIBUTE;
+	}
+
+	debug_tree(current_function_decl);
+	debug_tree(lhs);
+	gcc_unreachable();
+}
+
+static void has_capture_use_ssa_var(bool *has_capture_use,
+		gimple_set *visited_defs, tree_set *use_visited, tree node)
+{
+	imm_use_iterator imm_iter;
+	use_operand_p use_p;
+
+	if (pointer_set_insert(use_visited, node))
+		return;
+
+	if (*has_capture_use)
+		return;
+
+	if (is_va_format_use_nocapture(node))
+		return;
+
+	if (lhs_is_a_nocapture_parm_decl(node))
+		return;
+
+	if (TREE_CODE(node) != SSA_NAME)
+		goto true_out;
+
+	FOR_EACH_IMM_USE_FAST(use_p, imm_iter, node) {
+		gimple use_stmt = USE_STMT(use_p);
+
+		if (use_stmt == NULL)
+			return;
+		if (is_gimple_debug(use_stmt))
+			continue;
+
+		if (pointer_set_insert(visited_defs, use_stmt))
+			continue;
+
+		switch (gimple_code(use_stmt)) {
+		case GIMPLE_COND:
+		case GIMPLE_SWITCH:
+			return;
+
+		case GIMPLE_ASM:
+			goto true_out;
+
+		case GIMPLE_CALL: {
+			const gcall *call = as_a_const_gcall(use_stmt);
+			int arg_num = get_arg_num(call, node);
+
+			if (is_call_arg_nocapture(visited_defs, call, arg_num))
+				return;
+			goto true_out;
+		}
+
+		case GIMPLE_ASSIGN: {
+			tree lhs;
+			gassign *assign = as_a_gassign(use_stmt);
+			const_tree rhs = gimple_assign_rhs1(assign);
+
+			if (TREE_CODE(rhs) == INDIRECT_REF)
+				return;
+#if BUILDING_GCC_VERSION >= 4006
+			if (TREE_CODE(rhs) == MEM_REF)
+				return;
+#endif
+			if (is_cast_to_integer_type(assign))
+				return;
+
+			lhs = gimple_assign_lhs(assign);
+			has_capture_use_ssa_var(has_capture_use, visited_defs,
+							use_visited, lhs);
+			return;
+		}
+
+		case GIMPLE_PHI: {
+			tree result = gimple_phi_result(use_stmt);
+
+			has_capture_use_ssa_var(has_capture_use, visited_defs,
+							use_visited, result);
+			return;
+		}
+
+		case GIMPLE_RETURN: {
+			const greturn *ret_stmt = as_a_const_greturn(use_stmt);
+
+			if (search_return_capture_use(ret_stmt))
+				goto true_out;
+			return;
+		}
+
+		default:
+			debug_tree(node);
+			debug_gimple_stmt(use_stmt);
+			gcc_unreachable();
+		}
+	}
+	return;
+
+true_out:
+	*has_capture_use = true;
+}
+
+static bool search_capture_ssa_use(gimple_set *visited_defs, tree node)
+{
+	tree_set *use_visited;
+	bool has_capture_use = false;
+
+	use_visited = tree_pointer_set_create();
+	has_capture_use_ssa_var(&has_capture_use, visited_defs, use_visited,
+									node);
+	pointer_set_destroy(use_visited);
+
+	return has_capture_use;
+}
+
+static bool search_capture_use(const_tree vardecl, gimple stmt)
+{
+	unsigned int i;
+	gimple_set *visited_defs = pointer_set_create();
+
+	for (i = 0; i < gimple_num_ops(stmt); i++) {
+		int arg_num;
+		tree op = *(gimple_op_ptr(stmt, i));
+
+		if (op == NULL_TREE)
+			continue;
+		if (is_gimple_constant(op))
+			continue;
+
+		if (!compare_ops(vardecl, op))
+			continue;
+
+		switch (gimple_code(stmt)) {
+		case GIMPLE_COND:
+			break;
+
+		case GIMPLE_ASM:
+			gcc_assert(get_init_exit_section(vardecl) == NONE);
+			goto true_out;
+
+		case GIMPLE_CALL:
+			if (i == 0)
+				break;
+			/* return, fndecl */
+			gcc_assert(i >= 3);
+			arg_num = i - 2;
+
+			if (is_call_arg_nocapture(visited_defs,
+					as_a_const_gcall(stmt), arg_num))
+				break;
+			goto true_out;
+
+		case GIMPLE_ASSIGN: {
+			tree lhs;
+			const_tree rhs = gimple_assign_rhs1(stmt);
+
+			if (TREE_CODE(rhs) == INDIRECT_REF)
+				break;
+#if BUILDING_GCC_VERSION >= 4006
+			if (TREE_CODE(rhs) == MEM_REF)
+				break;
+#endif
+			lhs = gimple_assign_lhs(stmt);
+			if (lhs_is_a_nocapture_parm_decl(lhs))
+				break;
+
+			if (!search_capture_ssa_use(visited_defs, lhs))
+				break;
+			gcc_assert(get_init_exit_section(vardecl) == NONE);
+			goto true_out;
+		}
+
+		case GIMPLE_RETURN:
+			if (search_return_capture_use(as_a_const_greturn(stmt)))
+				goto true_out;
+			break;
+		default:
+			debug_tree(vardecl);
+			debug_gimple_stmt(stmt);
+			gcc_unreachable();
+		}
+	}
+
+	pointer_set_destroy(visited_defs);
+	return false;
+
+true_out:
+	pointer_set_destroy(visited_defs);
+	return true;
+
+}
+
+/* Check all initialized local variables for nocapture uses. */
+static bool is_in_capture_init(const_tree vardecl)
+{
+	unsigned int i __unused;
+	tree var;
+
+	if (TREE_CODE(vardecl) == PARM_DECL)
+		return false;
+
+	FOR_EACH_LOCAL_DECL(cfun, i, var) {
+		const_tree type, initial = DECL_INITIAL(var);
+
+		if (DECL_EXTERNAL(var))
+			continue;
+		if (initial == NULL_TREE)
+			continue;
+		if (TREE_CODE(initial) != CONSTRUCTOR)
+			continue;
+
+		type = TREE_TYPE(var);
+		gcc_assert(TREE_CODE(type) == RECORD_TYPE || DECL_P(var));
+		if (check_constructor(initial, vardecl))
+			return true;
+	}
+	return false;
+}
+
+static bool has_capture_use_local_var(const_tree vardecl)
+{
+	basic_block bb;
+	enum tree_code code = TREE_CODE(vardecl);
+
+	gcc_assert(code == VAR_DECL || code == PARM_DECL);
+
+	if (is_in_capture_init(vardecl))
+		return true;
+
+	FOR_EACH_BB_FN(bb, cfun) {
+		gimple_stmt_iterator gsi;
+
+		for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+			if (search_capture_use(vardecl, gsi_stmt(gsi)))
+				return true;
+		}
+	}
+
+	return false;
+}
+
+/* Search local variables that have only nocapture uses. */
+static void find_local_str(void)
+{
+	unsigned int i __unused;
+	tree var;
+
+	FOR_EACH_LOCAL_DECL(cfun, i, var) {
+		tree str, init_val;
+
+		if (TREE_CODE(TREE_TYPE(var)) != ARRAY_TYPE)
+			continue;
+
+		init_val = DECL_INITIAL(var);
+		if (init_val == NULL_TREE || init_val == error_mark_node)
+			continue;
+		if (TREE_CODE(init_val) != STRING_CST)
+			continue;
+
+		if (has_capture_use_local_var(var))
+			continue;
+
+		str = get_string_cst(init_val);
+		gcc_assert(str);
+
+		if (set_init_exit_section(var) && verbose)
+			inform(DECL_SOURCE_LOCATION(var), "initified local var: %s: %s",
+				DECL_NAME_POINTER(current_function_decl),
+				TREE_STRING_POINTER(str));
+	}
+}
+
+static tree create_decl(tree node)
+{
+	tree str, decl, type, name, type_type;
+	location_t loc;
+
+	str = get_string_cst(node);
+	type = TREE_TYPE(str);
+	gcc_assert(TREE_CODE(type) == ARRAY_TYPE);
+
+	type_type = TREE_TYPE(type);
+	gcc_assert(type_type != NULL_TREE &&
+					TREE_CODE(type_type) == INTEGER_TYPE);
+
+	name = create_tmp_var_name("initify");
+	loc = DECL_SOURCE_LOCATION(current_function_decl);
+	decl = build_decl(loc, VAR_DECL, name, type);
+
+	DECL_INITIAL(decl) = str;
+	DECL_CONTEXT(decl) = current_function_decl;
+	DECL_ARTIFICIAL(decl) = 1;
+
+	TREE_STATIC(decl) = 1;
+	TREE_READONLY(decl) = 1;
+	TREE_ADDRESSABLE(decl) = 1;
+	TREE_USED(decl) = 1;
+
+	add_referenced_var(decl);
+	add_local_decl(cfun, decl);
+
+	varpool_add_new_variable(decl);
+	varpool_mark_needed_node(varpool_node(decl));
+
+	DECL_CHAIN(decl) = BLOCK_VARS(DECL_INITIAL(current_function_decl));
+	BLOCK_VARS(DECL_INITIAL(current_function_decl)) = decl;
+
+	return build_fold_addr_expr_loc(loc, decl);
+}
+
+static void set_section_call_assign(gimple stmt, tree node, unsigned int num)
+{
+	tree decl;
+
+	decl = create_decl(node);
+
+	switch (gimple_code(stmt)) {
+	case GIMPLE_ASSIGN:
+		gcc_assert(gimple_num_ops(stmt) == 2);
+		gimple_assign_set_rhs1(stmt, decl);
+		break;
+
+	case GIMPLE_CALL:
+		gimple_call_set_arg(stmt, num, decl);
+		break;
+
+	default:
+		debug_gimple_stmt(stmt);
+		error("%s: unknown gimple code", __func__);
+		gcc_unreachable();
+	}
+
+	update_stmt(stmt);
+
+	if (set_init_exit_section(TREE_OPERAND(decl, 0)) && verbose)
+		inform(gimple_location(stmt), "initified function arg: %E: [%E]",
+				current_function_decl, get_string_cst(node));
+}
+
+static tree initify_create_new_var(tree type)
+{
+	tree new_var = create_tmp_var(type, "initify");
+
+	add_referenced_var(new_var);
+	mark_sym_for_renaming(new_var);
+	return new_var;
+}
+
+static void initify_create_new_phi_arg(gimple_set *visited_defs, tree ssa_var,
+						gphi *stmt, unsigned int i)
+{
+	gassign *assign;
+	gimple_stmt_iterator gsi;
+	basic_block arg_bb;
+	tree decl, arg;
+	const_tree str;
+	location_t loc;
+
+	arg = gimple_phi_arg_def(stmt, i);
+
+	if (search_capture_ssa_use(visited_defs, arg))
+		return;
+
+	decl = create_decl(arg);
+
+	assign = gimple_build_assign(ssa_var, decl);
+
+	arg_bb = gimple_phi_arg_edge(stmt, i)->src;
+	gcc_assert(arg_bb->index != 0);
+
+	gsi = gsi_after_labels(arg_bb);
+	gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
+	update_stmt(assign);
+
+	if (!set_init_exit_section(TREE_OPERAND(decl, 0)) || !verbose)
+		return;
+
+	loc = gimple_location(stmt);
+	str = get_string_cst(arg);
+	inform(loc, "initified local var, phi arg: %E: [%E]",
+						current_function_decl, str);
+}
+
+static void set_section_phi(bool *has_str_cst, gimple_set *visited, gphi *stmt)
+{
+	tree result, ssa_var;
+	unsigned int i;
+
+	result = gimple_phi_result(stmt);
+	ssa_var = initify_create_new_var(TREE_TYPE(result));
+
+	for (i = 0; i < gimple_phi_num_args(stmt); i++) {
+		tree arg = gimple_phi_arg_def(stmt, i);
+
+		if (get_string_cst(arg) == NULL_TREE)
+			search_constant_strings(has_str_cst, visited, arg);
+		else
+			initify_create_new_phi_arg(visited, ssa_var, stmt, i);
+	}
+}
+
+static void search_constant_strings(bool *has_str_cst, gimple_set *visited,
+								tree node)
+{
+	gimple def_stmt;
+	const_tree parm_decl;
+
+	if (!*has_str_cst)
+		return;
+
+	if (TREE_CODE(node) != SSA_NAME)
+		goto false_out;
+
+	parm_decl = SSA_NAME_VAR(node);
+	if (parm_decl != NULL_TREE && TREE_CODE(parm_decl) == PARM_DECL)
+		goto false_out;
+
+	def_stmt = initify_get_def_stmt(node);
+	if (pointer_set_insert(visited, def_stmt))
+		return;
+
+	switch (gimple_code(def_stmt)) {
+	case GIMPLE_NOP:
+	case GIMPLE_CALL:
+	case GIMPLE_ASM:
+	case GIMPLE_RETURN:
+		goto false_out;
+
+	case GIMPLE_PHI:
+		set_section_phi(has_str_cst, visited, as_a_gphi(def_stmt));
+		return;
+
+	case GIMPLE_ASSIGN: {
+		tree rhs1, str;
+
+		if (gimple_num_ops(def_stmt) != 2)
+			goto false_out;
+
+		rhs1 = gimple_assign_rhs1(def_stmt);
+		search_constant_strings(has_str_cst, visited, rhs1);
+		if (!*has_str_cst)
+			return;
+
+		if (search_capture_ssa_use(visited, node))
+			goto false_out;
+
+		str = get_string_cst(rhs1);
+		gcc_assert(str != NULL_TREE);
+		set_section_call_assign(def_stmt, rhs1, 0);
+		return;
+	}
+
+	default:
+		debug_gimple_stmt(def_stmt);
+		error("%s: unknown gimple code", __func__);
+		gcc_unreachable();
+	}
+	gcc_unreachable();
+
+false_out:
+	*has_str_cst = false;
+}
+
+/* Search constant strings assigned to variables. */
+static void search_var_param(gcall *stmt)
+{
+	int num;
+	gimple_set *visited = pointer_set_create();
+
+	pointer_set_insert(visited, stmt);
+
+	for (num = 0; num < (int)gimple_call_num_args(stmt); num++) {
+		const_tree type, fndecl;
+		bool has_str_cst = true;
+		tree str, arg = gimple_call_arg(stmt, num);
+
+		str = get_string_cst(arg);
+		if (str != NULL_TREE)
+			continue;
+
+		if (TREE_CODE(TREE_TYPE(arg)) != POINTER_TYPE)
+			continue;
+		type = TREE_TYPE(TREE_TYPE(arg));
+		if (!TYPE_STRING_FLAG(type))
+			continue;
+
+		fndecl = gimple_call_fndecl(stmt);
+		if (is_negative_nocapture_arg(fndecl, -(num + 1)) &&
+				is_return_value_captured(visited, stmt))
+			continue;
+
+		if (is_fndecl_nocapture_arg(fndecl, num + 1) != NONE_ATTRIBUTE)
+			search_constant_strings(&has_str_cst, visited, arg);
+	}
+
+	pointer_set_destroy(visited);
+}
+
+/* Search constant strings passed as arguments. */
+static void search_str_param(gcall *stmt)
+{
+	int num;
+	gimple_set *visited = pointer_set_create();
+
+	pointer_set_insert(visited, stmt);
+
+	for (num = 0; num < (int)gimple_call_num_args(stmt); num++) {
+		const_tree fndecl;
+		tree str, arg = gimple_call_arg(stmt, num);
+
+		str = get_string_cst(arg);
+		if (str == NULL_TREE)
+			continue;
+
+		fndecl = gimple_call_fndecl(stmt);
+		if (is_negative_nocapture_arg(fndecl, -(num + 1)) &&
+				is_return_value_captured(visited, stmt))
+			continue;
+
+		if (is_fndecl_nocapture_arg(fndecl, num + 1) != NONE_ATTRIBUTE)
+			set_section_call_assign(stmt, arg, num);
+	}
+
+	pointer_set_destroy(visited);
+}
+
+/* Search constant strings in arguments of nocapture functions. */
+static void search_const_strs(void)
+{
+	basic_block bb;
+
+	FOR_EACH_BB_FN(bb, cfun) {
+		gimple_stmt_iterator gsi;
+
+		for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+			gcall *call_stmt;
+			gimple stmt = gsi_stmt(gsi);
+
+			if (!is_gimple_call(stmt))
+				continue;
+
+			call_stmt = as_a_gcall(stmt);
+			if (!has_nocapture_param(gimple_call_fndecl(call_stmt)))
+				continue;
+			search_str_param(call_stmt);
+			search_var_param(call_stmt);
+		}
+	}
+}
+
+/*
+ * Verify the data flows of the uses of function arguments marked by the
+ * nocapture attribute. The printf attribute is ignored temporarily.
+ */
+static void verify_nocapture_functions(void)
+{
+	int i, len;
+	tree arg_list;
+
+	if (disable_verify_nocapture_functions)
+		return;
+
+	if (is_syscall(current_function_decl))
+		return;
+
+	if (!has_nocapture_param(current_function_decl))
+		return;
+
+	arg_list = DECL_ARGUMENTS(current_function_decl);
+	len = list_length(arg_list);
+	for (i = 0; i < len; i++) {
+		const_tree arg;
+
+		if (is_fndecl_nocapture_arg(current_function_decl, i + 1) !=
+								NOCAPTURE)
+			continue;
+
+		arg = chain_index(i, arg_list);
+		gcc_assert(arg != NULL_TREE);
+
+		if (has_capture_use_local_var(arg)) {
+			warning(0, "%qE captures its %u (%qD) parameter, please remove it from the nocapture attribute.",
+					current_function_decl, i + 1, arg);
+		}
+	}
+}
+
+/*
+ * Find and move constant strings to the proper init or exit read-only
+ * data section.
+ */
+static unsigned int initify_function_transform(
+					struct cgraph_node *node __unused)
+{
+	verify_nocapture_functions();
+
+	if (get_init_exit_section(current_function_decl) == NONE)
+		return 0;
+
+	find_local_str();
+	search_const_strs();
+
+	return TODO_dump_func | TODO_verify_ssa | TODO_verify_stmts
+		| TODO_remove_unused_locals | TODO_cleanup_cfg
+		| TODO_ggc_collect | TODO_verify_flow | TODO_update_ssa;
+}
+
+static const char * section_type_name(struct cgraph_node *node)
+{
+	enum section_type section;
+
+	section = (enum section_type)(unsigned long)NODE_SYMBOL(node)->aux;
+	switch (section) {
+	case INIT:
+		return "init";
+	case EXIT:
+		return "exit";
+	case BOTH:
+		return "init and exit";
+	case NONE:
+		return "none";
+	default:
+		return "imposible";
+	}
+}
+
+static void __unused debug_print_section_type(struct cgraph_node *node)
+{
+	fprintf(stderr, "%s\n", section_type_name(node));
+}
+
+/*
+ * If the function is called by only __init/__exit functions then it can become
+ * an __init/__exit function as well.
+ */
+static bool should_init_exit(struct cgraph_node *callee)
+{
+	struct cgraph_edge *e;
+	bool only_init_callers;
+	const_tree callee_decl = NODE_DECL(callee);
+
+	if (NODE_SYMBOL(callee)->aux != (void *)NONE)
+		return false;
+	if (get_init_exit_section(callee_decl) != NONE)
+		return false;
+
+	/* If gcc isn't in LTO mode then we can handle only static functions. */
+	if (!in_lto_p && TREE_PUBLIC(callee_decl))
+		return false;
+
+	if (NODE_SYMBOL(callee)->address_taken)
+		return false;
+
+	e = callee->callers;
+	if (!e)
+		return false;
+
+	only_init_callers = true;
+	for (; e; e = e->next_caller) {
+		enum section_type caller_section;
+		struct cgraph_node *caller = e->caller;
+
+		caller_section = get_init_exit_section(NODE_DECL(caller));
+		if (caller_section == NONE &&
+			NODE_SYMBOL(caller)->aux == (void *)NONE)
+			only_init_callers = false;
+	}
+
+	return only_init_callers;
+}
+
+static bool inherit_section(struct cgraph_node *callee,
+				struct cgraph_node *caller,
+				enum section_type curfn_section)
+{
+	if (curfn_section == NONE)
+		curfn_section = (enum section_type)(unsigned long)
+					NODE_SYMBOL(caller)->aux;
+
+	if (curfn_section == INIT && NODE_SYMBOL(callee)->aux == (void *)EXIT)
+		goto both_section;
+
+	if (curfn_section == EXIT && NODE_SYMBOL(callee)->aux == (void *)INIT)
+		goto both_section;
+
+	if (!should_init_exit(callee))
+		return false;
+
+	gcc_assert(NODE_SYMBOL(callee)->aux == (void *)NONE);
+	NODE_SYMBOL(callee)->aux = (void *)curfn_section;
+	return true;
+
+both_section:
+	NODE_SYMBOL(callee)->aux = (void *)BOTH;
+	return true;
+}
+
+/*
+ * Try to propagate __init/__exit to callees in __init/__exit functions.
+ * If a function is called by __init and __exit functions as well then it can be
+ * an __exit function at most.
+ */
+static bool search_init_exit_callers(void)
+{
+	struct cgraph_node *node;
+	bool change = false;
+
+	FOR_EACH_FUNCTION(node) {
+		struct cgraph_edge *e;
+		enum section_type section;
+		const_tree cur_fndecl = NODE_DECL(node);
+
+		if (DECL_BUILT_IN(cur_fndecl))
+			continue;
+
+		section = get_init_exit_section(cur_fndecl);
+		if (section == NONE && NODE_SYMBOL(node)->aux == (void *)NONE)
+			continue;
+
+		for (e = node->callees; e; e = e->next_callee) {
+			if (e->callee->global.inlined_to)
+				continue;
+
+			if (inherit_section(e->callee, node, section))
+				change = true;
+		}
+	}
+
+	return change;
+}
+
+/* We can't move functions to the init/exit sections from certain sections. */
+static bool can_move_to_init_exit(const_tree fndecl)
+{
+	const char *section_name = get_decl_section_name(fndecl);
+
+	if (!section_name)
+		return true;
+
+	if (!strcmp(section_name, ".ref.text"))
+		return false;
+
+	if (!strcmp(section_name, ".meminit.text"))
+		return false;
+
+	inform(DECL_SOURCE_LOCATION(fndecl), "Section of %qE: %s\n",
+						fndecl, section_name);
+	gcc_unreachable();
+}
+
+static void move_function_to_init_exit_text(struct cgraph_node *node)
+{
+	const char *section_name;
+	tree id, attr;
+	tree section_str, attr_args, fndecl = NODE_DECL(node);
+
+	/*
+	 * If the function is a candidate for both __init and __exit and enable
+	 * _init_to_exit_moves is false then these functions arent't moved
+	 * to the exit section.
+	 */
+	if (NODE_SYMBOL(node)->aux == (void *)BOTH) {
+		if (enable_init_to_exit_moves)
+			NODE_SYMBOL(node)->aux = (void *)EXIT;
+		else
+			return;
+	}
+
+	if (NODE_SYMBOL(node)->aux == (void *)NONE)
+		return;
+
+	if (!can_move_to_init_exit(fndecl))
+		return;
+
+	if (verbose) {
+		const char *attr_name;
+		location_t loc = DECL_SOURCE_LOCATION(fndecl);
+
+		attr_name = NODE_SYMBOL(node)->aux ==
+					(void *)INIT ? "__init" : "__exit";
+
+		if (in_lto_p && TREE_PUBLIC(fndecl))
+			inform(loc, "%s attribute is missing from the %qE function (public)",
+							attr_name, fndecl);
+
+		if (!in_lto_p && !TREE_PUBLIC(fndecl))
+			inform(loc, "%s attribute is missing from the %qE function (static)",
+							attr_name, fndecl);
+	}
+
+	if (in_lto_p)
+		return;
+
+	/* Add the init/exit section attribute to the function declaration. */
+	DECL_ATTRIBUTES(fndecl) = copy_list(DECL_ATTRIBUTES(fndecl));
+
+	section_name = NODE_SYMBOL(node)->aux ==
+				(void *)INIT ? ".init.text" : ".exit.text";
+	section_str = build_string(strlen(section_name) + 1, section_name);
+	TREE_READONLY(section_str) = 1;
+	TREE_STATIC(section_str) = 1;
+	attr_args = build_tree_list(NULL_TREE, section_str);
+
+	id = get_identifier("__section__");
+	attr = DECL_ATTRIBUTES(fndecl);
+	DECL_ATTRIBUTES(fndecl) = tree_cons(id, attr_args, attr);
+
+#if BUILDING_GCC_VERSION < 5000
+	DECL_SECTION_NAME(fndecl) = section_str;
+#endif
+	set_decl_section_name(fndecl, section_name);
+}
+
+/* Find all functions that can become __init/__exit functions */
+static unsigned int initify_execute(void)
+{
+	struct cgraph_node *node;
+
+	if (!search_init_exit_functions)
+		return 0;
+
+	if (flag_lto && !in_lto_p)
+		return 0;
+
+	FOR_EACH_FUNCTION(node)
+		NODE_SYMBOL(node)->aux = (void *)NONE;
+
+	while (search_init_exit_callers()) {};
+
+	FOR_EACH_FUNCTION(node) {
+		move_function_to_init_exit_text(node);
+
+		NODE_SYMBOL(node)->aux = NULL;
+	}
+
+	return 0;
+}
+
+#define PASS_NAME initify
+#define NO_WRITE_SUMMARY
+#define NO_GENERATE_SUMMARY
+#define NO_READ_SUMMARY
+#define NO_READ_OPTIMIZATION_SUMMARY
+#define NO_WRITE_OPTIMIZATION_SUMMARY
+#define NO_STMT_FIXUP
+#define NO_VARIABLE_TRANSFORM
+#define NO_GATE
+
+#include "gcc-generate-ipa-pass.h"
+
+static unsigned int (*old_section_type_flags)(tree decl, const char *name,
+								int reloc);
+
+static unsigned int initify_section_type_flags(tree decl, const char *name,
+								int reloc)
+{
+	if (!strcmp(name, ".init.rodata.str") ||
+					!strcmp(name, ".exit.rodata.str")) {
+		gcc_assert(TREE_CODE(decl) == VAR_DECL);
+		gcc_assert(DECL_INITIAL(decl));
+		gcc_assert(TREE_CODE(DECL_INITIAL(decl)) == STRING_CST);
+
+		return 1 | SECTION_MERGE | SECTION_STRINGS;
+	}
+
+	return old_section_type_flags(decl, name, reloc);
+}
+
+static void initify_start_unit(void __unused *gcc_data,
+						void __unused *user_data)
+{
+	old_section_type_flags = targetm.section_type_flags;
+	targetm.section_type_flags = initify_section_type_flags;
+}
+
+int plugin_init(struct plugin_name_args *plugin_info,
+					struct plugin_gcc_version *version)
+{
+	struct register_pass_info initify_info;
+	int i;
+	const int argc = plugin_info->argc;
+	bool enabled = true;
+	const struct plugin_argument * const argv = plugin_info->argv;
+	const char * const plugin_name = plugin_info->base_name;
+
+	initify_info.pass				= make_initify_pass();
+	initify_info.reference_pass_name		= "inline";
+	initify_info.ref_pass_instance_number		= 1;
+	initify_info.pos_op				= PASS_POS_INSERT_AFTER;
+
+	if (!plugin_default_version_check(version, &gcc_version)) {
+		error(G_("incompatible gcc/plugin versions"));
+		return 1;
+	}
+
+	for (i = 0; i < argc; ++i) {
+		if (!(strcmp(argv[i].key, "disable"))) {
+			enabled = false;
+			continue;
+		}
+		if (!strcmp(argv[i].key, "verbose")) {
+			verbose = true;
+			continue;
+		}
+		if (!strcmp(argv[i].key, "print_missing_attr")) {
+			print_missing_attr = true;
+			continue;
+		}
+		if (!strcmp(argv[i].key, "search_init_exit_functions")) {
+			search_init_exit_functions = true;
+			continue;
+		}
+		if (!strcmp(argv[i].key, "enable_init_to_exit_moves")) {
+			enable_init_to_exit_moves = true;
+			continue;
+		}
+
+		if (!strcmp(argv[i].key, "disable_verify_nocapture_functions")) {
+			disable_verify_nocapture_functions = true;
+			continue;
+		}
+
+		error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name,
+								argv[i].key);
+	}
+
+	register_callback(plugin_name, PLUGIN_INFO, NULL, &initify_plugin_info);
+	if (enabled) {
+		register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+								&initify_info);
+		register_callback(plugin_name, PLUGIN_START_UNIT,
+						initify_start_unit, NULL);
+	}
+	register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
+									NULL);
+
+	return 0;
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 2/4] util: Move type casts into is_kernel_rodata
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
  2016-12-16 22:06 ` [PATCH v4 1/4] gcc-plugins: Add " Kees Cook
@ 2016-12-16 22:06 ` Kees Cook
  2016-12-16 22:06 ` [PATCH v4 3/4] initify: Mark functions with the __nocapture attribute Kees Cook
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:06 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, Emese Revfy, linux-kernel, Arnd Bergmann,
	Josh Triplett, pageexec, spender, mmarek, yamada.masahiro,
	linux-kbuild, minipli, linux, catalin.marinas, linux,
	david.brown, benh, tglx, akpm, jlayton, sam

From: Emese Revfy <re.emese@gmail.com>

This moves type casts into the is_kernel_rodata() function itself so that
parameters can be marked as "nocapture" in the coming initify gcc plugin.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
[kees: expanded commit message]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 mm/util.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/mm/util.c b/mm/util.c
index 1a41553db866..5606c6b74eee 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -17,10 +17,10 @@
 
 #include "internal.h"
 
-static inline int is_kernel_rodata(unsigned long addr)
+static inline int is_kernel_rodata(const void *addr)
 {
-	return addr >= (unsigned long)__start_rodata &&
-		addr < (unsigned long)__end_rodata;
+	return (unsigned long)addr >= (unsigned long)__start_rodata &&
+		(unsigned long)addr < (unsigned long)__end_rodata;
 }
 
 /**
@@ -31,7 +31,7 @@ static inline int is_kernel_rodata(unsigned long addr)
  */
 void kfree_const(const void *x)
 {
-	if (!is_kernel_rodata((unsigned long)x))
+	if (!is_kernel_rodata(x))
 		kfree(x);
 }
 EXPORT_SYMBOL(kfree_const);
@@ -68,7 +68,7 @@ EXPORT_SYMBOL(kstrdup);
  */
 const char *kstrdup_const(const char *s, gfp_t gfp)
 {
-	if (is_kernel_rodata((unsigned long)s))
+	if (is_kernel_rodata(s))
 		return s;
 
 	return kstrdup(s, gfp);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 3/4] initify: Mark functions with the __nocapture attribute
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
  2016-12-16 22:06 ` [PATCH v4 1/4] gcc-plugins: Add " Kees Cook
  2016-12-16 22:06 ` [PATCH v4 2/4] util: Move type casts into is_kernel_rodata Kees Cook
@ 2016-12-16 22:06 ` Kees Cook
  2016-12-16 22:06 ` [PATCH v4 4/4] initify: Mark functions with the __unverified_nocapture attribute Kees Cook
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:06 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, Emese Revfy, linux-kernel, Arnd Bergmann,
	Josh Triplett, pageexec, spender, mmarek, yamada.masahiro,
	linux-kbuild, minipli, linux, catalin.marinas, linux,
	david.brown, benh, tglx, akpm, jlayton, sam

From: Emese Revfy <re.emese@gmail.com>

The nocapture gcc attribute can be on functions only. The attribute
takes zero or more signed integer constants as parameters that specify
the function parameters to initify when the passed arguments are of const
char* type.

A negative attribute parameter value means that the corresponding function
parameter is returned by the function and the passed argument will only be
initified if the data flow of the returned value is not captured in the caller.

If no values are passed to the attribute then all function parameters are
treated as nocapture.

If the marked parameter is a vararg then the plugin initifies all vararg
arguments.

I couldn't test the arm, arm64 and powerpc architectures.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/arm/include/asm/string.h     | 10 +++---
 arch/arm64/include/asm/string.h   | 25 ++++++-------
 arch/powerpc/include/asm/string.h | 19 +++++-----
 arch/x86/include/asm/string_32.h  | 25 ++++++-------
 arch/x86/include/asm/string_64.h  | 23 ++++++------
 arch/x86/kernel/hpet.c            |  2 +-
 drivers/acpi/acpica/acutils.h     |  2 +-
 fs/char_dev.c                     |  2 +-
 fs/ntfs/debug.h                   |  6 ++--
 fs/ocfs2/cluster/masklog.h        |  2 +-
 include/acpi/acpixf.h             |  2 +-
 include/asm-generic/bug.h         |  7 ++--
 include/drm/drmP.h                |  2 +-
 include/linux/audit.h             |  5 +--
 include/linux/fs.h                |  8 +++--
 include/linux/printk.h            |  2 +-
 include/linux/ratelimit.h         |  3 +-
 include/linux/string.h            | 75 ++++++++++++++++++++-------------------
 lib/vsprintf.c                    |  6 ++--
 mm/util.c                         |  2 +-
 security/integrity/integrity.h    |  2 +-
 21 files changed, 121 insertions(+), 109 deletions(-)

diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index cf4f3aad0fc1..8f2f2d9a6549 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -7,19 +7,19 @@
  */
 
 #define __HAVE_ARCH_STRRCHR
-extern char * strrchr(const char * s, int c);
+extern char * strrchr(const char * s, int c) __nocapture(-1);
 
 #define __HAVE_ARCH_STRCHR
-extern char * strchr(const char * s, int c);
+extern char * strchr(const char * s, int c) __nocapture(-1);
 
 #define __HAVE_ARCH_MEMCPY
-extern void * memcpy(void *, const void *, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
 
 #define __HAVE_ARCH_MEMMOVE
-extern void * memmove(void *, const void *, __kernel_size_t);
+extern void * memmove(void *, const void *, __kernel_size_t) __nocapture(2);
 
 #define __HAVE_ARCH_MEMCHR
-extern void * memchr(const void *, int, __kernel_size_t);
+extern void * memchr(const void *, int, __kernel_size_t) __nocapture(-1);
 
 #define __HAVE_ARCH_MEMSET
 extern void * memset(void *, int, __kernel_size_t);
diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
index 2eb714c4639f..188a1022c764 100644
--- a/arch/arm64/include/asm/string.h
+++ b/arch/arm64/include/asm/string.h
@@ -17,40 +17,41 @@
 #define __ASM_STRING_H
 
 #define __HAVE_ARCH_STRRCHR
-extern char *strrchr(const char *, int c);
+extern char *strrchr(const char *, int c) __nocapture(-1);
 
 #define __HAVE_ARCH_STRCHR
-extern char *strchr(const char *, int c);
+extern char *strchr(const char *, int c) __nocapture(-1);
 
 #define __HAVE_ARCH_STRCMP
-extern int strcmp(const char *, const char *);
+extern int strcmp(const char *, const char *) __nocapture();
 
 #define __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *, const char *, __kernel_size_t);
+extern int
+strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
 
 #define __HAVE_ARCH_STRLEN
-extern __kernel_size_t strlen(const char *);
+extern __kernel_size_t strlen(const char *) __nocapture(1);
 
 #define __HAVE_ARCH_STRNLEN
-extern __kernel_size_t strnlen(const char *, __kernel_size_t);
+extern __kernel_size_t strnlen(const char *, __kernel_size_t) __nocapture(1);
 
 #define __HAVE_ARCH_MEMCPY
-extern void *memcpy(void *, const void *, __kernel_size_t);
-extern void *__memcpy(void *, const void *, __kernel_size_t);
+extern void *memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
+extern void *__memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
 
 #define __HAVE_ARCH_MEMMOVE
-extern void *memmove(void *, const void *, __kernel_size_t);
-extern void *__memmove(void *, const void *, __kernel_size_t);
+extern void *memmove(void *, const void *, __kernel_size_t) __nocapture(2);
+extern void *__memmove(void *, const void *, __kernel_size_t) __nocapture(2);
 
 #define __HAVE_ARCH_MEMCHR
-extern void *memchr(const void *, int, __kernel_size_t);
+extern void *memchr(const void *, int, __kernel_size_t) __nocapture(-1);
 
 #define __HAVE_ARCH_MEMSET
 extern void *memset(void *, int, __kernel_size_t);
 extern void *__memset(void *, int, __kernel_size_t);
 
 #define __HAVE_ARCH_MEMCMP
-extern int memcmp(const void *, const void *, size_t);
+extern int memcmp(const void *, const void *, size_t) __nocapture(1, 2);
 
 
 #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h
index da3cdffca440..ba08cf461ca2 100644
--- a/arch/powerpc/include/asm/string.h
+++ b/arch/powerpc/include/asm/string.h
@@ -11,16 +11,17 @@
 #define __HAVE_ARCH_MEMCMP
 #define __HAVE_ARCH_MEMCHR
 
-extern char * strcpy(char *,const char *);
-extern char * strncpy(char *,const char *, __kernel_size_t);
-extern __kernel_size_t strlen(const char *);
-extern int strcmp(const char *,const char *);
-extern int strncmp(const char *, const char *, __kernel_size_t);
-extern char * strcat(char *, const char *);
+extern char * strcpy(char *,const char *) __nocapture(2);
+extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
+extern __kernel_size_t strlen(const char *) __nocapture(1);
+extern int strcmp(const char *,const char *) __nocapture();
+extern int
+strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
+extern char * strcat(char *, const char *) __nocapture(2);
 extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
-extern void * memmove(void *,const void *,__kernel_size_t);
-extern int memcmp(const void *,const void *,__kernel_size_t);
+extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
+extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
+extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
 extern void * memchr(const void *,int,__kernel_size_t);
 
 #endif /* __KERNEL__ */
diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h
index 3d3e8353ee5c..db6fe4c3d1e8 100644
--- a/arch/x86/include/asm/string_32.h
+++ b/arch/x86/include/asm/string_32.h
@@ -6,28 +6,29 @@
 /* Let gcc decide whether to inline or use the out of line functions */
 
 #define __HAVE_ARCH_STRCPY
-extern char *strcpy(char *dest, const char *src);
+extern char *strcpy(char *dest, const char *src) __nocapture(2);
 
 #define __HAVE_ARCH_STRNCPY
-extern char *strncpy(char *dest, const char *src, size_t count);
+extern char *strncpy(char *dest, const char *src, size_t count) __nocapture(2);
 
 #define __HAVE_ARCH_STRCAT
-extern char *strcat(char *dest, const char *src);
+extern char *strcat(char *dest, const char *src) __nocapture(2);
 
 #define __HAVE_ARCH_STRNCAT
-extern char *strncat(char *dest, const char *src, size_t count);
+extern char *strncat(char *dest, const char *src, size_t count) __nocapture(2);
 
 #define __HAVE_ARCH_STRCMP
-extern int strcmp(const char *cs, const char *ct);
+extern int strcmp(const char *cs, const char *ct) __nocapture();
 
 #define __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *cs, const char *ct, size_t count);
+extern int
+strncmp(const char *cs, const char *ct, size_t count) __nocapture(1, 2);
 
 #define __HAVE_ARCH_STRCHR
-extern char *strchr(const char *s, int c);
+extern char *strchr(const char *s, int c) __nocapture(-1);
 
 #define __HAVE_ARCH_STRLEN
-extern size_t strlen(const char *s);
+extern size_t strlen(const char *s) __nocapture(1);
 
 static __always_inline void *__memcpy(void *to, const void *from, size_t n)
 {
@@ -197,12 +198,12 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
 #endif
 
 #define __HAVE_ARCH_MEMMOVE
-void *memmove(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n) __nocapture(2);
 
 #define memcmp __builtin_memcmp
 
 #define __HAVE_ARCH_MEMCHR
-extern void *memchr(const void *cs, int c, size_t count);
+extern void *memchr(const void *cs, int c, size_t count) __nocapture(-1);
 
 static inline void *__memset_generic(void *s, char c, size_t count)
 {
@@ -243,11 +244,11 @@ void *__constant_c_memset(void *s, unsigned long c, size_t count)
 
 /* Added by Gertjan van Wingerde to make minix and sysv module work */
 #define __HAVE_ARCH_STRNLEN
-extern size_t strnlen(const char *s, size_t count);
+extern size_t strnlen(const char *s, size_t count) __nocapture(1);
 /* end of additional stuff */
 
 #define __HAVE_ARCH_STRSTR
-extern char *strstr(const char *cs, const char *ct);
+extern char *strstr(const char *cs, const char *ct) __nocapture(-1, 2);
 
 /*
  * This looks horribly ugly, but the compiler can optimize it totally,
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index a164862d77e3..e06b6eb806ac 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -28,8 +28,8 @@ static __always_inline void *__inline_memcpy(void *to, const void *from, size_t
    function. */
 
 #define __HAVE_ARCH_MEMCPY 1
-extern void *memcpy(void *to, const void *from, size_t len);
-extern void *__memcpy(void *to, const void *from, size_t len);
+extern void *memcpy(void *to, const void *from, size_t len) __nocapture(2);
+extern void *__memcpy(void *to, const void *from, size_t len) __nocapture(2);
 
 #ifndef CONFIG_KMEMCHECK
 #if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
@@ -57,14 +57,14 @@ void *memset(void *s, int c, size_t n);
 void *__memset(void *s, int c, size_t n);
 
 #define __HAVE_ARCH_MEMMOVE
-void *memmove(void *dest, const void *src, size_t count);
-void *__memmove(void *dest, const void *src, size_t count);
+void *memmove(void *dest, const void *src, size_t count) __nocapture(2);
+void *__memmove(void *dest, const void *src, size_t count) __nocapture(2);
 
-int memcmp(const void *cs, const void *ct, size_t count);
-size_t strlen(const char *s);
-char *strcpy(char *dest, const char *src);
-char *strcat(char *dest, const char *src);
-int strcmp(const char *cs, const char *ct);
+int memcmp(const void *cs, const void *ct, size_t count) __nocapture(1, 2);
+size_t strlen(const char *s) __nocapture(1);
+char *strcpy(char *dest, const char *src) __nocapture(2);
+char *strcat(char *dest, const char *src) __nocapture(2);
+int strcmp(const char *cs, const char *ct) __nocapture(1, 2);
 
 #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
 
@@ -79,7 +79,8 @@ int strcmp(const char *cs, const char *ct);
 #define memset(s, c, n) __memset(s, c, n)
 #endif
 
-__must_check int memcpy_mcsafe_unrolled(void *dst, const void *src, size_t cnt);
+__must_check __nocapture(2) int
+memcpy_mcsafe_unrolled(void *dst, const void *src, size_t cnt);
 DECLARE_STATIC_KEY_FALSE(mcsafe_key);
 
 /**
@@ -96,7 +97,7 @@ DECLARE_STATIC_KEY_FALSE(mcsafe_key);
  *
  * Return 0 for success, -EFAULT for fail
  */
-static __always_inline __must_check int
+static __always_inline __must_check __nocapture(2) int
 memcpy_mcsafe(void *dst, const void *src, size_t cnt)
 {
 #ifdef CONFIG_X86_MCE
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 274fab99169d..b863fc09022d 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -136,7 +136,7 @@ int is_hpet_enabled(void)
 }
 EXPORT_SYMBOL_GPL(is_hpet_enabled);
 
-static void _hpet_print_config(const char *function, int line)
+static void __nocapture(1) _hpet_print_config(const char *function, int line)
 {
 	u32 i, timers, l, h;
 	printk(KERN_INFO "hpet: %s(%d):\n", function, line);
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 0a1b53c9ee0e..2349b23e86c5 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -289,7 +289,7 @@ void acpi_ut_init_stack_ptr_trace(void);
 
 void acpi_ut_track_stack_ptr(void);
 
-void
+__nocapture(2) void
 acpi_ut_trace(u32 line_number,
 	      const char *function_name,
 	      const char *module_name, u32 component_id);
diff --git a/fs/char_dev.c b/fs/char_dev.c
index 44a240c4bb65..3bc97002c86f 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -70,7 +70,7 @@ void chrdev_show(struct seq_file *f, off_t offset)
  *
  * Returns a -ve errno on failure.
  */
-static struct char_device_struct *
+static __nocapture(4) struct char_device_struct *
 __register_chrdev_region(unsigned int major, unsigned int baseminor,
 			   int minorct, const char *name)
 {
diff --git a/fs/ntfs/debug.h b/fs/ntfs/debug.h
index 61bf091e32a8..6ac561943118 100644
--- a/fs/ntfs/debug.h
+++ b/fs/ntfs/debug.h
@@ -30,7 +30,7 @@
 
 extern int debug_msgs;
 
-extern __printf(4, 5)
+extern __printf(4, 5) __nocapture(3)
 void __ntfs_debug(const char *file, int line, const char *function,
 		  const char *format, ...);
 /**
@@ -58,12 +58,12 @@ do {									\
 
 #endif	/* !DEBUG */
 
-extern  __printf(3, 4)
+extern  __printf(3, 4) __nocapture(1)
 void __ntfs_warning(const char *function, const struct super_block *sb,
 		    const char *fmt, ...);
 #define ntfs_warning(sb, f, a...)	__ntfs_warning(__func__, sb, f, ##a)
 
-extern  __printf(3, 4)
+extern  __printf(3, 4) __nocapture(1)
 void __ntfs_error(const char *function, const struct super_block *sb,
 		  const char *fmt, ...);
 #define ntfs_error(sb, f, a...)		__ntfs_error(__func__, sb, f, ##a)
diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
index 308ea0eb35fd..3c16da69605d 100644
--- a/fs/ocfs2/cluster/masklog.h
+++ b/fs/ocfs2/cluster/masklog.h
@@ -162,7 +162,7 @@ extern struct mlog_bits mlog_and_bits, mlog_not_bits;
 
 #endif
 
-__printf(4, 5)
+__printf(4, 5) __nocapture(2)
 void __mlog_printk(const u64 *m, const char *func, int line,
 		   const char *fmt, ...);
 
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index c7b3a132dbe7..951c1f41024f 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -929,7 +929,7 @@ ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
 /*
  * Debug output
  */
-ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6)
+ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) __nocapture(3)
 			       void ACPI_INTERNAL_VAR_XFACE
 			       acpi_debug_print(u32 requested_debug_level,
 						u32 line_number,
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 6f96247226a4..f6ae0d76f2e7 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -62,13 +62,13 @@ struct bug_entry {
  * to provide better diagnostics.
  */
 #ifndef __WARN_TAINT
-extern __printf(3, 4)
+extern __printf(3, 4) __nocapture(1)
 void warn_slowpath_fmt(const char *file, const int line,
 		       const char *fmt, ...);
-extern __printf(4, 5)
+extern __printf(4, 5) __nocapture(1)
 void warn_slowpath_fmt_taint(const char *file, const int line, unsigned taint,
 			     const char *fmt, ...);
-extern void warn_slowpath_null(const char *file, const int line);
+extern __nocapture(1) void warn_slowpath_null(const char *file, const int line);
 #define WANT_WARN_ON_SLOWPATH
 #define __WARN()		warn_slowpath_null(__FILE__, __LINE__)
 #define __WARN_printf(arg...)	warn_slowpath_fmt(__FILE__, __LINE__, arg)
@@ -84,6 +84,7 @@ extern void warn_slowpath_null(const char *file, const int line);
 /* used internally by panic.c */
 struct warn_args;
 
+__nocapture(1, 0)
 void __warn(const char *file, int line, void *caller, unsigned taint,
 	    struct pt_regs *regs, struct warn_args *args);
 
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 672644031bd5..231aebeed9be 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -136,7 +136,7 @@ struct dma_buf_attachment;
 #define DRM_UT_ATOMIC		0x10
 #define DRM_UT_VBL		0x20
 
-extern __printf(6, 7)
+extern __printf(6, 7) __nocapture(4)
 void drm_dev_printk(const struct device *dev, const char *level,
 		    unsigned int category, const char *function_name,
 		    const char *prefix, const char *format, ...);
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9d4443f93db6..730485bb4b65 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -135,7 +135,7 @@ extern void		    audit_log_n_hex(struct audit_buffer *ab,
 					  size_t len);
 extern void		    audit_log_n_string(struct audit_buffer *ab,
 					       const char *buf,
-					       size_t n);
+					       size_t n) __nocapture(2);
 extern void		    audit_log_n_untrustedstring(struct audit_buffer *ab,
 							const char *string,
 							size_t n);
@@ -552,7 +552,8 @@ static inline bool audit_loginuid_set(struct task_struct *tsk)
 	return uid_valid(audit_get_loginuid(tsk));
 }
 
-static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
+static inline __nocapture(2)
+void audit_log_string(struct audit_buffer *ab, const char *buf)
 {
 	audit_log_n_string(ab, buf, strlen(buf));
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index dc0478c07b2a..37fb674893a8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2456,13 +2456,15 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
 #define CHRDEV_MAJOR_HASH_SIZE	255
 /* Marks the bottom of the first segment of free char majors */
 #define CHRDEV_MAJOR_DYN_END 234
-extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
+extern int __nocapture(4)
+alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
 extern int register_chrdev_region(dev_t, unsigned, const char *);
 extern int __register_chrdev(unsigned int major, unsigned int baseminor,
 			     unsigned int count, const char *name,
 			     const struct file_operations *fops);
-extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
-				unsigned int count, const char *name);
+extern __nocapture(4) void __unregister_chrdev(unsigned int major,
+				unsigned int baseminor, unsigned int count,
+				const char *name);
 extern void unregister_chrdev_region(dev_t, unsigned);
 extern void chrdev_show(struct seq_file *,off_t);
 
diff --git a/include/linux/printk.h b/include/linux/printk.h
index eac1af8502bb..e5ed13403835 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -172,7 +172,7 @@ __printf(1, 2) __cold int printk_deferred(const char *fmt, ...);
  * with all other unrelated printk_ratelimit() callsites.  Instead use
  * printk_ratelimited() or plain old __ratelimit().
  */
-extern int __printk_ratelimit(const char *func);
+extern int __printk_ratelimit(const char *func) __nocapture(1);
 #define printk_ratelimit() __printk_ratelimit(__func__)
 extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
 				   unsigned int interval_msec);
diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h
index 57c9e0622a38..fe141261a47d 100644
--- a/include/linux/ratelimit.h
+++ b/include/linux/ratelimit.h
@@ -72,7 +72,8 @@ ratelimit_set_flags(struct ratelimit_state *rs, unsigned long flags)
 
 extern struct ratelimit_state printk_ratelimit_state;
 
-extern int ___ratelimit(struct ratelimit_state *rs, const char *func);
+extern __nocapture(2)
+int ___ratelimit(struct ratelimit_state *rs, const char *func);
 #define __ratelimit(state) ___ratelimit(state, __func__)
 
 #ifdef CONFIG_PRINTK
diff --git a/include/linux/string.h b/include/linux/string.h
index 26b6f6a66f83..8b3b97e7b2b0 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -18,51 +18,52 @@ extern void *memdup_user_nul(const void __user *, size_t);
 #include <asm/string.h>
 
 #ifndef __HAVE_ARCH_STRCPY
-extern char * strcpy(char *,const char *);
+extern char * strcpy(char *,const char *) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRNCPY
-extern char * strncpy(char *,const char *, __kernel_size_t);
+extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
+size_t strlcpy(char *, const char *, size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRSCPY
 ssize_t __must_check strscpy(char *, const char *, size_t);
 #endif
 #ifndef __HAVE_ARCH_STRCAT
-extern char * strcat(char *, const char *);
+extern char * strcat(char *, const char *) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRNCAT
-extern char * strncat(char *, const char *, __kernel_size_t);
+extern char * strncat(char *, const char *, __kernel_size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRLCAT
-extern size_t strlcat(char *, const char *, __kernel_size_t);
+extern size_t strlcat(char *, const char *, __kernel_size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRCMP
-extern int strcmp(const char *,const char *);
+extern int strcmp(const char *,const char *) __nocapture();
 #endif
 #ifndef __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *,const char *,__kernel_size_t);
+extern int strncmp(const char *,const char *,__kernel_size_t) __nocapture(1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRCASECMP
-extern int strcasecmp(const char *s1, const char *s2);
+extern int strcasecmp(const char *s1, const char *s2) __nocapture();
 #endif
 #ifndef __HAVE_ARCH_STRNCASECMP
-extern int strncasecmp(const char *s1, const char *s2, size_t n);
+extern int
+strncasecmp(const char *s1, const char *s2, size_t n) __nocapture(1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRCHR
-extern char * strchr(const char *,int);
+extern char * strchr(const char *,int) __nocapture(-1);
 #endif
 #ifndef __HAVE_ARCH_STRCHRNUL
-extern char * strchrnul(const char *,int);
+extern char * strchrnul(const char *,int) __nocapture(-1);
 #endif
 #ifndef __HAVE_ARCH_STRNCHR
-extern char * strnchr(const char *, size_t, int);
+extern char * strnchr(const char *, size_t, int) __nocapture(-1);
 #endif
 #ifndef __HAVE_ARCH_STRRCHR
-extern char * strrchr(const char *,int);
+extern char * strrchr(const char *,int) __nocapture(-1);
 #endif
-extern char * __must_check skip_spaces(const char *);
+extern char * __must_check skip_spaces(const char *) __nocapture(-1);
 
 extern char *strim(char *);
 
@@ -72,63 +73,63 @@ static inline __must_check char *strstrip(char *str)
 }
 
 #ifndef __HAVE_ARCH_STRSTR
-extern char * strstr(const char *, const char *);
+extern char * strstr(const char *, const char *) __nocapture(-1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRNSTR
-extern char * strnstr(const char *, const char *, size_t);
+extern char * strnstr(const char *, const char *, size_t) __nocapture(-1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRLEN
-extern __kernel_size_t strlen(const char *);
+extern __kernel_size_t strlen(const char *) __nocapture(1);
 #endif
 #ifndef __HAVE_ARCH_STRNLEN
-extern __kernel_size_t strnlen(const char *,__kernel_size_t);
+extern __kernel_size_t strnlen(const char *,__kernel_size_t) __nocapture(1);
 #endif
 #ifndef __HAVE_ARCH_STRPBRK
-extern char * strpbrk(const char *,const char *);
+extern char * strpbrk(const char *,const char *) __nocapture(-1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRSEP
-extern char * strsep(char **,const char *);
+extern char * strsep(char **,const char *) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_STRSPN
-extern __kernel_size_t strspn(const char *,const char *);
+extern __kernel_size_t strspn(const char *,const char *) __nocapture();
 #endif
 #ifndef __HAVE_ARCH_STRCSPN
-extern __kernel_size_t strcspn(const char *,const char *);
+extern __kernel_size_t strcspn(const char *,const char *) __nocapture();
 #endif
 
 #ifndef __HAVE_ARCH_MEMSET
 extern void * memset(void *,int,__kernel_size_t);
 #endif
 #ifndef __HAVE_ARCH_MEMCPY
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_MEMMOVE
-extern void * memmove(void *,const void *,__kernel_size_t);
+extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
 #endif
 #ifndef __HAVE_ARCH_MEMSCAN
 extern void * memscan(void *,int,__kernel_size_t);
 #endif
 #ifndef __HAVE_ARCH_MEMCMP
-extern int memcmp(const void *,const void *,__kernel_size_t);
+extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
 #endif
 #ifndef __HAVE_ARCH_MEMCHR
-extern void * memchr(const void *,int,__kernel_size_t);
+extern void * memchr(const void *,int,__kernel_size_t) __nocapture(-1);
 #endif
-void *memchr_inv(const void *s, int c, size_t n);
+void *memchr_inv(const void *s, int c, size_t n) __nocapture(-1);
 char *strreplace(char *s, char old, char new);
 
 extern void kfree_const(const void *x);
 
-extern char *kstrdup(const char *s, gfp_t gfp) __malloc;
-extern const char *kstrdup_const(const char *s, gfp_t gfp);
-extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
-extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
+extern char *kstrdup(const char *s, gfp_t gfp) __malloc __nocapture(1);
+extern const char *kstrdup_const(const char *s, gfp_t gfp) __nocapture(1);
+extern char *kstrndup(const char *s, size_t len, gfp_t gfp) __nocapture(1);
+extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __nocapture(1);
 
 extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
 extern void argv_free(char **argv);
 
-extern bool sysfs_streq(const char *s1, const char *s2);
-extern int kstrtobool(const char *s, bool *res);
+extern bool sysfs_streq(const char *s1, const char *s2) __nocapture();
+extern int kstrtobool(const char *s, bool *res) __nocapture(1);
 static inline int strtobool(const char *s, bool *res)
 {
 	return kstrtobool(s, res);
@@ -137,8 +138,10 @@ static inline int strtobool(const char *s, bool *res)
 int match_string(const char * const *array, size_t n, const char *string);
 
 #ifdef CONFIG_BINARY_PRINTF
-int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
-int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf);
+int vbin_printf(u32 *bin_buf, size_t size, const char *fmt,
+		va_list args) __nocapture(3);
+int bstr_printf(char *buf, size_t size, const char *fmt,
+		const u32 *bin_buf) __nocapture(3);
 int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4);
 #endif
 
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 0967771d8f7f..a192761d338a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -118,7 +118,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
 }
 EXPORT_SYMBOL(simple_strtoll);
 
-static noinline_for_stack
+static noinline_for_stack __nocapture(1)
 int skip_atoi(const char **s)
 {
 	int i = 0;
@@ -1570,7 +1570,7 @@ int kptr_restrict __read_mostly;
  * function pointers are really function descriptors, which contain a
  * pointer to the real address.
  */
-static noinline_for_stack
+static noinline_for_stack __nocapture(1)
 char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 	      struct printf_spec spec)
 {
@@ -1749,7 +1749,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
  * @precision: precision of a number
  * @qualifier: qualifier of a number (long, size_t, ...)
  */
-static noinline_for_stack
+static noinline_for_stack __nocapture(1)
 int format_decode(const char *fmt, struct printf_spec *spec)
 {
 	const char *start = fmt;
diff --git a/mm/util.c b/mm/util.c
index 5606c6b74eee..bdfb314bae35 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -17,7 +17,7 @@
 
 #include "internal.h"
 
-static inline int is_kernel_rodata(const void *addr)
+static inline __nocapture(1) int is_kernel_rodata(const void *addr)
 {
 	return (unsigned long)addr >= (unsigned long)__start_rodata &&
 		(unsigned long)addr < (unsigned long)__end_rodata;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 24520b4ef3b0..b89448a69cf4 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -177,7 +177,7 @@ static inline void evm_load_x509(void)
 
 #ifdef CONFIG_INTEGRITY_AUDIT
 /* declarations */
-void integrity_audit_msg(int audit_msgno, struct inode *inode,
+void __nocapture(4) integrity_audit_msg(int audit_msgno, struct inode *inode,
 			 const unsigned char *fname, const char *op,
 			 const char *cause, int result, int info);
 #else
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 4/4] initify: Mark functions with the __unverified_nocapture attribute
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
                   ` (2 preceding siblings ...)
  2016-12-16 22:06 ` [PATCH v4 3/4] initify: Mark functions with the __nocapture attribute Kees Cook
@ 2016-12-16 22:06 ` Kees Cook
  2016-12-16 22:19 ` [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
  2016-12-19 18:24 ` Laura Abbott
  5 siblings, 0 replies; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:06 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, Emese Revfy, linux-kernel, Arnd Bergmann,
	Josh Triplett, pageexec, spender, mmarek, yamada.masahiro,
	linux-kbuild, minipli, linux, catalin.marinas, linux,
	david.brown, benh, tglx, akpm, jlayton, sam

From: Emese Revfy <re.emese@gmail.com>

This attribute disables the compile data flow verification of the
designated nocapture parameters of the function. Use it only on function
parameters that are difficult for the plugin to analyze.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 include/linux/compiler-gcc.h | 1 +
 include/linux/compiler.h     | 4 ++++
 lib/vsprintf.c               | 4 ++--
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 1e11ee911c3e..4ebb3ba17631 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -207,6 +207,7 @@
  */
 #ifdef INITIFY_PLUGIN
 #define __nocapture(...) __attribute__((nocapture(__VA_ARGS__)))
+#define __unverified_nocapture(...) __attribute__((unverified_nocapture(__VA_ARGS__)))
 #endif
 
 /*
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 8b3dcc790bb6..1bde420f07bb 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -437,6 +437,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
 # define __nocapture(...)
 #endif
 
+#ifndef __unverified_nocapture
+# define __unverified_nocapture(...)
+#endif
+
 /*
  * Tell gcc if a function is cold. The compiler will assume any path
  * directly leading to the call is unlikely.
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index a192761d338a..cb964b51f9f8 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -118,7 +118,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
 }
 EXPORT_SYMBOL(simple_strtoll);
 
-static noinline_for_stack __nocapture(1)
+static noinline_for_stack __nocapture(1) __unverified_nocapture(1)
 int skip_atoi(const char **s)
 {
 	int i = 0;
@@ -1570,7 +1570,7 @@ int kptr_restrict __read_mostly;
  * function pointers are really function descriptors, which contain a
  * pointer to the real address.
  */
-static noinline_for_stack __nocapture(1)
+static noinline_for_stack __nocapture(1) __unverified_nocapture(1)
 char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 	      struct printf_spec spec)
 {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
                   ` (3 preceding siblings ...)
  2016-12-16 22:06 ` [PATCH v4 4/4] initify: Mark functions with the __unverified_nocapture attribute Kees Cook
@ 2016-12-16 22:19 ` Kees Cook
  2016-12-19 11:10   ` Emese Revfy
  2016-12-19 18:24 ` Laura Abbott
  5 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2016-12-16 22:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Kees Cook, LKML, Arnd Bergmann, Emese Revfy, Josh Triplett,
	PaX Team, Brad Spengler, Michal Marek, Masahiro Yamada,
	linux-kbuild, minipli, Russell King, Catalin Marinas,
	Rasmus Villemoes, David Brown, benh, Thomas Gleixner,
	Andrew Morton, Jeff Layton, Sam Ravnborg

On Fri, Dec 16, 2016 at 2:06 PM, Kees Cook <keescook@chromium.org> wrote:
> Hi,
>
> This is a continuation of Emese Revfy's initify plugin upstreaming. This
> is based on her v3, but updated with various fixes from her github tree.
> Additionally, I split off the printf attribute fixes and sent those
> separately.
>
> This is the initify gcc plugin. The kernel already has a mechanism to
> free up code and data memory that is only used during kernel or module
> initialization.  This plugin will teach the compiler to find more such
> code and data that can be freed after initialization. It reduces memory
> usage.  The initify gcc plugin can be useful for embedded systems.
>
> Originally it was a CII project supported by the Linux Foundation.
>
> This plugin is the part of grsecurity/PaX.
>
> The plugin supports all gcc versions from 4.5 to 7.0.
>
> Changes on top of the PaX version (since March 6.). These are the important
> ones:
>  * move all local strings to init.rodata.str and exit.rodata.str
>    (not just __func__)
>  * report all initified strings and functions
>    (GCC_PLUGIN_INITIFY_VERBOSE config option)
>  * automatically discover init/exit functions and apply the __init or
>    __exit attributes on them
>
> You can find more about the changes here:
> https://github.com/ephox-gcc-plugins/initify
>
> This patch set is based on v4.9-rc2.
>
> Some build statistics about the plugin:
>
> On allyes config (amd64, gcc-6):
> * 8412 initified strings
> *  167 initified functions
>
> On allmod config (i386, gcc-6):
> * 8597 initified strings
> *  159 initified functions
>
> On allyes config (amd64, gcc-6):
>
> section         vanilla                 vanilla + initify        change
> -----------------------------------------------------------------------
> .rodata         21746728 (0x14bd428)    21488680 (0x147e428)    -258048
> .init.data       1338376  (0x146c08)     1683016  (0x19ae48)    +344640
> .text           78270904 (0x4aa51b8)    78228280 (0x4a9ab38)     -42624
> .init.text       1184725  (0x1213d5)     1223257  (0x12aa59)     +38532
> .exit.data           104  (0x000068)       17760  (0x004560)     +17656
> .exit.text        174473  (0x02a989)      175763  (0x02ae93)      +1290
>
>         FileSiz (vanilla)       FileSiz (vanilla + initify)      change
> ------------------------------------------------------------------------
> 00      102936576 (0x622b000)   102678528 (0x61ec000)           -258048
> 03       28680192 (0x1b5a000)    29081600 (0x1bbc000)           +401408
>
> 00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
>        .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
>        __param __modver
> 03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
>        .parainstructions .altinstructions .altinstr_replacement
>        .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk
>
>
> On defconfig (amd64, gcc-6):
> * 1957 initified strings
> *   29 initified functions
>
> On defconfig (amd64, gcc-6):
>
> section         vanilla                 vanilla + initify        change
> -----------------------------------------------------------------------
> .rodata         2524240 (0x268450)      2462800 (0x259450)      -61440
> .init.data       560256 (0x088c80)       644000 (0x09d3a0)      +83744
> .text           9377367 (0x8f1657)      9373079 (0x8f0597)       -4288
> .init.text       438586 (0x06b13a)       441828 (0x06bde4)       +3242
> .exit.data            0                     832 (0x000340)        +832
> .exit.text         8857 (0x002299)          8857 (0x002299)          0
>
>         FileSiz (vanilla)       FileSiz (vanilla + initify)      change
> ------------------------------------------------------------------------
> 00      13398016 (0xcc7000)     13336576 (0xcb8000)             -61440
> 03       2203648 (0x21a000)      2293760 (0x230000)             +90112
>
> 00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
>        .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
>        __param __modver
> 03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
>        .parainstructions .altinstructions .altinstr_replacement
>        .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk
>
> One thing of note is that this plugin triggers false positive warnings
> from the modpost section mismatch detector. Further work is needed to
> deal with this.

FWIW, it still seems to me that these aren't false positives:

WARNING: vmlinux.o(.text.unlikely+0x1b1): Section mismatch in
reference from the function uncore_pci_exit.part.22() to the function
.init.text:uncore_free_pcibus_map()
The function uncore_pci_exit.part.22() references
the function __init uncore_free_pcibus_map().
This is often because uncore_pci_exit.part.22 lacks a __init
annotation or the annotation of uncore_free_pcibus_map is wrong.

This is complaining about arch/x86/events/intel/uncore.c:

__init intel_uncore_init() calls uncore_pci_exit().
__exit intel_uncore_exit() calls uncore_pci_exit().

uncore_pci_exit() is marked as "both" (which will resolve to __exit).

__init intel_uncore_init() calls uncore_free_pcibus_map().
uncore_pci_exit() calls uncore_free_pcibus_map().

uncore_free_pcibus_map() should be marked as "both", but it seems like
it ends up marked only __init.

When I tried a build that looked only at -D MODULE being set, I got
fewer warnings, but the modpost on builtins would still have warnings,
but they complained about things in __exit calling __init, which for a
builtin is fine: the __exit section will never be called...

I think the modular-build checking is better than the Kconfig
approach. It just requires that the __exit sections for non-modules
get discarded before the modpost runs to complain about it...

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 1/4] gcc-plugins: Add the initify gcc plugin
  2016-12-16 22:06 ` [PATCH v4 1/4] gcc-plugins: Add " Kees Cook
@ 2016-12-16 22:45   ` PaX Team
  2016-12-16 23:02     ` Kees Cook
  0 siblings, 1 reply; 23+ messages in thread
From: PaX Team @ 2016-12-16 22:45 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook
  Cc: Kees Cook, Emese Revfy, linux-kernel, Arnd Bergmann,
	Josh Triplett, spender, mmarek, yamada.masahiro, linux-kbuild,
	minipli, linux, catalin.marinas, linux, david.brown, benh, tglx,
	akpm, jlayton, sam

On 16 Dec 2016 at 14:06, Kees Cook wrote:

> diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
> index 950fd2e64bb7..369bfb471e58 100644
> --- a/scripts/gcc-plugins/gcc-common.h
> +++ b/scripts/gcc-plugins/gcc-common.h
> @@ -287,6 +287,26 @@ static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct c
>   return NULL;
>  }
> 
> +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
> +				bool (*callback)(cgraph_node_ptr, void *),
> +				void *data, bool include_overwritable)
> +{
> +	cgraph_node_ptr alias;
> +
> +	if (callback(node, data))
> +		return true;
> +
> +	for (alias = node->same_body; alias; alias = alias->next) {
> +		if (include_overwritable ||
> +			cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE)
> +			if (cgraph_for_node_and_aliases(alias, callback, data,
> +							include_overwritable))
> +				return true;
> +	}
> +
> +	return false;
> +}
> +
>  #define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \
>   for ((node) = cgraph_first_function_with_gimple_body(); (node); \
>    (node) = cgraph_next_function_with_gimple_body(node))

this hunk above and...

> @@ -674,6 +707,14 @@ static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node)
>   return node->get_alias_target();
>  }
> 
> +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
> +				bool (*callback)(cgraph_node_ptr, void *),
> +				void *data, bool include_overwritable)
> +{
> +	return node->call_for_symbol_thunks_and_aliases(callback, data,
> +							include_overwritable);
> +}
> +
>  static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data)
>  {
>   return symtab->add_cgraph_insertion_hook(hook, data);

...this one aren't needed by any plugins upstream so maybe introduce them when
the needed arises? and the whole patch against gcc-common.h would also conflict
with the version i maintain and that you said you'd sync to so there's a decision
to be made regarding how this will is to be maintained...

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 1/4] gcc-plugins: Add the initify gcc plugin
  2016-12-16 22:45   ` PaX Team
@ 2016-12-16 23:02     ` Kees Cook
  2016-12-16 23:15       ` PaX Team
  0 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2016-12-16 23:02 UTC (permalink / raw)
  To: PaX Team
  Cc: kernel-hardening, Emese Revfy, LKML, Arnd Bergmann,
	Josh Triplett, Brad Spengler, Michal Marek, Masahiro Yamada,
	linux-kbuild, minipli, Russell King, Catalin Marinas,
	Rasmus Villemoes, David Brown, benh, Thomas Gleixner,
	Andrew Morton, Jeff Layton, Sam Ravnborg

On Fri, Dec 16, 2016 at 2:45 PM, PaX Team <pageexec@freemail.hu> wrote:
> On 16 Dec 2016 at 14:06, Kees Cook wrote:
>
>> diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
>> index 950fd2e64bb7..369bfb471e58 100644
>> --- a/scripts/gcc-plugins/gcc-common.h
>> +++ b/scripts/gcc-plugins/gcc-common.h
>> @@ -287,6 +287,26 @@ static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct c
>>   return NULL;
>>  }
>>
>> +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
>> +                             bool (*callback)(cgraph_node_ptr, void *),
>> +                             void *data, bool include_overwritable)
>> +{
>> +     cgraph_node_ptr alias;
>> +
>> +     if (callback(node, data))
>> +             return true;
>> +
>> +     for (alias = node->same_body; alias; alias = alias->next) {
>> +             if (include_overwritable ||
>> +                     cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE)
>> +                     if (cgraph_for_node_and_aliases(alias, callback, data,
>> +                                                     include_overwritable))
>> +                             return true;
>> +     }
>> +
>> +     return false;
>> +}
>> +
>>  #define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \
>>   for ((node) = cgraph_first_function_with_gimple_body(); (node); \
>>    (node) = cgraph_next_function_with_gimple_body(node))
>
> this hunk above and...
>
>> @@ -674,6 +707,14 @@ static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node)
>>   return node->get_alias_target();
>>  }
>>
>> +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node,
>> +                             bool (*callback)(cgraph_node_ptr, void *),
>> +                             void *data, bool include_overwritable)
>> +{
>> +     return node->call_for_symbol_thunks_and_aliases(callback, data,
>> +                                                     include_overwritable);
>> +}
>> +
>>  static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data)
>>  {
>>   return symtab->add_cgraph_insertion_hook(hook, data);
>
> ...this one aren't needed by any plugins upstream so maybe introduce them when
> the needed arises?

Hrm, sure. I was just going off of Emese's v3. (And this is partially
an artifact of basing off of v4.9-rc2... I'll refresh it to v4.10-rc2
once it's out.)

> and the whole patch against gcc-common.h would also conflict
> with the version i maintain and that you said you'd sync to so there's a decision
> to be made regarding how this will is to be maintained...

What's easiest for you? I'm okay to carry "unused by upstream yet"
functions and macros in gcc-common, though I don't like carrying lots
of commented out stuff. :P

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 1/4] gcc-plugins: Add the initify gcc plugin
  2016-12-16 23:02     ` Kees Cook
@ 2016-12-16 23:15       ` PaX Team
  0 siblings, 0 replies; 23+ messages in thread
From: PaX Team @ 2016-12-16 23:15 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, Emese Revfy, LKML, Arnd Bergmann,
	Josh Triplett, Brad Spengler, Michal Marek, Masahiro Yamada,
	linux-kbuild, minipli, Russell King, Catalin Marinas,
	Rasmus Villemoes, David Brown, benh, Thomas Gleixner,
	Andrew Morton, Jeff Layton, Sam Ravnborg

On 16 Dec 2016 at 15:02, Kees Cook wrote:

> >>  static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data)
> >>  {
> >>   return symtab->add_cgraph_insertion_hook(hook, data);
> >
> > ...this one aren't needed by any plugins upstream so maybe introduce them when
> > the needed arises?
> 
> Hrm, sure. I was just going off of Emese's v3. (And this is partially
> an artifact of basing off of v4.9-rc2... I'll refresh it to v4.10-rc2
> once it's out.)
> 
> > and the whole patch against gcc-common.h would also conflict
> > with the version i maintain and that you said you'd sync to so there's a decision
> > to be made regarding how this will is to be maintained...
> 
> What's easiest for you? I'm okay to carry "unused by upstream yet"
> functions and macros in gcc-common, though I don't like carrying lots
> of commented out stuff. :P

well, as i explained it the other day, my version has 'everything but the
kitchen sink' because i use it for development which isn't necessarily what
other projects need or want for themselves (e.g., consider the consequences
of Arnd's recent call to reduce the number of supported gcc versions, if that
goes anywhere above 4.5, gcc-common.h in linux can be further trimmed from my
version). at the end of the day this is a policy call and i'm not the one to
make it for linux ;).

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2016-12-16 22:19 ` [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
@ 2016-12-19 11:10   ` Emese Revfy
  2017-01-04  0:23     ` Kees Cook
  0 siblings, 1 reply; 23+ messages in thread
From: Emese Revfy @ 2016-12-19 11:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Fri, 16 Dec 2016 14:19:10 -0800
Kees Cook <keescook@chromium.org> wrote:
 
> FWIW, it still seems to me that these aren't false positives:
> 
> WARNING: vmlinux.o(.text.unlikely+0x1b1): Section mismatch in
> reference from the function uncore_pci_exit.part.22() to the function
> .init.text:uncore_free_pcibus_map()
> The function uncore_pci_exit.part.22() references
> the function __init uncore_free_pcibus_map().
> This is often because uncore_pci_exit.part.22 lacks a __init
> annotation or the annotation of uncore_free_pcibus_map is wrong.

Hi,

I can't reproduce this section mismatch. Could you please send me your .config?
Which gcc version did you use?
I used this tree: git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git initify-v4

Thanks
-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
                   ` (4 preceding siblings ...)
  2016-12-16 22:19 ` [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
@ 2016-12-19 18:24 ` Laura Abbott
  2017-01-04  0:23   ` Kees Cook
  5 siblings, 1 reply; 23+ messages in thread
From: Laura Abbott @ 2016-12-19 18:24 UTC (permalink / raw)
  To: Kees Cook, kernel-hardening
  Cc: linux-kernel, Arnd Bergmann, Emese Revfy, Josh Triplett,
	pageexec, spender, mmarek, yamada.masahiro, linux-kbuild,
	minipli, linux, catalin.marinas, linux, david.brown, benh, tglx,
	akpm, jlayton, sam

On 12/16/2016 02:06 PM, Kees Cook wrote:
> Hi,
> 
> This is a continuation of Emese Revfy's initify plugin upstreaming. This
> is based on her v3, but updated with various fixes from her github tree.
> Additionally, I split off the printf attribute fixes and sent those
> separately.
> 
> This is the initify gcc plugin. The kernel already has a mechanism to
> free up code and data memory that is only used during kernel or module
> initialization.  This plugin will teach the compiler to find more such
> code and data that can be freed after initialization. It reduces memory
> usage.  The initify gcc plugin can be useful for embedded systems.
> 
> Originally it was a CII project supported by the Linux Foundation.
> 
> This plugin is the part of grsecurity/PaX.
> 
> The plugin supports all gcc versions from 4.5 to 7.0.
> 
> Changes on top of the PaX version (since March 6.). These are the important
> ones:
>  * move all local strings to init.rodata.str and exit.rodata.str
>    (not just __func__)
>  * report all initified strings and functions
>    (GCC_PLUGIN_INITIFY_VERBOSE config option)
>  * automatically discover init/exit functions and apply the __init or
>    __exit attributes on them
> 
> You can find more about the changes here:
> https://github.com/ephox-gcc-plugins/initify
> 
> This patch set is based on v4.9-rc2.
> 
> Some build statistics about the plugin:
> 
> On allyes config (amd64, gcc-6):
> * 8412 initified strings
> *  167 initified functions
> 
> On allmod config (i386, gcc-6):
> * 8597 initified strings
> *  159 initified functions
> 
> On allyes config (amd64, gcc-6):
> 
> section         vanilla                 vanilla + initify        change
> -----------------------------------------------------------------------
> .rodata         21746728 (0x14bd428)    21488680 (0x147e428)    -258048
> .init.data       1338376  (0x146c08)     1683016  (0x19ae48)    +344640
> .text           78270904 (0x4aa51b8)    78228280 (0x4a9ab38)     -42624
> .init.text       1184725  (0x1213d5)     1223257  (0x12aa59)     +38532
> .exit.data           104  (0x000068)       17760  (0x004560)     +17656
> .exit.text        174473  (0x02a989)      175763  (0x02ae93)      +1290
> 
>         FileSiz (vanilla)       FileSiz (vanilla + initify)      change
> ------------------------------------------------------------------------
> 00      102936576 (0x622b000)   102678528 (0x61ec000)           -258048
> 03       28680192 (0x1b5a000)    29081600 (0x1bbc000)           +401408
> 
> 00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
>        .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
>        __param __modver
> 03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
>        .parainstructions .altinstructions .altinstr_replacement
>        .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk
> 
> 
> On defconfig (amd64, gcc-6):
> * 1957 initified strings
> *   29 initified functions
> 
> On defconfig (amd64, gcc-6):
> 
> section         vanilla                 vanilla + initify        change
> -----------------------------------------------------------------------
> .rodata         2524240 (0x268450)      2462800 (0x259450)      -61440
> .init.data       560256 (0x088c80)       644000 (0x09d3a0)      +83744
> .text           9377367 (0x8f1657)      9373079 (0x8f0597)       -4288
> .init.text       438586 (0x06b13a)       441828 (0x06bde4)       +3242
> .exit.data            0                     832 (0x000340)        +832
> .exit.text         8857 (0x002299)          8857 (0x002299)          0
> 
>         FileSiz (vanilla)       FileSiz (vanilla + initify)      change
> ------------------------------------------------------------------------
> 00      13398016 (0xcc7000)     13336576 (0xcb8000)             -61440
> 03       2203648 (0x21a000)      2293760 (0x230000)             +90112
> 
> 00     .text .notes __ex_table .rodata __bug_table .pci_fixup .builtin_fw
>        .tracedata __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata
>        __param __modver
> 03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init
>        .parainstructions .altinstructions .altinstr_replacement
>        .iommu_table .apicdrivers .exit.text .exit.data .smp_locks .bss .brk
> 
> One thing of note is that this plugin triggers false positive warnings
> from the modpost section mismatch detector. Further work is needed to
> deal with this.
> 
> 
> Changed from v3:
>  * Refresh from Emese's latest version.
> 
> Changed from v2:
>  * Check all uses when walking a use-def chain.
>  * Check all uses of initialized local variables and initify them if they
>    have only nocapture uses. Previously only uses in call arguments
>    determined whether the initializer value could be initified.
>  * Handle the format gcc attribute from the plugin too.
>  * Verify nocapture parameters of calls. Track uses of these parameters
>    and verify that all uses are not captured. Verify only the nocapture
>    attribute (The format attribute should be verified too.).
>  * Fixed wrong indexing of function arguments.
>  * Fixed decl comparison. When comparing two decls the tree codes must be
>    the same.
>  * Search capture uses of the return value. Use negative nocapture
>    attribute parameter on a function argument to verify that the return
>    value is not captured.
>  * Stop the search for capture uses if there is a cast to integer type.
>  * Removed unnecessary duplication hook.
>  * Handle cloned functions with a changed argument list.
>  * Check visited tree nodes to avoid an infinite loop.
>  * Add a new initify plugin option: enable_init_to_exit_moves. Move a
>    function to the exit section if it is called by __init and __exit
>    functions too.
>  * Added plugin option to disable the search of capture uses in nocapture
>    functions. We must be able to disable verification of nocapture
>    functions because there is a lot of asm code in the str* and mem*
>    functions on i386.
>  * Added some more nocapture attributes.
>  * Added some more printf attributes.
>  * Added some unverified_nocapture attributes.
>  * Make is_kernel_rodata() nocapture.
>  * Added comment for the nocapture attribute from Kees.
> 
> Changes from v1:
>  * Removed unnecessary nocapture attributes from boot code
>    (Reported-by: PaX Team <pageexec@freemail.hu>)
>  * Removed nocapture attributes from functions that return
>    the marked parameter
>    (Reported-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>)
>  * Added nocapture attribute to strlen()
>  * Updated gcc-common.h from PaX
>  * Don't forcibly constify initified string types
>    this caused the size reduction of the .data section
>    (initify_plugin.c)
>  * Added the section mismatch problem in the commit message
> 

I gave this a spin on arm64 and it seems to boot up and run okay
with just the "select HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT if GCC_PLUGINS"
added for arm64. The patches could probably use more review but
I think it would be good to just fold this in for arm64 for ease of
testing.

Thanks,
Laura

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2016-12-19 11:10   ` Emese Revfy
@ 2017-01-04  0:23     ` Kees Cook
  2017-01-11  0:24       ` Emese Revfy
  0 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2017-01-04  0:23 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

[-- Attachment #1: Type: text/plain, Size: 992 bytes --]

On Mon, Dec 19, 2016 at 3:10 AM, Emese Revfy <re.emese@gmail.com> wrote:
> On Fri, 16 Dec 2016 14:19:10 -0800
> Kees Cook <keescook@chromium.org> wrote:
>
>> FWIW, it still seems to me that these aren't false positives:
>>
>> WARNING: vmlinux.o(.text.unlikely+0x1b1): Section mismatch in
>> reference from the function uncore_pci_exit.part.22() to the function
>> .init.text:uncore_free_pcibus_map()
>> The function uncore_pci_exit.part.22() references
>> the function __init uncore_free_pcibus_map().
>> This is often because uncore_pci_exit.part.22 lacks a __init
>> annotation or the annotation of uncore_free_pcibus_map is wrong.
>
> Hi,
>
> I can't reproduce this section mismatch. Could you please send me your .config?

Attached.

> Which gcc version did you use?

gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

> I used this tree: git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git initify-v4

Yup, that's what I was building from.

-Kees

-- 
Kees Cook
Nexus Security

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 28007 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2016-12-19 18:24 ` Laura Abbott
@ 2017-01-04  0:23   ` Kees Cook
  0 siblings, 0 replies; 23+ messages in thread
From: Kees Cook @ 2017-01-04  0:23 UTC (permalink / raw)
  To: Laura Abbott
  Cc: kernel-hardening, LKML, Arnd Bergmann, Emese Revfy,
	Josh Triplett, PaX Team, Brad Spengler, Michal Marek,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Sam Ravnborg

On Mon, Dec 19, 2016 at 10:24 AM, Laura Abbott <labbott@redhat.com> wrote:
> I gave this a spin on arm64 and it seems to boot up and run okay
> with just the "select HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT if GCC_PLUGINS"
> added for arm64. The patches could probably use more review but
> I think it would be good to just fold this in for arm64 for ease of
> testing.

Thanks! I've added this to arm64 for a v5 soon.

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-04  0:23     ` Kees Cook
@ 2017-01-11  0:24       ` Emese Revfy
  2017-01-11  1:09         ` Kees Cook
  0 siblings, 1 reply; 23+ messages in thread
From: Emese Revfy @ 2017-01-11  0:24 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Tue, 3 Jan 2017 16:23:03 -0800
Kees Cook <keescook@chromium.org> wrote:

> On Mon, Dec 19, 2016 at 3:10 AM, Emese Revfy <re.emese@gmail.com> wrote:
> > On Fri, 16 Dec 2016 14:19:10 -0800
> > Kees Cook <keescook@chromium.org> wrote:
> >
> >> FWIW, it still seems to me that these aren't false positives:
> >>
> >> WARNING: vmlinux.o(.text.unlikely+0x1b1): Section mismatch in
> >> reference from the function uncore_pci_exit.part.22() to the function
> >> .init.text:uncore_free_pcibus_map()
> >> The function uncore_pci_exit.part.22() references
> >> the function __init uncore_free_pcibus_map().
> >> This is often because uncore_pci_exit.part.22 lacks a __init
> >> annotation or the annotation of uncore_free_pcibus_map is wrong.

Thanks for the report, you can find the fix here:
https://github.com/ephox-gcc-plugins/initify/commit/5d93a54badd9751b79ef67ce8a8b4081fea37547

-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-11  0:24       ` Emese Revfy
@ 2017-01-11  1:09         ` Kees Cook
  2017-01-12 21:41           ` Emese Revfy
  0 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2017-01-11  1:09 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Tue, Jan 10, 2017 at 4:24 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Tue, 3 Jan 2017 16:23:03 -0800
> Kees Cook <keescook@chromium.org> wrote:
>
>> On Mon, Dec 19, 2016 at 3:10 AM, Emese Revfy <re.emese@gmail.com> wrote:
>> > On Fri, 16 Dec 2016 14:19:10 -0800
>> > Kees Cook <keescook@chromium.org> wrote:
>> >
>> >> FWIW, it still seems to me that these aren't false positives:
>> >>
>> >> WARNING: vmlinux.o(.text.unlikely+0x1b1): Section mismatch in
>> >> reference from the function uncore_pci_exit.part.22() to the function
>> >> .init.text:uncore_free_pcibus_map()
>> >> The function uncore_pci_exit.part.22() references
>> >> the function __init uncore_free_pcibus_map().
>> >> This is often because uncore_pci_exit.part.22 lacks a __init
>> >> annotation or the annotation of uncore_free_pcibus_map is wrong.
>
> Thanks for the report, you can find the fix here:
> https://github.com/ephox-gcc-plugins/initify/commit/5d93a54badd9751b79ef67ce8a8b4081fea37547

Awesome! Thanks, that eliminated almost all of the warnings. I'm still
seeing this:

WARNING: vmlinux.o(.text+0x1087e7): Section mismatch in reference from
the function rebind_subsystems() to the variable
.init.rodata.str:__func__.4400
The function rebind_subsystems() references
the variable __initconst __func__.4400.
This is often because rebind_subsystems lacks a __initconst
annotation or the annotation of __func__.4400 is wrong.

WARNING: vmlinux.o(.text+0x13789d): Section mismatch in reference from
the function register_ftrace_export() to the variable
.init.rodata.str:__func__.4251
The function register_ftrace_export() references
the variable __initconst __func__.4251.
This is often because register_ftrace_export lacks a __initconst
annotation or the annotation of __func__.4251 is wrong.

WARNING: vmlinux.o(.text+0x139d24): Section mismatch in reference from
the function tracepoint_printk_sysctl() to the variable
.init.rodata.str:__func__.4251
The function tracepoint_printk_sysctl() references
the variable __initconst __func__.4251.
This is often because tracepoint_printk_sysctl lacks a __initconst
annotation or the annotation of __func__.4251 is wrong.

I haven't taken a close look yet, but I assume these are from the
const strings in these functions?

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-11  1:09         ` Kees Cook
@ 2017-01-12 21:41           ` Emese Revfy
  2017-01-12 23:27             ` Kees Cook
  0 siblings, 1 reply; 23+ messages in thread
From: Emese Revfy @ 2017-01-12 21:41 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Tue, 10 Jan 2017 17:09:31 -0800
Kees Cook <keescook@chromium.org> wrote:

> WARNING: vmlinux.o(.text+0x1087e7): Section mismatch in reference from
> the function rebind_subsystems() to the variable
> .init.rodata.str:__func__.4400
> The function rebind_subsystems() references
> the variable __initconst __func__.4400.
> This is often because rebind_subsystems lacks a __initconst
> annotation or the annotation of __func__.4400 is wrong.

Thanks for the report, you can find the fix here:
https://github.com/ephox-gcc-plugins/initify/commit/25f34834e3373e067133bc5d39d42c50a3592d56
 
-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-12 21:41           ` Emese Revfy
@ 2017-01-12 23:27             ` Kees Cook
  2017-01-12 23:40               ` Kees Cook
  0 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2017-01-12 23:27 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Thu, Jan 12, 2017 at 1:41 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Tue, 10 Jan 2017 17:09:31 -0800
> Kees Cook <keescook@chromium.org> wrote:
>
>> WARNING: vmlinux.o(.text+0x1087e7): Section mismatch in reference from
>> the function rebind_subsystems() to the variable
>> .init.rodata.str:__func__.4400
>> The function rebind_subsystems() references
>> the variable __initconst __func__.4400.
>> This is often because rebind_subsystems lacks a __initconst
>> annotation or the annotation of __func__.4400 is wrong.
>
> Thanks for the report, you can find the fix here:
> https://github.com/ephox-gcc-plugins/initify/commit/25f34834e3373e067133bc5d39d42c50a3592d56

Awesome! I can confirm, it builds without warnings now. Thanks!

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-12 23:27             ` Kees Cook
@ 2017-01-12 23:40               ` Kees Cook
  2017-01-17 20:31                 ` Emese Revfy
  2017-02-15  0:23                 ` Emese Revfy
  0 siblings, 2 replies; 23+ messages in thread
From: Kees Cook @ 2017-01-12 23:40 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Thu, Jan 12, 2017 at 3:27 PM, Kees Cook <keescook@chromium.org> wrote:
> On Thu, Jan 12, 2017 at 1:41 PM, Emese Revfy <re.emese@gmail.com> wrote:
>> On Tue, 10 Jan 2017 17:09:31 -0800
>> Kees Cook <keescook@chromium.org> wrote:
>>
>>> WARNING: vmlinux.o(.text+0x1087e7): Section mismatch in reference from
>>> the function rebind_subsystems() to the variable
>>> .init.rodata.str:__func__.4400
>>> The function rebind_subsystems() references
>>> the variable __initconst __func__.4400.
>>> This is often because rebind_subsystems lacks a __initconst
>>> annotation or the annotation of __func__.4400 is wrong.
>>
>> Thanks for the report, you can find the fix here:
>> https://github.com/ephox-gcc-plugins/initify/commit/25f34834e3373e067133bc5d39d42c50a3592d56
>
> Awesome! I can confirm, it builds without warnings now. Thanks!

Hm, actually, with an "allyesconfig" build, I'm still seeing warnings
(and possibly some nocapture verification failures).

Most look like this:

WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
reference from the function clk_gate() to the variable
.init.rodata.str:__func__.29708
The function clk_gate() references
the variable __initconst __func__.29708.
This is often because clk_gate lacks a __initconst
annotation or the annotation of __func__.29708 is wrong.

And there's this (should KASAN be disabled for initify?)

mm/kasan/kasan.c: In function ‘memmove’:
mm/kasan/kasan.c:346:7: warning: ‘memmove’ captures its 2 (‘src’)
parameter, please remove it from the nocapture attribute.
 void *memmove(void *dest, const void *src, size_t len)
       ^
mm/kasan/kasan.c: In function ‘memcpy’:
mm/kasan/kasan.c:355:7: warning: ‘memcpy’ captures its 2 (‘src’)
parameter, please remove it from the nocapture attribute.
 void *memcpy(void *dest, const void *src, size_t len)
       ^

And ACPI:

drivers/acpi/acpica/utdebug.c: In function ‘acpi_debug_print’:
drivers/acpi/acpica/utdebug.c:158:1: warning: ‘acpi_debug_print’
captures its 3 (‘function_name’) parameter, please remove it from the
nocapture attribute.
 acpi_debug_print(u32 requested_debug_level,
 ^


I used my initify v5 development tree, with the following patch, with
"make allyesconfig":

http://git.kernel.org/cgit/linux/kernel/git/kees/linux.git/log/?h=for-next/gcc-plugin/initify

diff --git a/arch/Kconfig b/arch/Kconfig
index b6009a21ebea..5693ef5f22c8 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -359,7 +359,6 @@ config HAVE_GCC_PLUGINS
 menuconfig GCC_PLUGINS
        bool "GCC plugins"
        depends on HAVE_GCC_PLUGINS
-       depends on !COMPILE_TEST
        help
          GCC plugins are loadable modules that provide extra features to the
          compiler. They are useful for runtime instrumentation and
static analysis.
@@ -429,6 +428,7 @@ config GCC_PLUGIN_INITIFY
 config GCC_PLUGIN_INITIFY_VERBOSE
        bool "Report initification"
        depends on GCC_PLUGIN_INITIFY
+       depends on !COMPILE_TEST
        help
          Print all initified strings and all functions which should be
          __init/__exit.


I'll see if acpi needs __noverified_nocapture ...

-Kees

-- 
Kees Cook
Nexus Security

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-12 23:40               ` Kees Cook
@ 2017-01-17 20:31                 ` Emese Revfy
  2017-01-19  1:22                   ` Kees Cook
  2017-02-15  0:23                 ` Emese Revfy
  1 sibling, 1 reply; 23+ messages in thread
From: Emese Revfy @ 2017-01-17 20:31 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Thu, 12 Jan 2017 15:40:41 -0800
Kees Cook <keescook@chromium.org> wrote:

> WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
> reference from the function clk_gate() to the variable
> .init.rodata.str:__func__.29708
> The function clk_gate() references
> the variable __initconst __func__.29708.
> This is often because clk_gate lacks a __initconst
> annotation or the annotation of __func__.29708 is wrong.
>
> I used my initify v5 development tree, with the following patch, with
> "make allyesconfig":
> 
> http://git.kernel.org/cgit/linux/kernel/git/kees/linux.git/log/?h=for-next/gcc-plugin/initify

Hi,

I can't reproduce this mismatch with allyesconfig. Could you please send me your .config?

-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-17 20:31                 ` Emese Revfy
@ 2017-01-19  1:22                   ` Kees Cook
  0 siblings, 0 replies; 23+ messages in thread
From: Kees Cook @ 2017-01-19  1:22 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

[-- Attachment #1: Type: text/plain, Size: 982 bytes --]

On Tue, Jan 17, 2017 at 12:31 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Thu, 12 Jan 2017 15:40:41 -0800
> Kees Cook <keescook@chromium.org> wrote:
>
>> WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
>> reference from the function clk_gate() to the variable
>> .init.rodata.str:__func__.29708
>> The function clk_gate() references
>> the variable __initconst __func__.29708.
>> This is often because clk_gate lacks a __initconst
>> annotation or the annotation of __func__.29708 is wrong.
>>
>> I used my initify v5 development tree, with the following patch, with
>> "make allyesconfig":
>>
>> http://git.kernel.org/cgit/linux/kernel/git/kees/linux.git/log/?h=for-next/gcc-plugin/initify
>
> Hi,
>
> I can't reproduce this mismatch with allyesconfig. Could you please send me your .config?

Sure, attached. (Though note this requires the patch from my earlier
email to allow allyesconfig to work with the plugins.)

-Kees

-- 
Kees Cook
Nexus Security

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 57197 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-01-12 23:40               ` Kees Cook
  2017-01-17 20:31                 ` Emese Revfy
@ 2017-02-15  0:23                 ` Emese Revfy
  2017-02-15 19:27                   ` Kees Cook
  1 sibling, 1 reply; 23+ messages in thread
From: Emese Revfy @ 2017-02-15  0:23 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Thu, 12 Jan 2017 15:40:41 -0800
Kees Cook <keescook@chromium.org> wrote:

> WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
> reference from the function clk_gate() to the variable
> .init.rodata.str:__func__.29708
> The function clk_gate() references
> the variable __initconst __func__.29708.
> This is often because clk_gate lacks a __initconst
> annotation or the annotation of __func__.29708 is wrong.

Thanks for the report, it will be fixed in the next grsec patch.

-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-02-15  0:23                 ` Emese Revfy
@ 2017-02-15 19:27                   ` Kees Cook
  2017-02-20 21:42                     ` Emese Revfy
  0 siblings, 1 reply; 23+ messages in thread
From: Kees Cook @ 2017-02-15 19:27 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Tue, Feb 14, 2017 at 4:23 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Thu, 12 Jan 2017 15:40:41 -0800
> Kees Cook <keescook@chromium.org> wrote:
>
>> WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
>> reference from the function clk_gate() to the variable
>> .init.rodata.str:__func__.29708
>> The function clk_gate() references
>> the variable __initconst __func__.29708.
>> This is often because clk_gate lacks a __initconst
>> annotation or the annotation of __func__.29708 is wrong.
>
> Thanks for the report, it will be fixed in the next grsec patch.

Very cool, thanks! What did the problem turn out to be?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 0/4] Introduce the initify gcc plugin
  2017-02-15 19:27                   ` Kees Cook
@ 2017-02-20 21:42                     ` Emese Revfy
  0 siblings, 0 replies; 23+ messages in thread
From: Emese Revfy @ 2017-02-20 21:42 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, LKML, Arnd Bergmann, Josh Triplett, PaX Team,
	Brad Spengler, Michal Marek, Masahiro Yamada, linux-kbuild,
	minipli, Russell King, Catalin Marinas, Rasmus Villemoes,
	David Brown, benh, Thomas Gleixner, Andrew Morton, Jeff Layton,
	Sam Ravnborg

On Wed, 15 Feb 2017 11:27:06 -0800
Kees Cook <keescook@chromium.org> wrote:

> On Tue, Feb 14, 2017 at 4:23 PM, Emese Revfy <re.emese@gmail.com> wrote:
> > On Thu, 12 Jan 2017 15:40:41 -0800
> > Kees Cook <keescook@chromium.org> wrote:
> >
> >> WARNING: drivers/clk/bcm/built-in.o(.text+0xec2): Section mismatch in
> >> reference from the function clk_gate() to the variable
> >> .init.rodata.str:__func__.29708
> >> The function clk_gate() references
> >> the variable __initconst __func__.29708.
> >> This is often because clk_gate lacks a __initconst
> >> annotation or the annotation of __func__.29708 is wrong.
> >
> > Thanks for the report, it will be fixed in the next grsec patch.
> 
> Very cool, thanks! What did the problem turn out to be?

You can find it here:
https://github.com/ephox-gcc-plugins/initify/commit/73e857aa47bde7cc78ed865a008d1519893c0f54

-- 
Emese

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2017-02-20 21:42 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-16 22:06 [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
2016-12-16 22:06 ` [PATCH v4 1/4] gcc-plugins: Add " Kees Cook
2016-12-16 22:45   ` PaX Team
2016-12-16 23:02     ` Kees Cook
2016-12-16 23:15       ` PaX Team
2016-12-16 22:06 ` [PATCH v4 2/4] util: Move type casts into is_kernel_rodata Kees Cook
2016-12-16 22:06 ` [PATCH v4 3/4] initify: Mark functions with the __nocapture attribute Kees Cook
2016-12-16 22:06 ` [PATCH v4 4/4] initify: Mark functions with the __unverified_nocapture attribute Kees Cook
2016-12-16 22:19 ` [PATCH v4 0/4] Introduce the initify gcc plugin Kees Cook
2016-12-19 11:10   ` Emese Revfy
2017-01-04  0:23     ` Kees Cook
2017-01-11  0:24       ` Emese Revfy
2017-01-11  1:09         ` Kees Cook
2017-01-12 21:41           ` Emese Revfy
2017-01-12 23:27             ` Kees Cook
2017-01-12 23:40               ` Kees Cook
2017-01-17 20:31                 ` Emese Revfy
2017-01-19  1:22                   ` Kees Cook
2017-02-15  0:23                 ` Emese Revfy
2017-02-15 19:27                   ` Kees Cook
2017-02-20 21:42                     ` Emese Revfy
2016-12-19 18:24 ` Laura Abbott
2017-01-04  0:23   ` Kees Cook

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).