linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] Introduce the initify gcc plugin
@ 2016-07-04 23:39 Emese Revfy
  2016-07-04 23:40 ` [PATCH v2 1/3] Add " Emese Revfy
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-04 23:39 UTC (permalink / raw)
  To: kernel-hardening
  Cc: pageexec, spender, mmarek, keescook, linux-kernel,
	yamada.masahiro, linux-kbuild, minipli, linux, catalin.marinas,
	linux, david.brown, benh, tglx, akpm, jlayton, arnd, sam, isdn

I would like to introduce 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.

It is 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 6.0.

I made some 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 the "Add support for complex gcc plugins that
don't fit in a single file" patch set
(https://github.com/ephox-gcc-plugins/gcc-plugins_linux-next.git#initify
HEAD: e08eda5d85f7f52641640).

Some statistics about the plugin:

On allyes config (amd64, gcc-6):
* 7765 initified strings
*  231 initified functions

On allmod config (i386, gcc-6):
* 9177 initified strings
*  288 initified functions

On allyes config (amd64, gcc-6):

section         vanilla                 vanilla + initify        change
-----------------------------------------------------------------------
.rodata		19054824 (0x122c0e8)	18841832 (0x11f80e8)	-212992
.init.data	 1273768  (0x136fa8)	 1583496  (0x182988)	+309728
.text		46126301 (0x2bfd4dd)	46099805 (0x2bf6d5d)	 -26496
.init.text	  836339  (0x0cc2f3)	  855952  (0x0d0f90)	 +19613
.exit.data	     104  (0x000068)	   16736  (0x004160)	 +16632
.exit.text	  125511  (0x01ea47)	  133267  (0x020893)	  +7756

	FileSiz (vanilla)	FileSiz (vanilla + initify)	 change
------------------------------------------------------------------------
00	67727360 (0x4097000)	65417216 (0x3e63000)		-2310144
03	 2453504  (0x257000)	 2809856  (0x2ae000)		 +356352

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):
* 2044 initified strings
*   43 initified functions

On defconfig (amd64, gcc-6):

section         vanilla                 vanilla + initify        change
-----------------------------------------------------------------------
.rodata		2466736 (0x25a3b0)	2405296 (0x24b3b0)	-61440
.init.data	 564656 (0x089db0)	 645616 (0x09d9f0)	+80960
.text		9364727 (0x8ee4f7)	9360823 (0x8ed5b7)	 -3904
.init.text	 436097 (0x06a781)	 439161 (0x06b379)	 +3064
.exit.data	      0			    864 (0x000360)	  +864
.exit.text	   8806 (0x002266)	   9824 (0x002660)	 +1018

	FileSiz (vanilla)	FileSiz (vanilla + initify)	 change
------------------------------------------------------------------------
00	13340672 (0xcb9000)	13279232 (0xcaa000)		-61440
03	 1097728 (0x10c000)	 1183744 (0x121000)		+86016

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


Emese Revfy (3):
 Add the initify gcc plugin
 Mark functions with the __nocapture attribute
 Constify some function parameters


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

---
 arch/Kconfig                         |   23 +
 arch/arm/include/asm/string.h        |    4 +-
 arch/arm64/include/asm/string.h      |   19 +-
 arch/powerpc/include/asm/string.h    |   19 +-
 arch/x86/include/asm/string_32.h     |   21 +-
 arch/x86/include/asm/string_64.h     |   20 +-
 arch/x86/kernel/hpet.c               |    2 +-
 drivers/isdn/hisax/config.c          |    4 +-
 drivers/isdn/hisax/hisax.h           |    4 +-
 include/asm-generic/bug.h            |    6 +-
 include/asm-generic/vmlinux.lds.h    |    2 +
 include/linux/compiler-gcc.h         |   10 +-
 include/linux/compiler.h             |    4 +
 include/linux/fs.h                   |    5 +-
 include/linux/printk.h               |    2 +-
 include/linux/string.h               |   63 +-
 scripts/Makefile.gcc-plugins         |    4 +
 scripts/gcc-plugins/gcc-common.h     |   41 ++
 scripts/gcc-plugins/initify_plugin.c | 1183 ++++++++++++++++++++++++++++++++++
 19 files changed, 1353 insertions(+), 83 deletions(-)

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

* [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-04 23:39 [PATCH v2 0/3] Introduce the initify gcc plugin Emese Revfy
@ 2016-07-04 23:40 ` Emese Revfy
  2016-07-12 19:45   ` Kees Cook
  2016-07-04 23:42 ` [PATCH v2 2/3] Mark functions with the __nocapture attribute Emese Revfy
  2016-07-04 23:43 ` [PATCH v2 3/3] Constify some function parameters Emese Revfy
  2 siblings, 1 reply; 19+ messages in thread
From: Emese Revfy @ 2016-07-04 23:40 UTC (permalink / raw)
  To: kernel-hardening
  Cc: pageexec, spender, mmarek, keescook, linux-kernel,
	yamada.masahiro, linux-kbuild, minipli, linux, catalin.marinas,
	linux, david.brown, benh, tglx, akpm, jlayton, arnd, sam, isdn

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 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 the __initconst/__exitconst sections.
It reduces memory usage. 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>
---
 arch/Kconfig                         |   23 +
 include/asm-generic/vmlinux.lds.h    |    2 +
 scripts/Makefile.gcc-plugins         |    4 +
 scripts/gcc-plugins/gcc-common.h     |   41 ++
 scripts/gcc-plugins/initify_plugin.c | 1183 ++++++++++++++++++++++++++++++++++
 5 files changed, 1253 insertions(+)
 create mode 100644 scripts/gcc-plugins/initify_plugin.c

diff --git a/arch/Kconfig b/arch/Kconfig
index 1c91f52..0c877f6 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -417,6 +417,29 @@ 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 is the part of grsecurity/PaX. More information at:
+	   * https://grsecurity.net/
+	   * https://pax.grsecurity.net/
+
+config GCC_PLUGIN_INITIFY_VERBOSE
+	bool "Verbose"
+	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/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index ec3b78d..c73537a 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -531,6 +531,7 @@
 	MEM_DISCARD(init.data)						\
 	KERNEL_CTORS()							\
 	MCOUNT_REC()							\
+	*(.init.rodata.str)						\
 	*(.init.rodata)							\
 	FTRACE_EVENTS()							\
 	TRACE_SYSCALLS()						\
@@ -555,6 +556,7 @@
 #define EXIT_DATA							\
 	*(.exit.data)							\
 	MEM_DISCARD(exit.data)						\
+	*(.exit.rodata.str)						\
 	MEM_DISCARD(exit.rodata)
 
 #define EXIT_TEXT							\
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 1f922df..0ce8392 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -12,6 +12,10 @@ 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
+
   ifdef CONFIG_GCC_PLUGIN_SANCOV
     ifeq ($(CFLAGS_KCOV),)
       # It is needed because of the gcc-plugin.sh and gcc version checks.
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 172850b..7f2eb16 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -286,6 +286,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))
@@ -495,6 +515,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)
@@ -610,6 +638,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)
@@ -673,6 +706,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 0000000..1e66995
--- /dev/null
+++ b/scripts/gcc-plugins/initify_plugin.c
@@ -0,0 +1,1183 @@
+/*
+ * 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
+ *
+ * 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.
+ *
+ * Usage:
+ * $ make
+ * $ make run
+ */
+
+#include "gcc-common.h"
+
+int plugin_is_GPL_compatible;
+
+static struct plugin_info initify_plugin_info = {
+	.version	=	"20160701vanilla",
+	.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\tsearch function"
+				 " which should be marked by __init or __exit"
+				 " attribute\n"
+};
+
+static struct cgraph_2node_hook_list *node_duplication_hook_holder;
+#define ARGNUM_NONE 0
+static bool verbose, print_missing_attr, search_init_exit_functions;
+
+enum section_type {
+	INIT, EXIT, NONE
+};
+
+#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 bool pointer_set_contains(gimple_set *visited, const_gimple stmt)
+{
+	return visited->contains(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;
+}
+#else
+typedef struct pointer_set_t gimple_set;
+#endif
+
+static void walk_def_stmt(bool *has_str_cst, gimple_set *visited, tree node);
+
+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);
+}
+
+/* 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 tree handle_nocapture_attribute(tree *node, tree __unused name,
+					tree args, int __unused flags,
+					bool *no_add_attrs)
+{
+	tree orig_attr, arg, type_args = NULL_TREE;
+
+	*no_add_attrs = true;
+	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 NULL_TREE;
+	}
+
+	gcc_assert(type_args != NULL_TREE);
+
+	for (arg = args; arg; arg = TREE_CHAIN(arg)) {
+		const_tree type_arg, type;
+		int 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 NULL_TREE;
+		}
+
+		if (tree_int_cst_lt(position, integer_minus_one_node)) {
+			error("%qE parameter of the %qE attribute less than 0 (fn: %qE)",
+				position, name, *node);
+			return NULL_TREE;
+		}
+
+		idx = (int)tree_to_shwi(position);
+		if (idx == 0)
+			return NULL_TREE;
+		if (is_vararg_arg(type_args, idx))
+			break;
+
+		type_arg = chain_index(idx - 1, type_args);
+		type = TREE_VALUE(type_arg);
+
+		if (TREE_CODE(type) != POINTER_TYPE) {
+			error("%E. parameter of the %qE function must be a pointer",
+				position, *node);
+			return NULL_TREE;
+		}
+
+		if (!TYPE_READONLY(TREE_TYPE(type))) {
+			error("%E. parameter of the %qE function must be readonly",
+				position, *node);
+			return NULL_TREE;
+		}
+
+		if (TREE_THIS_VOLATILE(TREE_TYPE(type))) {
+			error("%E. parameter of the %qE function can't be volatile",
+				position, *node);
+			return NULL_TREE;
+		}
+
+	}
+
+	orig_attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(*node));
+	if (orig_attr)
+		chainon(TREE_VALUE(orig_attr), args);
+	else
+		*no_add_attrs = false;
+
+	return NULL_TREE;
+}
+
+static struct attribute_spec nocapture_attr = {
+	.name				= "nocapture",
+	.min_length			= 1,
+	.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 void register_attributes(void __unused *event_data, void __unused *data)
+{
+	register_attribute(&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)
+{
+	if (!strncmp(DECL_NAME_POINTER(fn), "sys_", 4))
+		return true;
+
+	if (!strncmp(DECL_NAME_POINTER(fn), "sys32_", 6))
+		return true;
+
+	if (!strncmp(DECL_NAME_POINTER(fn), "compat_sys_", 11))
+		return true;
+
+	return false;
+}
+
+static bool is_nocapture_param(const_tree fndecl, int fn_arg_count)
+{
+	const_tree attr, attr_val;
+	int fntype_arg_len;
+
+	if (is_syscall(fndecl))
+		return true;
+
+	fntype_arg_len = type_num_arguments(TREE_TYPE(fndecl));
+	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 = (int)tree_to_shwi(TREE_VALUE(attr_val));
+
+		if (attr_arg_val == -1)
+			return true;
+		if (attr_arg_val == fn_arg_count)
+			return true;
+		if (attr_arg_val > fntype_arg_len &&
+					fn_arg_count >= attr_arg_val)
+			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;
+
+	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 search_capture_use(const_tree vardecl, gimple stmt)
+{
+	unsigned int i;
+
+	for (i = 0; i < gimple_num_ops(stmt); i++) {
+		unsigned int arg_count;
+		const_tree fndecl, arg;
+		tree op = *(gimple_op_ptr(stmt, i));
+
+		if (op == NULL_TREE)
+			continue;
+		if (is_gimple_constant(op))
+			continue;
+
+		if (!compare_ops(vardecl, op))
+			continue;
+
+		if (!is_gimple_call(stmt))
+			return true;
+
+		/* return, fndecl */
+		gcc_assert(i >= 3);
+		arg_count = i - 2;
+
+		arg = gimple_call_arg(stmt, arg_count - 1);
+		gcc_assert(TREE_CODE(TREE_TYPE(arg)) == POINTER_TYPE);
+
+		fndecl = gimple_call_fndecl(stmt);
+		if (is_nocapture_param(fndecl, (int)arg_count))
+			continue;
+
+		gcc_assert(fndecl != NULL_TREE);
+
+		/*
+		 * 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_count);
+		return true;
+
+	}
+	return false;
+}
+
+static bool is_in_capture_init(const_tree vardecl)
+{
+	unsigned int i __unused;
+	tree var;
+
+	FOR_EACH_LOCAL_DECL(cfun, i, var) {
+		const_tree initial = DECL_INITIAL(var);
+
+		if (DECL_EXTERNAL(var))
+			continue;
+		if (initial == NULL_TREE)
+			continue;
+		if (TREE_CODE(initial) != CONSTRUCTOR)
+			continue;
+
+		gcc_assert(TREE_CODE(TREE_TYPE(var)) == 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;
+
+	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;
+	location_t loc = DECL_SOURCE_LOCATION(current_function_decl);
+
+	str = get_string_cst(node);
+	type = TREE_TYPE(str);
+	gcc_assert(TREE_CODE(type) == ARRAY_TYPE);
+	gcc_assert(TREE_TYPE(type) != NULL_TREE &&
+			TREE_CODE(TREE_TYPE(type)) == INTEGER_TYPE);
+	name = create_tmp_var_name("initify");
+	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(tree ssa_var, gphi *stmt, unsigned int i)
+{
+	gassign *assign;
+	gimple_stmt_iterator gsi;
+	basic_block arg_bb;
+	tree decl, arg;
+
+	arg = gimple_phi_arg_def(stmt, i);
+	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)
+		inform(gimple_location(stmt), "initified local var, phi arg: %E: [%E]",
+			current_function_decl, get_string_cst(arg));
+}
+
+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)
+			walk_def_stmt(has_str_cst, visited, arg);
+		else
+			initify_create_new_phi_arg(ssa_var, stmt, i);
+	}
+}
+
+static void walk_def_stmt(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) {
+		*has_str_cst = false;
+		return;
+	}
+
+	parm_decl = SSA_NAME_VAR(node);
+	if (parm_decl != NULL_TREE && TREE_CODE(parm_decl) == PARM_DECL) {
+		*has_str_cst = false;
+		return;
+	}
+
+	def_stmt = SSA_NAME_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:
+		*has_str_cst = false;
+		return;
+
+	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)
+			return;
+
+		rhs1 = gimple_assign_rhs1(def_stmt);
+		walk_def_stmt(has_str_cst, visited, rhs1);
+		if (!*has_str_cst)
+			return;
+		str = get_string_cst(rhs1);
+		if (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();
+	}
+}
+
+/* Search constant strings assigned to variables. */
+static void search_var_param(gcall *stmt)
+{
+	unsigned int num;
+
+	for (num = 0; num < gimple_call_num_args(stmt); num++) {
+		gimple_set *visited;
+		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_nocapture_param(fndecl, num + 1))
+			continue;
+
+		visited = pointer_set_create();
+		walk_def_stmt(&has_str_cst, visited, arg);
+		pointer_set_destroy(visited);
+	}
+}
+
+/* Search constant strings passed as arguments. */
+static void search_str_param(gcall *stmt)
+{
+	unsigned int num;
+
+	for (num = 0; num < 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_nocapture_param(fndecl, num + 1))
+			set_section_call_assign(stmt, arg, num);
+	}
+}
+
+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));
+	return attr != NULL_TREE;
+}
+
+/* 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);
+		}
+	}
+}
+
+/*
+ * Find and move constant strings to the proper init or exit read-only
+ * data section.
+ */
+static unsigned int initify_execute(void)
+{
+	if (get_init_exit_section(current_function_decl) == NONE)
+		return 0;
+
+	find_local_str();
+	search_const_strs();
+
+	return 0;
+}
+
+#define PASS_NAME initify
+#define NO_GATE
+#define TODO_FLAGS_FINISH	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
+
+#include "gcc-generate-gimple-pass.h"
+
+static bool search_init_functions_gate(void)
+{
+	return search_init_exit_functions;
+}
+
+/*
+ * 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 == EXIT && NODE_SYMBOL(callee)->aux == (void *)INIT)
+		goto set_section;
+
+	if (!should_init_exit(callee))
+		return false;
+
+	gcc_assert(NODE_SYMBOL(callee)->aux == (void *)NONE);
+
+set_section:
+	NODE_SYMBOL(callee)->aux = (void *)curfn_section;
+	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\000"))
+		return true;
+
+	if (!strcmp(section_name, ".meminit.text\000"))
+		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 (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 search_init_functions_execute(void)
+{
+	struct cgraph_node *node;
+
+	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;
+}
+
+/* Find the specified argument in the clone */
+static unsigned int orig_argnum_on_clone(struct cgraph_node *new_node,
+						unsigned int orig_argnum)
+{
+	bitmap args_to_skip;
+	unsigned int i, new_argnum = orig_argnum;
+
+	gcc_assert(new_node->clone_of && new_node->clone.tree_map);
+	args_to_skip = new_node->clone.args_to_skip;
+	if (bitmap_bit_p(args_to_skip, orig_argnum - 1))
+		return 0;
+
+	for (i = 0; i < orig_argnum; i++) {
+		if (bitmap_bit_p(args_to_skip, i))
+			new_argnum--;
+	}
+
+	return new_argnum + 1;
+}
+
+/* Determine if a cloned function has all the original arguments */
+static bool unchanged_arglist(struct cgraph_node *new_node,
+				struct cgraph_node *old_node)
+{
+	const_tree new_decl_list, old_decl_list;
+
+	if (new_node->clone_of && new_node->clone.tree_map)
+		return !new_node->clone.args_to_skip;
+
+	new_decl_list = DECL_ARGUMENTS(NODE_DECL(new_node));
+	old_decl_list = DECL_ARGUMENTS(NODE_DECL(old_node));
+	if (new_decl_list != NULL_TREE && old_decl_list != NULL_TREE)
+		gcc_assert(list_length(new_decl_list) ==
+						list_length(old_decl_list));
+
+	return true;
+}
+
+static void initify_node_duplication_hook(struct cgraph_node *src,
+						struct cgraph_node *dst,
+						void *data __unused)
+{
+	const_tree orig_fndecl, orig_decl_lst, arg;
+	unsigned int orig_argnum = 0;
+
+	if (unchanged_arglist(dst, src))
+		return;
+
+	orig_fndecl = NODE_DECL(src);
+	if (!has_nocapture_param(orig_fndecl))
+		return;
+
+	orig_decl_lst = DECL_ARGUMENTS(orig_fndecl);
+	gcc_assert(orig_decl_lst != NULL_TREE);
+
+	for (arg = orig_decl_lst; arg; arg = TREE_CHAIN(arg), orig_argnum++) {
+		if (!is_nocapture_param(orig_fndecl, orig_argnum))
+			continue;
+		if (orig_argnum_on_clone(dst, orig_argnum) == 0)
+			continue;
+
+		debug_cgraph_node(dst);
+		debug_cgraph_node(src);
+		gcc_unreachable();
+	}
+}
+
+static void initify_register_hooks(void)
+{
+	static bool init_p = false;
+
+	if (init_p)
+		return;
+	init_p = true;
+
+	node_duplication_hook_holder =
+		cgraph_add_node_duplication_hook(&initify_node_duplication_hook,
+									NULL);
+}
+
+static void search_init_functions_generate_summary(void)
+{
+	initify_register_hooks();
+}
+
+static void search_init_functions_read_summary(void)
+{
+	initify_register_hooks();
+}
+
+#define PASS_NAME search_init_functions
+#define NO_WRITE_SUMMARY
+#define NO_READ_OPTIMIZATION_SUMMARY
+#define NO_WRITE_OPTIMIZATION_SUMMARY
+#define NO_STMT_FIXUP
+#define NO_FUNCTION_TRANSFORM
+#define NO_VARIABLE_TRANSFORM
+
+#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_pass_info, search_init_functions_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_pass_info.pass				= make_initify_pass();
+	initify_pass_info.reference_pass_name		= "nrv";
+	initify_pass_info.ref_pass_instance_number	= 1;
+	initify_pass_info.pos_op			= PASS_POS_INSERT_AFTER;
+
+	search_init_functions_info.pass = make_search_init_functions_pass();
+	search_init_functions_info.reference_pass_name		= "inline";
+	search_init_functions_info.ref_pass_instance_number	= 1;
+	search_init_functions_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;
+		}
+
+		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_pass_info);
+		register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+						&search_init_functions_info);
+		register_callback(plugin_name, PLUGIN_START_UNIT,
+						initify_start_unit, NULL);
+	}
+	register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
+									NULL);
+
+	return 0;
+}
-- 
2.8.1

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

* [PATCH v2 2/3] Mark functions with the __nocapture attribute
  2016-07-04 23:39 [PATCH v2 0/3] Introduce the initify gcc plugin Emese Revfy
  2016-07-04 23:40 ` [PATCH v2 1/3] Add " Emese Revfy
@ 2016-07-04 23:42 ` Emese Revfy
  2016-07-12 19:08   ` Kees Cook
  2016-07-04 23:43 ` [PATCH v2 3/3] Constify some function parameters Emese Revfy
  2 siblings, 1 reply; 19+ messages in thread
From: Emese Revfy @ 2016-07-04 23:42 UTC (permalink / raw)
  To: kernel-hardening
  Cc: pageexec, spender, mmarek, keescook, linux-kernel,
	yamada.masahiro, linux-kbuild, minipli, linux, catalin.marinas,
	linux, david.brown, benh, tglx, akpm, jlayton, arnd, sam, isdn

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.

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

Signed-off-by: Emese Revfy <re.emese@gmail.com>
---
 arch/arm/include/asm/string.h     |  4 +--
 arch/arm64/include/asm/string.h   | 19 ++++++------
 arch/powerpc/include/asm/string.h | 19 ++++++------
 arch/x86/include/asm/string_32.h  | 21 ++++++-------
 arch/x86/include/asm/string_64.h  | 20 ++++++-------
 arch/x86/kernel/hpet.c            |  2 +-
 include/asm-generic/bug.h         |  6 ++--
 include/linux/compiler-gcc.h      | 10 +++++--
 include/linux/compiler.h          |  4 +++
 include/linux/fs.h                |  5 ++--
 include/linux/printk.h            |  2 +-
 include/linux/string.h            | 63 ++++++++++++++++++++-------------------
 12 files changed, 96 insertions(+), 79 deletions(-)

diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index cf4f3aa..ddb9d58 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -13,10 +13,10 @@ extern char * strrchr(const char * s, int c);
 extern char * strchr(const char * s, int c);
 
 #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);
diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
index 2eb714c..4263a27 100644
--- a/arch/arm64/include/asm/string.h
+++ b/arch/arm64/include/asm/string.h
@@ -23,24 +23,25 @@ extern char *strrchr(const char *, int c);
 extern char *strchr(const char *, int c);
 
 #define __HAVE_ARCH_STRCMP
-extern int strcmp(const char *, const char *);
+extern int strcmp(const char *, const char *) __nocapture(1, 2);
 
 #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);
@@ -50,7 +51,7 @@ 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 da3cdff..e67f1eb 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(1, 2);
+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 3d3e835..63f29bf 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(1, 2);
 
 #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);
 
 #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,7 +198,7 @@ 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
 
@@ -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(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 90dbbd9..607d3ba 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -27,8 +27,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
@@ -56,14 +56,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__)
 
@@ -89,7 +89,7 @@ int strcmp(const char *cs, const char *ct);
  *
  * Return 0 for success, -EFAULT for fail
  */
-int memcpy_mcsafe(void *dst, const void *src, size_t cnt);
+int memcpy_mcsafe(void *dst, const void *src, size_t cnt) __nocapture(2);
 
 #endif /* __KERNEL__ */
 
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 357900a..c68ab1a 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/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 6f96247..630d0c4 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)
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index df88c0a..192cea4 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -116,8 +116,10 @@
  */
 #define __pure			__attribute__((pure))
 #define __aligned(x)		__attribute__((aligned(x)))
-#define __printf(a, b)		__attribute__((format(printf, a, b)))
-#define __scanf(a, b)		__attribute__((format(scanf, a, b)))
+#define __printf(a, b)		__attribute__((format(printf, a, b))) \
+				__nocapture(a, b)
+#define __scanf(a, b)		__attribute__((format(scanf, a, b))) \
+				__nocapture(a, b)
 #define __attribute_const__	__attribute__((__const__))
 #define __maybe_unused		__attribute__((unused))
 #define __always_unused		__attribute__((unused))
@@ -193,6 +195,10 @@
 # define __latent_entropy	__attribute__((latent_entropy))
 #endif
 
+#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
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 0adcfc2..391b48b 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -412,6 +412,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/include/linux/fs.h b/include/linux/fs.h
index c4ab2cf..048a0d9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2476,8 +2476,9 @@ 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 e6ff22e..ca137fa 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -163,7 +163,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/string.h b/include/linux/string.h
index 26b6f6a..0a56912 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -18,37 +18,38 @@ 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);
+ssize_t __must_check strscpy(char *, const char *, size_t) __nocapture(2);
 #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(1, 2);
 #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(1, 2);
 #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);
@@ -72,44 +73,44 @@ 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(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(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(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(1, 2);
 #endif
 #ifndef __HAVE_ARCH_STRCSPN
-extern __kernel_size_t strcspn(const char *,const char *);
+extern __kernel_size_t strcspn(const char *,const char *) __nocapture(1, 2);
 #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);
@@ -119,16 +120,16 @@ 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(1, 2);
+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
 
-- 
2.8.1

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

* [PATCH v2 3/3] Constify some function parameters
  2016-07-04 23:39 [PATCH v2 0/3] Introduce the initify gcc plugin Emese Revfy
  2016-07-04 23:40 ` [PATCH v2 1/3] Add " Emese Revfy
  2016-07-04 23:42 ` [PATCH v2 2/3] Mark functions with the __nocapture attribute Emese Revfy
@ 2016-07-04 23:43 ` Emese Revfy
  2016-07-04 23:58   ` kbuild test robot
  2016-07-06 16:44   ` Emese Revfy
  2 siblings, 2 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-04 23:43 UTC (permalink / raw)
  To: kernel-hardening
  Cc: pageexec, spender, mmarek, keescook, linux-kernel,
	yamada.masahiro, linux-kbuild, minipli, linux, catalin.marinas,
	linux, david.brown, benh, tglx, akpm, jlayton, arnd, sam, isdn

Initify needs const pointer types, the initify plugin caught some __printf
arguments that weren't const yet.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
---
 drivers/isdn/hisax/config.c | 4 ++--
 drivers/isdn/hisax/hisax.h  | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
index bf04d2a..a7d53c9 100644
--- a/drivers/isdn/hisax/config.c
+++ b/drivers/isdn/hisax/config.c
@@ -659,7 +659,7 @@ int jiftime(char *s, long mark)
 
 static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
 
-void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt,
 		      va_list args)
 {
 	/* if head == NULL the fmt contains the full info */
@@ -729,7 +729,7 @@ void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
 	}
 }
 
-void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, ...)
 {
 	va_list args;
 
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index 6ead6314..338d040 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -1288,9 +1288,9 @@ int jiftime(char *s, long mark);
 int HiSax_command(isdn_ctrl *ic);
 int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
 __printf(3, 4)
-void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, ...);
 __printf(3, 0)
-void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, va_list args);
 void HiSax_reportcard(int cardnr, int sel);
 int QuickHex(char *txt, u_char *p, int cnt);
 void LogFrame(struct IsdnCardState *cs, u_char *p, int size);
-- 
2.8.1

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

* Re: [PATCH v2 3/3] Constify some function parameters
  2016-07-04 23:43 ` [PATCH v2 3/3] Constify some function parameters Emese Revfy
@ 2016-07-04 23:58   ` kbuild test robot
  2016-07-06 16:45     ` Emese Revfy
  2016-07-06 16:44   ` Emese Revfy
  1 sibling, 1 reply; 19+ messages in thread
From: kbuild test robot @ 2016-07-04 23:58 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kbuild-all, kernel-hardening, pageexec, spender, mmarek,
	keescook, linux-kernel, yamada.masahiro, linux-kbuild, minipli,
	linux, catalin.marinas, linux, david.brown, benh, tglx, akpm,
	jlayton, arnd, sam, isdn

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

Hi,

[auto build test WARNING on next-20160704]
[cannot apply to tip/x86/core asm-generic/master v4.7-rc6 v4.7-rc5 v4.7-rc4 v4.7-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Emese-Revfy/Introduce-the-initify-gcc-plugin/20160705-074117
config: sparc64-allyesconfig (attached as .config)
compiler: sparc64-linux-gnu-gcc (Debian 5.3.1-8) 5.3.1 20160205
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=sparc64 

All warnings (new ones prefixed by >>):

   drivers/isdn/hisax/config.c: In function 'VHiSax_putstatus':
>> drivers/isdn/hisax/config.c:688:5: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
      p = fmt;
        ^

vim +/const +688 drivers/isdn/hisax/config.c

^1da177e Linus Torvalds 2005-04-16  672  
^1da177e Linus Torvalds 2005-04-16  673  	if (!cs) {
^1da177e Linus Torvalds 2005-04-16  674  		printk(KERN_WARNING "HiSax: No CardStatus for message");
^1da177e Linus Torvalds 2005-04-16  675  		return;
^1da177e Linus Torvalds 2005-04-16  676  	}
^1da177e Linus Torvalds 2005-04-16  677  	spin_lock_irqsave(&cs->statlock, flags);
^1da177e Linus Torvalds 2005-04-16  678  	p = tmpbuf;
^1da177e Linus Torvalds 2005-04-16  679  	if (head) {
^1da177e Linus Torvalds 2005-04-16  680  		p += jiftime(p, jiffies);
^1da177e Linus Torvalds 2005-04-16  681  		p += sprintf(p, " %s", head);
^1da177e Linus Torvalds 2005-04-16  682  		p += vsprintf(p, fmt, args);
^1da177e Linus Torvalds 2005-04-16  683  		*p++ = '\n';
^1da177e Linus Torvalds 2005-04-16  684  		*p = 0;
^1da177e Linus Torvalds 2005-04-16  685  		len = p - tmpbuf;
^1da177e Linus Torvalds 2005-04-16  686  		p = tmpbuf;
^1da177e Linus Torvalds 2005-04-16  687  	} else {
^1da177e Linus Torvalds 2005-04-16 @688  		p = fmt;
^1da177e Linus Torvalds 2005-04-16  689  		len = strlen(fmt);
^1da177e Linus Torvalds 2005-04-16  690  	}
^1da177e Linus Torvalds 2005-04-16  691  	if (len > HISAX_STATUS_BUFSIZE) {
^1da177e Linus Torvalds 2005-04-16  692  		spin_unlock_irqrestore(&cs->statlock, flags);
^1da177e Linus Torvalds 2005-04-16  693  		printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
^1da177e Linus Torvalds 2005-04-16  694  		       len, HISAX_STATUS_BUFSIZE);
^1da177e Linus Torvalds 2005-04-16  695  		return;
^1da177e Linus Torvalds 2005-04-16  696  	}

:::::: The code at line 688 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2

:::::: TO: Linus Torvalds <torvalds@ppc970.osdl.org>
:::::: CC: Linus Torvalds <torvalds@ppc970.osdl.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 46918 bytes --]

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

* [PATCH v2 3/3] Constify some function parameters
  2016-07-04 23:43 ` [PATCH v2 3/3] Constify some function parameters Emese Revfy
  2016-07-04 23:58   ` kbuild test robot
@ 2016-07-06 16:44   ` Emese Revfy
  1 sibling, 0 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-06 16:44 UTC (permalink / raw)
  To: kernel-hardening
  Cc: pageexec, spender, mmarek, keescook, linux-kernel,
	yamada.masahiro, linux-kbuild, minipli, linux, catalin.marinas,
	linux, david.brown, benh, tglx, akpm, jlayton, arnd, sam, isdn

Initify needs const pointer types, the initify plugin caught some __printf
arguments that weren't const yet.

Signed-off-by: Emese Revfy <re.emese@gmail.com>
---
 drivers/isdn/hisax/config.c | 16 ++++++++--------
 drivers/isdn/hisax/hisax.h  |  4 ++--
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
index bf04d2a..2d12c6c 100644
--- a/drivers/isdn/hisax/config.c
+++ b/drivers/isdn/hisax/config.c
@@ -659,7 +659,7 @@ int jiftime(char *s, long mark)
 
 static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
 
-void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt,
 		      va_list args)
 {
 	/* if head == NULL the fmt contains the full info */
@@ -669,23 +669,24 @@ void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
 	u_char		*p;
 	isdn_ctrl	ic;
 	int		len;
+	const u_char	*data;
 
 	if (!cs) {
 		printk(KERN_WARNING "HiSax: No CardStatus for message");
 		return;
 	}
 	spin_lock_irqsave(&cs->statlock, flags);
-	p = tmpbuf;
 	if (head) {
+		p = tmpbuf;
 		p += jiftime(p, jiffies);
 		p += sprintf(p, " %s", head);
 		p += vsprintf(p, fmt, args);
 		*p++ = '\n';
 		*p = 0;
 		len = p - tmpbuf;
-		p = tmpbuf;
+		data = tmpbuf;
 	} else {
-		p = fmt;
+		data = fmt;
 		len = strlen(fmt);
 	}
 	if (len > HISAX_STATUS_BUFSIZE) {
@@ -699,13 +700,12 @@ void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
 	if (i >= len)
 		i = len;
 	len -= i;
-	memcpy(cs->status_write, p, i);
+	memcpy(cs->status_write, data, i);
 	cs->status_write += i;
 	if (cs->status_write > cs->status_end)
 		cs->status_write = cs->status_buf;
-	p += i;
 	if (len) {
-		memcpy(cs->status_write, p, len);
+		memcpy(cs->status_write, data + i, len);
 		cs->status_write += len;
 	}
 #ifdef KERNELSTACK_DEBUG
@@ -729,7 +729,7 @@ void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
 	}
 }
 
-void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, ...)
 {
 	va_list args;
 
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index 6ead6314..338d040 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -1288,9 +1288,9 @@ int jiftime(char *s, long mark);
 int HiSax_command(isdn_ctrl *ic);
 int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
 __printf(3, 4)
-void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, ...);
 __printf(3, 0)
-void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, const char *fmt, va_list args);
 void HiSax_reportcard(int cardnr, int sel);
 int QuickHex(char *txt, u_char *p, int cnt);
 void LogFrame(struct IsdnCardState *cs, u_char *p, int size);
-- 
2.8.1

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

* Re: [PATCH v2 3/3] Constify some function parameters
  2016-07-04 23:58   ` kbuild test robot
@ 2016-07-06 16:45     ` Emese Revfy
  0 siblings, 0 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-06 16:45 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, kernel-hardening, pageexec, spender, mmarek,
	keescook, linux-kernel, yamada.masahiro, linux-kbuild, minipli,
	linux, catalin.marinas, linux, david.brown, benh, tglx, akpm,
	jlayton, arnd, sam, isdn

On Tue, 5 Jul 2016 07:58:04 +0800
kbuild test robot <lkp@intel.com> wrote:

> All warnings (new ones prefixed by >>):
> 
>    drivers/isdn/hisax/config.c: In function 'VHiSax_putstatus':
> >> drivers/isdn/hisax/config.c:688:5: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
>       p = fmt;

Hi,

Thanks for the report, I resent "[PATCH v2 3/3] Constify some function parameters" with the fix.

-- 
Emese

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

* Re: [PATCH v2 2/3] Mark functions with the __nocapture attribute
  2016-07-04 23:42 ` [PATCH v2 2/3] Mark functions with the __nocapture attribute Emese Revfy
@ 2016-07-12 19:08   ` Kees Cook
  2016-07-12 19:23     ` [kernel-hardening] " Daniel Micay
  0 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2016-07-12 19:08 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Mon, Jul 4, 2016 at 7:42 PM, Emese Revfy <re.emese@gmail.com> wrote:
> 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.

Why is this called "nocapture"? Not captured by what? It seems like it
means "initify this if possible". Am I misunderstanding its purpose?

-Kees

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

Has anyone had a chance to try this out on these architectures? I'll
be testing arm shortly...

-Kees

>
> Signed-off-by: Emese Revfy <re.emese@gmail.com>
> ---
>  arch/arm/include/asm/string.h     |  4 +--
>  arch/arm64/include/asm/string.h   | 19 ++++++------
>  arch/powerpc/include/asm/string.h | 19 ++++++------
>  arch/x86/include/asm/string_32.h  | 21 ++++++-------
>  arch/x86/include/asm/string_64.h  | 20 ++++++-------
>  arch/x86/kernel/hpet.c            |  2 +-
>  include/asm-generic/bug.h         |  6 ++--
>  include/linux/compiler-gcc.h      | 10 +++++--
>  include/linux/compiler.h          |  4 +++
>  include/linux/fs.h                |  5 ++--
>  include/linux/printk.h            |  2 +-
>  include/linux/string.h            | 63 ++++++++++++++++++++-------------------
>  12 files changed, 96 insertions(+), 79 deletions(-)
>
> diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
> index cf4f3aa..ddb9d58 100644
> --- a/arch/arm/include/asm/string.h
> +++ b/arch/arm/include/asm/string.h
> @@ -13,10 +13,10 @@ extern char * strrchr(const char * s, int c);
>  extern char * strchr(const char * s, int c);
>
>  #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);
> diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
> index 2eb714c..4263a27 100644
> --- a/arch/arm64/include/asm/string.h
> +++ b/arch/arm64/include/asm/string.h
> @@ -23,24 +23,25 @@ extern char *strrchr(const char *, int c);
>  extern char *strchr(const char *, int c);
>
>  #define __HAVE_ARCH_STRCMP
> -extern int strcmp(const char *, const char *);
> +extern int strcmp(const char *, const char *) __nocapture(1, 2);
>
>  #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);
> @@ -50,7 +51,7 @@ 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 da3cdff..e67f1eb 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(1, 2);
> +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 3d3e835..63f29bf 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(1, 2);
>
>  #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);
>
>  #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,7 +198,7 @@ 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
>
> @@ -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(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 90dbbd9..607d3ba 100644
> --- a/arch/x86/include/asm/string_64.h
> +++ b/arch/x86/include/asm/string_64.h
> @@ -27,8 +27,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
> @@ -56,14 +56,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__)
>
> @@ -89,7 +89,7 @@ int strcmp(const char *cs, const char *ct);
>   *
>   * Return 0 for success, -EFAULT for fail
>   */
> -int memcpy_mcsafe(void *dst, const void *src, size_t cnt);
> +int memcpy_mcsafe(void *dst, const void *src, size_t cnt) __nocapture(2);
>
>  #endif /* __KERNEL__ */
>
> diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
> index 357900a..c68ab1a 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/include/asm-generic/bug.h b/include/asm-generic/bug.h
> index 6f96247..630d0c4 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)
> diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
> index df88c0a..192cea4 100644
> --- a/include/linux/compiler-gcc.h
> +++ b/include/linux/compiler-gcc.h
> @@ -116,8 +116,10 @@
>   */
>  #define __pure                 __attribute__((pure))
>  #define __aligned(x)           __attribute__((aligned(x)))
> -#define __printf(a, b)         __attribute__((format(printf, a, b)))
> -#define __scanf(a, b)          __attribute__((format(scanf, a, b)))
> +#define __printf(a, b)         __attribute__((format(printf, a, b))) \
> +                               __nocapture(a, b)
> +#define __scanf(a, b)          __attribute__((format(scanf, a, b))) \
> +                               __nocapture(a, b)
>  #define __attribute_const__    __attribute__((__const__))
>  #define __maybe_unused         __attribute__((unused))
>  #define __always_unused                __attribute__((unused))
> @@ -193,6 +195,10 @@
>  # define __latent_entropy      __attribute__((latent_entropy))
>  #endif
>
> +#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
> diff --git a/include/linux/compiler.h b/include/linux/compiler.h
> index 0adcfc2..391b48b 100644
> --- a/include/linux/compiler.h
> +++ b/include/linux/compiler.h
> @@ -412,6 +412,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/include/linux/fs.h b/include/linux/fs.h
> index c4ab2cf..048a0d9 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2476,8 +2476,9 @@ 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 e6ff22e..ca137fa 100644
> --- a/include/linux/printk.h
> +++ b/include/linux/printk.h
> @@ -163,7 +163,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/string.h b/include/linux/string.h
> index 26b6f6a..0a56912 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -18,37 +18,38 @@ 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);
> +ssize_t __must_check strscpy(char *, const char *, size_t) __nocapture(2);
>  #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(1, 2);
>  #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(1, 2);
>  #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);
> @@ -72,44 +73,44 @@ 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(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(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(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(1, 2);
>  #endif
>  #ifndef __HAVE_ARCH_STRCSPN
> -extern __kernel_size_t strcspn(const char *,const char *);
> +extern __kernel_size_t strcspn(const char *,const char *) __nocapture(1, 2);
>  #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);
> @@ -119,16 +120,16 @@ 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(1, 2);
> +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
>
> --
> 2.8.1
>



-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [kernel-hardening] Re: [PATCH v2 2/3] Mark functions with the __nocapture attribute
  2016-07-12 19:08   ` Kees Cook
@ 2016-07-12 19:23     ` Daniel Micay
  2016-07-12 19:47       ` Kees Cook
  0 siblings, 1 reply; 19+ messages in thread
From: Daniel Micay @ 2016-07-12 19:23 UTC (permalink / raw)
  To: kernel-hardening, Emese Revfy
  Cc: PaX Team, Brad Spengler, Michal Marek, LKML, Masahiro Yamada,
	linux-kbuild, minipli, Russell King, Catalin Marinas,
	Rasmus Villemoes, David Brown, benh, Thomas Gleixner,
	Andrew Morton, Jeff Layton, Arnd Bergmann, Sam Ravnborg,
	Karsten Keil

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

On Tue, 2016-07-12 at 15:08 -0400, Kees Cook wrote:
> On Mon, Jul 4, 2016 at 7:42 PM, Emese Revfy <re.emese@gmail.com>
> wrote:
> > 
> > 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.
> 
> Why is this called "nocapture"? Not captured by what? It seems like
> it
> means "initify this if possible". Am I misunderstanding its purpose?

It means they don't escape via that function, i.e. they aren't stored
anywhere to be used in any way after the call.

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 851 bytes --]

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-04 23:40 ` [PATCH v2 1/3] Add " Emese Revfy
@ 2016-07-12 19:45   ` Kees Cook
  2016-07-12 20:07     ` Emese Revfy
                       ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Kees Cook @ 2016-07-12 19:45 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
> 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 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 the __initconst/__exitconst sections.
> It reduces memory usage. 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.

Should the mismatch checker be updated to recognize this case? Without
the plugin, I assume these kinds of functions would only ever be
marked for __exit? If so, should the plugin strip the __init marking
and only add __exit?

> 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>

Thanks for sending this! I'll get it added to my tree for some 0day
build testing, and then get it into my -next tree.

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [kernel-hardening] Re: [PATCH v2 2/3] Mark functions with the __nocapture attribute
  2016-07-12 19:23     ` [kernel-hardening] " Daniel Micay
@ 2016-07-12 19:47       ` Kees Cook
  0 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2016-07-12 19:47 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Emese Revfy, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, Jul 12, 2016 at 3:23 PM, Daniel Micay <danielmicay@gmail.com> wrote:
> On Tue, 2016-07-12 at 15:08 -0400, Kees Cook wrote:
>> On Mon, Jul 4, 2016 at 7:42 PM, Emese Revfy <re.emese@gmail.com>
>> wrote:
>> >
>> > 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.
>>
>> Why is this called "nocapture"? Not captured by what? It seems like
>> it
>> means "initify this if possible". Am I misunderstanding its purpose?
>
> It means they don't escape via that function, i.e. they aren't stored
> anywhere to be used in any way after the call.

Ah, those arguments are confirmed to not be "captured" by that
function, leaving them available to be moved to .init. Got it, thanks!

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 20:07     ` Emese Revfy
@ 2016-07-12 20:05       ` Kees Cook
  2016-07-13 20:34         ` Emese Revfy
  0 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2016-07-12 20:05 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, Jul 12, 2016 at 4:07 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Tue, 12 Jul 2016 15:45:56 -0400
> Kees Cook <keescook@chromium.org> wrote:
>
>> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
>> Thanks for sending this! I'll get it added to my tree for some 0day
>> build testing, and then get it into my -next tree.
>
> I'll send a new patch set soon. I'm working on some fixes and
> I would like to implement the handling of return uses to increase the coverage
> (which decreases because of the bug fixes :) ).
> So please hold off with testing until the next patch.

Okay, cool. I'll just do some simple tests with what I have so I can
feel like I understand its intended use, etc. :)

One change I made was to add this comment in compiler-gcc.h for people
that become curious about __nocapture and go looking for its
definition (please feel free to adjust for accuracy, etc):


/*
 * The initify gcc-plugin attempts to identify const arguments that are only
 * used during init (see __init), so they can be moved to the .init.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 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


-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 19:45   ` Kees Cook
@ 2016-07-12 20:07     ` Emese Revfy
  2016-07-12 20:05       ` Kees Cook
  2016-07-12 22:08     ` Russell King - ARM Linux
  2016-07-13 20:48     ` Emese Revfy
  2 siblings, 1 reply; 19+ messages in thread
From: Emese Revfy @ 2016-07-12 20:07 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, 12 Jul 2016 15:45:56 -0400
Kees Cook <keescook@chromium.org> wrote:

> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
> Thanks for sending this! I'll get it added to my tree for some 0day
> build testing, and then get it into my -next tree.

I'll send a new patch set soon. I'm working on some fixes and
I would like to implement the handling of return uses to increase the coverage
(which decreases because of the bug fixes :) ).
So please hold off with testing until the next patch.

-- 
Emese

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 19:45   ` Kees Cook
  2016-07-12 20:07     ` Emese Revfy
@ 2016-07-12 22:08     ` Russell King - ARM Linux
  2016-07-12 22:38       ` Kees Cook
  2016-07-13 20:48     ` Emese Revfy
  2 siblings, 1 reply; 19+ messages in thread
From: Russell King - ARM Linux @ 2016-07-12 22:08 UTC (permalink / raw)
  To: Kees Cook
  Cc: Emese Revfy, kernel-hardening, PaX Team, Brad Spengler,
	Michal Marek, LKML, Masahiro Yamada, linux-kbuild, minipli,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, Jul 12, 2016 at 03:45:56PM -0400, Kees Cook wrote:
> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
> > 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.
> 
> Should the mismatch checker be updated to recognize this case? Without
> the plugin, I assume these kinds of functions would only ever be
> marked for __exit? If so, should the plugin strip the __init marking
> and only add __exit?

That sounds like a problem for architectures that still discard the
__exit section at link time to reduce the size of the linked kernel
image - though, obviously, if using the plugin results in a smaller
kernel image _with_ the exit sections, then there's a net benefit
size-wise.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 22:08     ` Russell King - ARM Linux
@ 2016-07-12 22:38       ` Kees Cook
  2016-07-13 21:26         ` Emese Revfy
  0 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2016-07-12 22:38 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Emese Revfy, kernel-hardening, PaX Team, Brad Spengler,
	Michal Marek, LKML, Masahiro Yamada, linux-kbuild, minipli,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, Jul 12, 2016 at 6:08 PM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Tue, Jul 12, 2016 at 03:45:56PM -0400, Kees Cook wrote:
>> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
>> > 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.
>>
>> Should the mismatch checker be updated to recognize this case? Without
>> the plugin, I assume these kinds of functions would only ever be
>> marked for __exit? If so, should the plugin strip the __init marking
>> and only add __exit?
>
> That sounds like a problem for architectures that still discard the
> __exit section at link time to reduce the size of the linked kernel
> image - though, obviously, if using the plugin results in a smaller
> kernel image _with_ the exit sections, then there's a net benefit
> size-wise.

Ah right, __exit is dropped for non-modular builds. So, for "both
__init and __exit" it sounds like the behavior depends on the build:

- if modular: remove __init marking (since we need it after init)
- if non-modular: remove __exit marking (since we'll never call exit)

Is this something the build itself (rather than the plugin) could
notice and fix up? Hmmm

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 20:05       ` Kees Cook
@ 2016-07-13 20:34         ` Emese Revfy
  0 siblings, 0 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-13 20:34 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, 12 Jul 2016 16:05:45 -0400
Kees Cook <keescook@chromium.org> wrote:

> /*
>  * The initify gcc-plugin attempts to identify const arguments that are only
>  * used during init (see __init), so they can be moved to the .init.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 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

Thanks, I'll take it in the next patch set with some additions (the attribute
also handles __exit functions and the plugin does other things e.g., it can
identify candidate init/exit functions and move them automatically to init.text/exit.text).

-- 
Emese

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 19:45   ` Kees Cook
  2016-07-12 20:07     ` Emese Revfy
  2016-07-12 22:08     ` Russell King - ARM Linux
@ 2016-07-13 20:48     ` Emese Revfy
  2016-07-13 21:04       ` Kees Cook
  2 siblings, 1 reply; 19+ messages in thread
From: Emese Revfy @ 2016-07-13 20:48 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, 12 Jul 2016 15:45:56 -0400
Kees Cook <keescook@chromium.org> wrote:

> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
> > 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.
> 
> Should the mismatch checker be updated to recognize this case? Without
> the plugin, I assume these kinds of functions would only ever be
> marked for __exit? If so, should the plugin strip the __init marking
> and only add __exit?

I don't modify the existing attributes. I just add a new __init/__exit when
a function hasn't a section attribute yet.
There are three cases:
 * when the function is called only by __init functions then the plugin adds
   the __init attribute
 * when the function is called only by __exit functions then the plugin adds
   the __exit attribute
 * when the function is called by __init and __exit functions too then the
   plugin adds the __exit attribute.
The last case causes the false positive(?) message of the section mismatch.

-- 
Emese

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-13 20:48     ` Emese Revfy
@ 2016-07-13 21:04       ` Kees Cook
  0 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2016-07-13 21:04 UTC (permalink / raw)
  To: Emese Revfy
  Cc: kernel-hardening, PaX Team, Brad Spengler, Michal Marek, LKML,
	Masahiro Yamada, linux-kbuild, minipli, Russell King,
	Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Wed, Jul 13, 2016 at 1:48 PM, Emese Revfy <re.emese@gmail.com> wrote:
> On Tue, 12 Jul 2016 15:45:56 -0400
> Kees Cook <keescook@chromium.org> wrote:
>
>> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
>> > 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.
>>
>> Should the mismatch checker be updated to recognize this case? Without
>> the plugin, I assume these kinds of functions would only ever be
>> marked for __exit? If so, should the plugin strip the __init marking
>> and only add __exit?
>
> I don't modify the existing attributes. I just add a new __init/__exit when
> a function hasn't a section attribute yet.
> There are three cases:
>  * when the function is called only by __init functions then the plugin adds
>    the __init attribute
>  * when the function is called only by __exit functions then the plugin adds
>    the __exit attribute
>  * when the function is called by __init and __exit functions too then the
>    plugin adds the __exit attribute.
> The last case causes the false positive(?) message of the section mismatch.

In the latter case, how does the linker actually choose where to put
such a function?

For a modular build, if it puts it in .init, it will be missing during
exit. If it puts it in .exit, that seems correct.

For a non-modular build, if it puts it in .init, this is correct. If
it puts it in .exit, it may be missing for init because the exit
section may have already been removed at final link time.

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

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

* Re: [PATCH v2 1/3] Add the initify gcc plugin
  2016-07-12 22:38       ` Kees Cook
@ 2016-07-13 21:26         ` Emese Revfy
  0 siblings, 0 replies; 19+ messages in thread
From: Emese Revfy @ 2016-07-13 21:26 UTC (permalink / raw)
  To: Kees Cook
  Cc: Russell King - ARM Linux, kernel-hardening, PaX Team,
	Brad Spengler, Michal Marek, LKML, Masahiro Yamada, linux-kbuild,
	minipli, Catalin Marinas, Rasmus Villemoes, David Brown, benh,
	Thomas Gleixner, Andrew Morton, Jeff Layton, Arnd Bergmann,
	Sam Ravnborg, Karsten Keil

On Tue, 12 Jul 2016 18:38:47 -0400
Kees Cook <keescook@chromium.org> wrote:

> On Tue, Jul 12, 2016 at 6:08 PM, Russell King - ARM Linux
> <linux@armlinux.org.uk> wrote:
> > On Tue, Jul 12, 2016 at 03:45:56PM -0400, Kees Cook wrote:
> >> On Mon, Jul 4, 2016 at 7:40 PM, Emese Revfy <re.emese@gmail.com> wrote:
> > That sounds like a problem for architectures that still discard the
> > __exit section at link time to reduce the size of the linked kernel
> > image - though, obviously, if using the plugin results in a smaller
> > kernel image _with_ the exit sections, then there's a net benefit
> > size-wise.
> 
> Ah right, __exit is dropped for non-modular builds. So, for "both
> __init and __exit" it sounds like the behavior depends on the build:
> 
> - if modular: remove __init marking (since we need it after init)
> - if non-modular: remove __exit marking (since we'll never call exit)

When gcc compiles vmlinux these functions should be in __init and when
it compiles *.ko then they can be in __exit. I have no time to do this
now but I added it to my todo list.

The temporary fix can be that I enable this section move only on x86
(on other archs it will decrase the coverage).

-- 
Emese

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

end of thread, other threads:[~2016-07-13 21:20 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-04 23:39 [PATCH v2 0/3] Introduce the initify gcc plugin Emese Revfy
2016-07-04 23:40 ` [PATCH v2 1/3] Add " Emese Revfy
2016-07-12 19:45   ` Kees Cook
2016-07-12 20:07     ` Emese Revfy
2016-07-12 20:05       ` Kees Cook
2016-07-13 20:34         ` Emese Revfy
2016-07-12 22:08     ` Russell King - ARM Linux
2016-07-12 22:38       ` Kees Cook
2016-07-13 21:26         ` Emese Revfy
2016-07-13 20:48     ` Emese Revfy
2016-07-13 21:04       ` Kees Cook
2016-07-04 23:42 ` [PATCH v2 2/3] Mark functions with the __nocapture attribute Emese Revfy
2016-07-12 19:08   ` Kees Cook
2016-07-12 19:23     ` [kernel-hardening] " Daniel Micay
2016-07-12 19:47       ` Kees Cook
2016-07-04 23:43 ` [PATCH v2 3/3] Constify some function parameters Emese Revfy
2016-07-04 23:58   ` kbuild test robot
2016-07-06 16:45     ` Emese Revfy
2016-07-06 16:44   ` Emese Revfy

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).