summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2017-02-17 11:21:50 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2017-02-17 11:21:50 +1100
commitcd73d1675614a817ff3ea3fbf32e9d8251d7828b (patch)
treecfb12928c3022310328d2fbc16326332b72d2a70
parent3e8e4b94d54386657b5786965967b5089f9a3402 (diff)
parent5fe5cb271594c90f11730a24ceaca5bebe9f82af (diff)
Merge remote-tracking branch 'kspp/for-next/kspp'
-rw-r--r--Documentation/DocBook/kgdb.tmpl8
-rw-r--r--Documentation/security/self-protection.txt10
-rw-r--r--arch/Kconfig96
-rw-r--r--arch/arm/Kconfig4
-rw-r--r--arch/arm/Kconfig.debug11
-rw-r--r--arch/arm/configs/aspeed_g4_defconfig4
-rw-r--r--arch/arm/configs/aspeed_g5_defconfig4
-rw-r--r--arch/arm/include/asm/cacheflush.h2
-rw-r--r--arch/arm/include/asm/string.h10
-rw-r--r--arch/arm/kernel/patch.c4
-rw-r--r--arch/arm/kernel/vmlinux.lds.S8
-rw-r--r--arch/arm/mm/Kconfig14
-rw-r--r--arch/arm/mm/init.c4
-rw-r--r--arch/arm64/Kconfig6
-rw-r--r--arch/arm64/Kconfig.debug13
-rw-r--r--arch/arm64/include/asm/string.h25
-rw-r--r--arch/arm64/kernel/insn.c2
-rw-r--r--arch/parisc/Kconfig1
-rw-r--r--arch/parisc/Kconfig.debug11
-rw-r--r--arch/parisc/configs/712_defconfig1
-rw-r--r--arch/parisc/configs/c3000_defconfig1
-rw-r--r--arch/parisc/mm/init.c2
-rw-r--r--arch/powerpc/include/asm/string.h19
-rw-r--r--arch/s390/Kconfig5
-rw-r--r--arch/s390/Kconfig.debug4
-rw-r--r--arch/x86/Kconfig6
-rw-r--r--arch/x86/Kconfig.debug11
-rw-r--r--arch/x86/include/asm/string_32.h25
-rw-r--r--arch/x86/include/asm/string_64.h23
-rw-r--r--arch/x86/kernel/hpet.c2
-rw-r--r--arch/x86/lib/Makefile4
-rw-r--r--drivers/acpi/acpica/acutils.h2
-rw-r--r--fs/char_dev.c2
-rw-r--r--fs/ntfs/debug.h6
-rw-r--r--fs/ocfs2/cluster/masklog.h2
-rw-r--r--include/acpi/acpixf.h2
-rw-r--r--include/asm-generic/asm-prototypes.h8
-rw-r--r--include/asm-generic/bug.h7
-rw-r--r--include/asm-generic/vmlinux.lds.h2
-rw-r--r--include/drm/drm_drv.h2
-rw-r--r--include/linux/audit.h5
-rw-r--r--include/linux/compiler-gcc.h22
-rw-r--r--include/linux/compiler.h14
-rw-r--r--include/linux/filter.h4
-rw-r--r--include/linux/fs.h8
-rw-r--r--include/linux/init.h4
-rw-r--r--include/linux/module.h2
-rw-r--r--include/linux/printk.h2
-rw-r--r--include/linux/ratelimit.h3
-rw-r--r--include/linux/string.h75
-rw-r--r--init/main.c4
-rw-r--r--kernel/configs/android-recommended.config2
-rw-r--r--kernel/module.c6
-rw-r--r--kernel/power/hibernate.c2
-rw-r--r--kernel/power/power.h4
-rw-r--r--kernel/power/snapshot.c4
-rw-r--r--lib/string.c3
-rw-r--r--lib/test_user_copy.c116
-rw-r--r--lib/vsprintf.c6
-rw-r--r--mm/kasan/kasan.c2
-rw-r--r--mm/util.c10
-rw-r--r--scripts/Makefile.gcc-plugins18
-rw-r--r--scripts/gcc-plugins/cyc_complexity_plugin.c6
-rw-r--r--scripts/gcc-plugins/gcc-common.h55
-rw-r--r--scripts/gcc-plugins/initify_plugin.c1860
-rw-r--r--scripts/gcc-plugins/latent_entropy_plugin.c8
-rw-r--r--scripts/gcc-plugins/sancov_plugin.c8
-rw-r--r--scripts/gcc-plugins/structleak_plugin.c246
-rw-r--r--security/integrity/integrity.h2
69 files changed, 2603 insertions, 271 deletions
diff --git a/Documentation/DocBook/kgdb.tmpl b/Documentation/DocBook/kgdb.tmpl
index f3abca7ec53d..856ac20bf367 100644
--- a/Documentation/DocBook/kgdb.tmpl
+++ b/Documentation/DocBook/kgdb.tmpl
@@ -115,12 +115,12 @@
</para>
<para>
If the architecture that you are using supports the kernel option
- CONFIG_DEBUG_RODATA, you should consider turning it off. This
+ CONFIG_STRICT_KERNEL_RWX, you should consider turning it off. This
option will prevent the use of software breakpoints because it
marks certain regions of the kernel's memory space as read-only.
If kgdb supports it for the architecture you are using, you can
use hardware breakpoints if you desire to run with the
- CONFIG_DEBUG_RODATA option turned on, else you need to turn off
+ CONFIG_STRICT_KERNEL_RWX option turned on, else you need to turn off
this option.
</para>
<para>
@@ -135,7 +135,7 @@
<para>Here is an example set of .config symbols to enable or
disable for kgdb:
<itemizedlist>
- <listitem><para># CONFIG_DEBUG_RODATA is not set</para></listitem>
+ <listitem><para># CONFIG_STRICT_KERNEL_RWX is not set</para></listitem>
<listitem><para>CONFIG_FRAME_POINTER=y</para></listitem>
<listitem><para>CONFIG_KGDB=y</para></listitem>
<listitem><para>CONFIG_KGDB_SERIAL_CONSOLE=y</para></listitem>
@@ -166,7 +166,7 @@
</para>
<para>Here is an example set of .config symbols to enable/disable kdb:
<itemizedlist>
- <listitem><para># CONFIG_DEBUG_RODATA is not set</para></listitem>
+ <listitem><para># CONFIG_STRICT_KERNEL_RWX is not set</para></listitem>
<listitem><para>CONFIG_FRAME_POINTER=y</para></listitem>
<listitem><para>CONFIG_KGDB=y</para></listitem>
<listitem><para>CONFIG_KGDB_SERIAL_CONSOLE=y</para></listitem>
diff --git a/Documentation/security/self-protection.txt b/Documentation/security/self-protection.txt
index 3010576c9fca..141acfebe6ef 100644
--- a/Documentation/security/self-protection.txt
+++ b/Documentation/security/self-protection.txt
@@ -51,11 +51,17 @@ kernel, they are implemented in a way where the memory is temporarily
made writable during the update, and then returned to the original
permissions.)
-In support of this are (the poorly named) CONFIG_DEBUG_RODATA and
-CONFIG_DEBUG_SET_MODULE_RONX, which seek to make sure that code is not
+In support of this are CONFIG_STRICT_KERNEL_RWX and
+CONFIG_STRICT_MODULE_RWX, which seek to make sure that code is not
writable, data is not executable, and read-only data is neither writable
nor executable.
+Most architectures have these options on by default and not user selectable.
+For some architectures like arm that wish to have these be selectable,
+the architecture Kconfig can select ARCH_OPTIONAL_KERNEL_RWX to enable
+a Kconfig prompt. CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT determines
+the default setting when ARCH_OPTIONAL_KERNEL_RWX is enabled.
+
#### Function pointers and sensitive variables must not be writable
Vast areas of kernel memory contain function pointers that are looked
diff --git a/arch/Kconfig b/arch/Kconfig
index bd04eace455c..b6e78dc56935 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -410,6 +410,68 @@ config GCC_PLUGIN_LATENT_ENTROPY
* https://grsecurity.net/
* https://pax.grsecurity.net/
+config GCC_PLUGIN_STRUCTLEAK
+ bool "Force initialization of variables containing userspace addresses"
+ depends on GCC_PLUGINS
+ help
+ This plugin zero-initializes any structures that containing a
+ __user attribute. This can prevent some classes of information
+ exposures.
+
+ This plugin was ported from grsecurity/PaX. More information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
+config GCC_PLUGIN_STRUCTLEAK_VERBOSE
+ bool "Report initialized variables"
+ depends on GCC_PLUGIN_STRUCTLEAK
+ depends on !COMPILE_TEST
+ help
+ This option will cause a warning to be printed each time the
+ structleak plugin finds a variable it thinks needs to be
+ initialized. Since not all existing initializers are detected
+ by the plugin, this can produce false positive warnings.
+
+config GCC_PLUGIN_INITIFY
+ bool "Free more kernel memory after init"
+ depends on GCC_PLUGINS
+ help
+ The kernel has a mechanism to free up code and data memory that is
+ only used during kernel or module initialization. Enabling this
+ feature will teach the compiler to find more such code and data
+ that can be freed after initialization.
+
+ This plugin currently triggers false positive warnings from the
+ section mismatch checker.
+
+ This plugin is the part of grsecurity/PaX. More information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
+config GCC_PLUGIN_INITIFY_VERBOSE
+ bool "Report initification"
+ depends on GCC_PLUGIN_INITIFY
+ depends on !COMPILE_TEST
+ help
+ Print all initified strings and all functions which should be
+ __init/__exit.
+ 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_GCC_PLUGIN_INITIFY_INIT_EXIT
+ bool
+ depends on GCC_PLUGINS
+ help
+ The architecture supports moving functions to the exit section
+ if they are called by both __init and __exit functions. This
+ requires that exit sections are not removed from non-modular
+ builds.
+
+config GCC_PLUGIN_INITIFY_INIT_EXIT
+ bool
+ depends on GCC_PLUGIN_INITIFY && HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT
+
config HAVE_CC_STACKPROTECTOR
bool
help
@@ -784,4 +846,38 @@ config VMAP_STACK
config ARCH_WANT_RELAX_ORDER
bool
+config ARCH_OPTIONAL_KERNEL_RWX
+ def_bool n
+
+config ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
+ def_bool n
+
+config ARCH_HAS_STRICT_KERNEL_RWX
+ def_bool n
+
+config STRICT_KERNEL_RWX
+ bool "Make kernel text and rodata read-only" if ARCH_OPTIONAL_KERNEL_RWX
+ depends on ARCH_HAS_STRICT_KERNEL_RWX
+ default !ARCH_OPTIONAL_KERNEL_RWX || ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
+ help
+ If this is set, kernel text and rodata memory will be made read-only,
+ and non-text memory will be made non-executable. This provides
+ protection against certain security exploits (e.g. executing the heap
+ or modifying text)
+
+ These features are considered standard security practice these days.
+ You should say Y here in almost all cases.
+
+config ARCH_HAS_STRICT_MODULE_RWX
+ def_bool n
+
+config STRICT_MODULE_RWX
+ bool "Set loadable kernel module data as NX and text as RO" if ARCH_OPTIONAL_KERNEL_RWX
+ depends on ARCH_HAS_STRICT_MODULE_RWX && MODULES
+ default !ARCH_OPTIONAL_KERNEL_RWX || ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
+ help
+ If this is set, module text and rodata memory will be made read-only,
+ and non-text memory will be made non-executable. This provides
+ protection against certain security exploits (e.g. writing to text)
+
source "kernel/gcov/Kconfig"
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index bf8d86d5c2b2..71cea9e7e746 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -5,10 +5,14 @@ config ARM
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
+ select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
+ select ARCH_HAS_STRICT_MODULE_RWX if MMU
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAVE_CUSTOM_GPIO_H
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_MIGHT_HAVE_PC_PARPORT
+ select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
+ select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT if CPU_V7
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index d83f7c369e51..426d2716f55d 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1738,17 +1738,6 @@ config PID_IN_CONTEXTIDR
additional instructions during context switch. Say Y here only if you
are planning to use hardware trace tools with this kernel.
-config DEBUG_SET_MODULE_RONX
- bool "Set loadable kernel module data as NX and text as RO"
- depends on MODULES && MMU
- ---help---
- This option helps catch unintended modifications to loadable
- kernel module's text and read-only data. It also prevents execution
- of module data. Such protection may interfere with run-time code
- patching and dynamic kernel tracing - and they might also protect
- against certain classes of kernel exploits.
- If in doubt, say "N".
-
source "drivers/hwtracing/coresight/Kconfig"
endmenu
diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig
index 2ea82272c2cb..d25010e3a0bc 100644
--- a/arch/arm/configs/aspeed_g4_defconfig
+++ b/arch/arm/configs/aspeed_g4_defconfig
@@ -25,7 +25,6 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_ARCH_MULTI_V7 is not set
CONFIG_ARCH_ASPEED=y
CONFIG_MACH_ASPEED_G4=y
-CONFIG_DEBUG_RODATA=y
CONFIG_AEABI=y
CONFIG_UACCESS_WITH_MEMCPY=y
CONFIG_SECCOMP=y
@@ -109,7 +108,8 @@ CONFIG_DEBUG_LL_UART_8250=y
CONFIG_DEBUG_UART_PHYS=0x1e784000
CONFIG_DEBUG_UART_VIRT=0xe8784000
CONFIG_EARLY_PRINTK=y
-CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_STRICT_MODULE_RWX=y
+CONFIG_STRICT_KERNEL_RWX=y
# CONFIG_XZ_DEC_X86 is not set
# CONFIG_XZ_DEC_POWERPC is not set
# CONFIG_XZ_DEC_IA64 is not set
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index ae4295ec5c3e..5f660b02abd9 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -26,7 +26,6 @@ CONFIG_ARCH_MULTI_V6=y
# CONFIG_ARCH_MULTI_V7 is not set
CONFIG_ARCH_ASPEED=y
CONFIG_MACH_ASPEED_G5=y
-CONFIG_DEBUG_RODATA=y
CONFIG_VMSPLIT_2G=y
CONFIG_AEABI=y
CONFIG_UACCESS_WITH_MEMCPY=y
@@ -116,7 +115,8 @@ CONFIG_DEBUG_LL_UART_8250=y
CONFIG_DEBUG_UART_PHYS=0x1e784000
CONFIG_DEBUG_UART_VIRT=0xe8784000
CONFIG_EARLY_PRINTK=y
-CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_STRICT_MODULE_RWX=y
+CONFIG_STRICT_KERNEL_RWX=y
CONFIG_CRYPTO_ECHAINIV=y
# CONFIG_XZ_DEC_X86 is not set
# CONFIG_XZ_DEC_POWERPC is not set
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index bdd283bc5842..02454fa15d2c 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -490,7 +490,7 @@ static inline int set_memory_x(unsigned long addr, int numpages) { return 0; }
static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
#endif
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
#else
diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index cf4f3aad0fc1..8f2f2d9a6549 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -7,19 +7,19 @@
*/
#define __HAVE_ARCH_STRRCHR
-extern char * strrchr(const char * s, int c);
+extern char * strrchr(const char * s, int c) __nocapture(-1);
#define __HAVE_ARCH_STRCHR
-extern char * strchr(const char * s, int c);
+extern char * strchr(const char * s, int c) __nocapture(-1);
#define __HAVE_ARCH_MEMCPY
-extern void * memcpy(void *, const void *, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
#define __HAVE_ARCH_MEMMOVE
-extern void * memmove(void *, const void *, __kernel_size_t);
+extern void * memmove(void *, const void *, __kernel_size_t) __nocapture(2);
#define __HAVE_ARCH_MEMCHR
-extern void * memchr(const void *, int, __kernel_size_t);
+extern void * memchr(const void *, int, __kernel_size_t) __nocapture(-1);
#define __HAVE_ARCH_MEMSET
extern void * memset(void *, int, __kernel_size_t);
diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c
index 69bda1a5707e..020560b2dcb7 100644
--- a/arch/arm/kernel/patch.c
+++ b/arch/arm/kernel/patch.c
@@ -24,9 +24,9 @@ static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags)
bool module = !core_kernel_text(uintaddr);
struct page *page;
- if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX))
+ if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
page = vmalloc_to_page(addr);
- else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA))
+ else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
page = virt_to_page(addr);
else
return addr;
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index f7f55df0bf7b..ce18007f9e4e 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -97,7 +97,7 @@ SECTIONS
HEAD_TEXT
}
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
. = ALIGN(1<<SECTION_SHIFT);
#endif
@@ -158,7 +158,7 @@ SECTIONS
NOTES
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
. = ALIGN(1<<SECTION_SHIFT);
#else
. = ALIGN(PAGE_SIZE);
@@ -230,7 +230,7 @@ SECTIONS
PERCPU_SECTION(L1_CACHE_BYTES)
#endif
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
. = ALIGN(1<<SECTION_SHIFT);
#else
. = ALIGN(THREAD_SIZE);
@@ -325,7 +325,7 @@ SECTIONS
STABS_DEBUG
}
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
/*
* Without CONFIG_DEBUG_ALIGN_RODATA, __start_rodata_section_aligned will
* be the first section-aligned location after __start_rodata. Otherwise,
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index ac395eca7dee..c6c4c9c8824b 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -1072,21 +1072,9 @@ config ARCH_SUPPORTS_BIG_ENDIAN
This option specifies the architecture can support big endian
operation.
-config DEBUG_RODATA
- bool "Make kernel text and rodata read-only"
- depends on MMU && !XIP_KERNEL
- default y if CPU_V7
- help
- If this is set, kernel text and rodata memory will be made
- read-only, and non-text kernel memory will be made non-executable.
- The tradeoff is that each region is padded to section-size (1MiB)
- boundaries (because their permissions are different and splitting
- the 1M pages into 4K ones causes TLB performance problems), which
- can waste memory.
-
config DEBUG_ALIGN_RODATA
bool "Make rodata strictly non-executable"
- depends on DEBUG_RODATA
+ depends on STRICT_KERNEL_RWX
default y
help
If this is set, rodata will be made explicitly non-executable. This
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index d1e26610977d..bf4d3bc41a7a 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -590,7 +590,7 @@ void __init mem_init(void)
}
}
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
struct section_perm {
const char *name;
unsigned long start;
@@ -759,7 +759,7 @@ void set_kernel_text_ro(void)
#else
static inline void fix_kernmem_perms(void) { }
-#endif /* CONFIG_DEBUG_RODATA */
+#endif /* CONFIG_STRICT_KERNEL_RWX */
void free_tcmmem(void)
{
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index d2fe685b9026..2fa0e91955e8 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -14,6 +14,8 @@ config ARM64
select ARCH_HAS_GIGANTIC_PAGE
select ARCH_HAS_KCOV
select ARCH_HAS_SG_CHAIN
+ select ARCH_HAS_STRICT_KERNEL_RWX
+ select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_SUPPORTS_ATOMIC_RMW
@@ -84,6 +86,7 @@ config ARM64
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_GCC_PLUGINS
+ select HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT if GCC_PLUGINS
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_IRQ_TIME_ACCOUNTING
@@ -124,9 +127,6 @@ config ARCH_PHYS_ADDR_T_64BIT
config MMU
def_bool y
-config DEBUG_RODATA
- def_bool y
-
config ARM64_PAGE_SHIFT
int
default 16 if ARM64_64K_PAGES
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index f7f38b1aab14..fca2f02cde68 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -71,19 +71,8 @@ config DEBUG_WX
If in doubt, say "Y".
-config DEBUG_SET_MODULE_RONX
- bool "Set loadable kernel module data as NX and text as RO"
- depends on MODULES
- default y
- help
- Is this is set, kernel module text and rodata will be made read-only.
- This is to help catch accidental or malicious attempts to change the
- kernel's executable code.
-
- If in doubt, say Y.
-
config DEBUG_ALIGN_RODATA
- depends on DEBUG_RODATA
+ depends on STRICT_KERNEL_RWX
bool "Align linker sections up to SECTION_SIZE"
help
If this option is enabled, sections that may potentially be marked as
diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
index 2eb714c4639f..188a1022c764 100644
--- a/arch/arm64/include/asm/string.h
+++ b/arch/arm64/include/asm/string.h
@@ -17,40 +17,41 @@
#define __ASM_STRING_H
#define __HAVE_ARCH_STRRCHR
-extern char *strrchr(const char *, int c);
+extern char *strrchr(const char *, int c) __nocapture(-1);
#define __HAVE_ARCH_STRCHR
-extern char *strchr(const char *, int c);
+extern char *strchr(const char *, int c) __nocapture(-1);
#define __HAVE_ARCH_STRCMP
-extern int strcmp(const char *, const char *);
+extern int strcmp(const char *, const char *) __nocapture();
#define __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *, const char *, __kernel_size_t);
+extern int
+strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
#define __HAVE_ARCH_STRLEN
-extern __kernel_size_t strlen(const char *);
+extern __kernel_size_t strlen(const char *) __nocapture(1);
#define __HAVE_ARCH_STRNLEN
-extern __kernel_size_t strnlen(const char *, __kernel_size_t);
+extern __kernel_size_t strnlen(const char *, __kernel_size_t) __nocapture(1);
#define __HAVE_ARCH_MEMCPY
-extern void *memcpy(void *, const void *, __kernel_size_t);
-extern void *__memcpy(void *, const void *, __kernel_size_t);
+extern void *memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
+extern void *__memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
#define __HAVE_ARCH_MEMMOVE
-extern void *memmove(void *, const void *, __kernel_size_t);
-extern void *__memmove(void *, const void *, __kernel_size_t);
+extern void *memmove(void *, const void *, __kernel_size_t) __nocapture(2);
+extern void *__memmove(void *, const void *, __kernel_size_t) __nocapture(2);
#define __HAVE_ARCH_MEMCHR
-extern void *memchr(const void *, int, __kernel_size_t);
+extern void *memchr(const void *, int, __kernel_size_t) __nocapture(-1);
#define __HAVE_ARCH_MEMSET
extern void *memset(void *, int, __kernel_size_t);
extern void *__memset(void *, int, __kernel_size_t);
#define __HAVE_ARCH_MEMCMP
-extern int memcmp(const void *, const void *, size_t);
+extern int memcmp(const void *, const void *, size_t) __nocapture(1, 2);
#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 022d4a9d1738..b6badff5a151 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -93,7 +93,7 @@ static void __kprobes *patch_map(void *addr, int fixmap)
bool module = !core_kernel_text(uintaddr);
struct page *page;
- if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX))
+ if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
page = vmalloc_to_page(addr);
else if (!module)
page = phys_to_page(__pa_symbol(addr));
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 3a71f38cdc05..ad294b3fb90b 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -8,6 +8,7 @@ config PARISC
select HAVE_SYSCALL_TRACEPOINTS
select ARCH_WANT_FRAME_POINTERS
select ARCH_HAS_ELF_RANDOMIZE
+ select ARCH_HAS_STRICT_KERNEL_RWX
select RTC_CLASS
select RTC_DRV_GENERIC
select INIT_ALL_POSSIBLE
diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug
index 68b7cbd0810a..0d856b94c9b1 100644
--- a/arch/parisc/Kconfig.debug
+++ b/arch/parisc/Kconfig.debug
@@ -5,15 +5,4 @@ source "lib/Kconfig.debug"
config TRACE_IRQFLAGS_SUPPORT
def_bool y
-config DEBUG_RODATA
- bool "Write protect kernel read-only data structures"
- depends on DEBUG_KERNEL
- default y
- help
- Mark the kernel read-only data as write-protected in the pagetables,
- in order to catch accidental (and incorrect) writes to such const
- data. This option may have a slight performance impact because a
- portion of the kernel code won't be covered by a TLB anymore.
- If in doubt, say "N".
-
endmenu
diff --git a/arch/parisc/configs/712_defconfig b/arch/parisc/configs/712_defconfig
index db8f56bf3883..143d02652792 100644
--- a/arch/parisc/configs/712_defconfig
+++ b/arch/parisc/configs/712_defconfig
@@ -182,7 +182,6 @@ CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_DEBUG_RODATA=y
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_TEST=m
CONFIG_CRYPTO_HMAC=y
diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig
index fb92b8920785..8e8f0e34f817 100644
--- a/arch/parisc/configs/c3000_defconfig
+++ b/arch/parisc/configs/c3000_defconfig
@@ -166,7 +166,6 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_DEBUG_RODATA=y
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_TEST=m
CONFIG_CRYPTO_MD5=m
diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
index e02ada312be8..a055e5b6b380 100644
--- a/arch/parisc/mm/init.c
+++ b/arch/parisc/mm/init.c
@@ -545,7 +545,7 @@ void free_initmem(void)
}
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
void mark_rodata_ro(void)
{
/* rodata memory was already mapped with KERNEL_RO access rights by
diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h
index da3cdffca440..ba08cf461ca2 100644
--- a/arch/powerpc/include/asm/string.h
+++ b/arch/powerpc/include/asm/string.h
@@ -11,16 +11,17 @@
#define __HAVE_ARCH_MEMCMP
#define __HAVE_ARCH_MEMCHR
-extern char * strcpy(char *,const char *);
-extern char * strncpy(char *,const char *, __kernel_size_t);
-extern __kernel_size_t strlen(const char *);
-extern int strcmp(const char *,const char *);
-extern int strncmp(const char *, const char *, __kernel_size_t);
-extern char * strcat(char *, const char *);
+extern char * strcpy(char *,const char *) __nocapture(2);
+extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
+extern __kernel_size_t strlen(const char *) __nocapture(1);
+extern int strcmp(const char *,const char *) __nocapture();
+extern int
+strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
+extern char * strcat(char *, const char *) __nocapture(2);
extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
-extern void * memmove(void *,const void *,__kernel_size_t);
-extern int memcmp(const void *,const void *,__kernel_size_t);
+extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
+extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
+extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
extern void * memchr(const void *,int,__kernel_size_t);
#endif /* __KERNEL__ */
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index c6722112527d..53bb0e3e0db3 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -62,9 +62,6 @@ config PCI_QUIRKS
config ARCH_SUPPORTS_UPROBES
def_bool y
-config DEBUG_RODATA
- def_bool y
-
config S390
def_bool y
select ARCH_HAS_DEVMEM_IS_ALLOWED
@@ -73,6 +70,8 @@ config S390
select ARCH_HAS_GIGANTIC_PAGE
select ARCH_HAS_KCOV
select ARCH_HAS_SG_CHAIN
+ select ARCH_HAS_STRICT_KERNEL_RWX
+ select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_INLINE_READ_LOCK
diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug
index 32b3e3bfd022..ba5f878a295c 100644
--- a/arch/s390/Kconfig.debug
+++ b/arch/s390/Kconfig.debug
@@ -17,10 +17,6 @@ config S390_PTDUMP
kernel.
If in doubt, say "N"
-config DEBUG_SET_MODULE_RONX
- def_bool y
- depends on MODULES
-
config EARLY_PRINTK
def_bool y
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 40a3b3793009..43f6efae73cf 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -55,6 +55,8 @@ config X86
select ARCH_HAS_MMIO_FLUSH
select ARCH_HAS_PMEM_API if X86_64
select ARCH_HAS_SG_CHAIN
+ select ARCH_HAS_STRICT_KERNEL_RWX
+ select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
@@ -128,6 +130,7 @@ config X86
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_FUNCTION_TRACER
select HAVE_GCC_PLUGINS
+ select HAVE_GCC_PLUGIN_INITIFY_INIT_EXIT if GCC_PLUGINS
select HAVE_HW_BREAKPOINT
select HAVE_IDE
select HAVE_IOREMAP_PROT
@@ -310,9 +313,6 @@ config ARCH_SUPPORTS_UPROBES
config FIX_EARLYCON_MEM
def_bool y
-config DEBUG_RODATA
- def_bool y
-
config PGTABLE_LEVELS
int
default 4 if X86_64
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 67eec55093a5..69cdd0b2176b 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -109,17 +109,6 @@ config DEBUG_WX
If in doubt, say "Y".
-config DEBUG_SET_MODULE_RONX
- bool "Set loadable kernel module data as NX and text as RO"
- depends on MODULES
- ---help---
- This option helps catch unintended modifications to loadable
- kernel module's text and read-only data. It also prevents execution
- of module data. Such protection may interfere with run-time code
- patching and dynamic kernel tracing - and they might also protect
- against certain classes of kernel exploits.
- If in doubt, say "N".
-
config DEBUG_NX_TEST
tristate "Testcase for the NX non-executable stack feature"
depends on DEBUG_KERNEL && m
diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h
index 3d3e8353ee5c..db6fe4c3d1e8 100644
--- a/arch/x86/include/asm/string_32.h
+++ b/arch/x86/include/asm/string_32.h
@@ -6,28 +6,29 @@
/* Let gcc decide whether to inline or use the out of line functions */
#define __HAVE_ARCH_STRCPY
-extern char *strcpy(char *dest, const char *src);
+extern char *strcpy(char *dest, const char *src) __nocapture(2);
#define __HAVE_ARCH_STRNCPY
-extern char *strncpy(char *dest, const char *src, size_t count);
+extern char *strncpy(char *dest, const char *src, size_t count) __nocapture(2);
#define __HAVE_ARCH_STRCAT
-extern char *strcat(char *dest, const char *src);
+extern char *strcat(char *dest, const char *src) __nocapture(2);
#define __HAVE_ARCH_STRNCAT
-extern char *strncat(char *dest, const char *src, size_t count);
+extern char *strncat(char *dest, const char *src, size_t count) __nocapture(2);
#define __HAVE_ARCH_STRCMP
-extern int strcmp(const char *cs, const char *ct);
+extern int strcmp(const char *cs, const char *ct) __nocapture();
#define __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *cs, const char *ct, size_t count);
+extern int
+strncmp(const char *cs, const char *ct, size_t count) __nocapture(1, 2);
#define __HAVE_ARCH_STRCHR
-extern char *strchr(const char *s, int c);
+extern char *strchr(const char *s, int c) __nocapture(-1);
#define __HAVE_ARCH_STRLEN
-extern size_t strlen(const char *s);
+extern size_t strlen(const char *s) __nocapture(1);
static __always_inline void *__memcpy(void *to, const void *from, size_t n)
{
@@ -197,12 +198,12 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
#endif
#define __HAVE_ARCH_MEMMOVE
-void *memmove(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n) __nocapture(2);
#define memcmp __builtin_memcmp
#define __HAVE_ARCH_MEMCHR
-extern void *memchr(const void *cs, int c, size_t count);
+extern void *memchr(const void *cs, int c, size_t count) __nocapture(-1);
static inline void *__memset_generic(void *s, char c, size_t count)
{
@@ -243,11 +244,11 @@ void *__constant_c_memset(void *s, unsigned long c, size_t count)
/* Added by Gertjan van Wingerde to make minix and sysv module work */
#define __HAVE_ARCH_STRNLEN
-extern size_t strnlen(const char *s, size_t count);
+extern size_t strnlen(const char *s, size_t count) __nocapture(1);
/* end of additional stuff */
#define __HAVE_ARCH_STRSTR
-extern char *strstr(const char *cs, const char *ct);
+extern char *strstr(const char *cs, const char *ct) __nocapture(-1, 2);
/*
* This looks horribly ugly, but the compiler can optimize it totally,
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index a164862d77e3..e06b6eb806ac 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -28,8 +28,8 @@ static __always_inline void *__inline_memcpy(void *to, const void *from, size_t
function. */
#define __HAVE_ARCH_MEMCPY 1
-extern void *memcpy(void *to, const void *from, size_t len);
-extern void *__memcpy(void *to, const void *from, size_t len);
+extern void *memcpy(void *to, const void *from, size_t len) __nocapture(2);
+extern void *__memcpy(void *to, const void *from, size_t len) __nocapture(2);
#ifndef CONFIG_KMEMCHECK
#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
@@ -57,14 +57,14 @@ void *memset(void *s, int c, size_t n);
void *__memset(void *s, int c, size_t n);
#define __HAVE_ARCH_MEMMOVE
-void *memmove(void *dest, const void *src, size_t count);
-void *__memmove(void *dest, const void *src, size_t count);
+void *memmove(void *dest, const void *src, size_t count) __nocapture(2);
+void *__memmove(void *dest, const void *src, size_t count) __nocapture(2);
-int memcmp(const void *cs, const void *ct, size_t count);
-size_t strlen(const char *s);
-char *strcpy(char *dest, const char *src);
-char *strcat(char *dest, const char *src);
-int strcmp(const char *cs, const char *ct);
+int memcmp(const void *cs, const void *ct, size_t count) __nocapture(1, 2);
+size_t strlen(const char *s) __nocapture(1);
+char *strcpy(char *dest, const char *src) __nocapture(2);
+char *strcat(char *dest, const char *src) __nocapture(2);
+int strcmp(const char *cs, const char *ct) __nocapture(1, 2);
#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
@@ -79,7 +79,8 @@ int strcmp(const char *cs, const char *ct);
#define memset(s, c, n) __memset(s, c, n)
#endif
-__must_check int memcpy_mcsafe_unrolled(void *dst, const void *src, size_t cnt);
+__must_check __nocapture(2) int
+memcpy_mcsafe_unrolled(void *dst, const void *src, size_t cnt);
DECLARE_STATIC_KEY_FALSE(mcsafe_key);
/**
@@ -96,7 +97,7 @@ DECLARE_STATIC_KEY_FALSE(mcsafe_key);
*
* Return 0 for success, -EFAULT for fail
*/
-static __always_inline __must_check int
+static __always_inline __must_check __nocapture(2) int
memcpy_mcsafe(void *dst, const void *src, size_t cnt)
{
#ifdef CONFIG_X86_MCE
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index dc6ba5bda9fc..5dba48443398 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/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 34a74131a12c..b98b8fdb7aaf 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -29,6 +29,10 @@ lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
ifeq ($(CONFIG_X86_32),y)
+ CFLAGS_strstr_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+ CFLAGS_string_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+ CFLAGS_memcpy_32.o += $(INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS)
+
obj-y += atomic64_32.o
lib-y += atomic64_cx8_32.o
lib-y += checksum_32.o
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 6f28cfae2212..945d344d4423 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -291,7 +291,7 @@ void acpi_ut_init_stack_ptr_trace(void);
void acpi_ut_track_stack_ptr(void);
-void
+__nocapture(2) void
acpi_ut_trace(u32 line_number,
const char *function_name,
const char *module_name, u32 component_id);
diff --git a/fs/char_dev.c b/fs/char_dev.c
index 44a240c4bb65..3bc97002c86f 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -70,7 +70,7 @@ void chrdev_show(struct seq_file *f, off_t offset)
*
* Returns a -ve errno on failure.
*/
-static struct char_device_struct *
+static __nocapture(4) struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
diff --git a/fs/ntfs/debug.h b/fs/ntfs/debug.h
index 61bf091e32a8..6ac561943118 100644
--- a/fs/ntfs/debug.h
+++ b/fs/ntfs/debug.h
@@ -30,7 +30,7 @@
extern int debug_msgs;
-extern __printf(4, 5)
+extern __printf(4, 5) __nocapture(3)
void __ntfs_debug(const char *file, int line, const char *function,
const char *format, ...);
/**
@@ -58,12 +58,12 @@ do { \
#endif /* !DEBUG */
-extern __printf(3, 4)
+extern __printf(3, 4) __nocapture(1)
void __ntfs_warning(const char *function, const struct super_block *sb,
const char *fmt, ...);
#define ntfs_warning(sb, f, a...) __ntfs_warning(__func__, sb, f, ##a)
-extern __printf(3, 4)
+extern __printf(3, 4) __nocapture(1)
void __ntfs_error(const char *function, const struct super_block *sb,
const char *fmt, ...);
#define ntfs_error(sb, f, a...) __ntfs_error(__func__, sb, f, ##a)
diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
index 308ea0eb35fd..3c16da69605d 100644
--- a/fs/ocfs2/cluster/masklog.h
+++ b/fs/ocfs2/cluster/masklog.h
@@ -162,7 +162,7 @@ extern struct mlog_bits mlog_and_bits, mlog_not_bits;
#endif
-__printf(4, 5)
+__printf(4, 5) __nocapture(2)
void __mlog_printk(const u64 *m, const char *func, int line,
const char *fmt, ...);
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3795386ea706..71c881345a9f 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -938,7 +938,7 @@ ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
/*
* Debug output
*/
-ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6)
+ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) __nocapture(3)
void ACPI_INTERNAL_VAR_XFACE
acpi_debug_print(u32 requested_debug_level,
u32 line_number,
diff --git a/include/asm-generic/asm-prototypes.h b/include/asm-generic/asm-prototypes.h
index 939869c772b1..ffc0dd7e8ed2 100644
--- a/include/asm-generic/asm-prototypes.h
+++ b/include/asm-generic/asm-prototypes.h
@@ -2,12 +2,12 @@
#undef __memset
extern void *__memset(void *, int, __kernel_size_t);
#undef __memcpy
-extern void *__memcpy(void *, const void *, __kernel_size_t);
+extern void *__memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
#undef __memmove
-extern void *__memmove(void *, const void *, __kernel_size_t);
+extern void *__memmove(void *, const void *, __kernel_size_t) __nocapture(2);
#undef memset
extern void *memset(void *, int, __kernel_size_t);
#undef memcpy
-extern void *memcpy(void *, const void *, __kernel_size_t);
+extern void *memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
#undef memmove
-extern void *memmove(void *, const void *, __kernel_size_t);
+extern void *memmove(void *, const void *, __kernel_size_t) __nocapture(2);
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 6f96247226a4..f6ae0d76f2e7 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -62,13 +62,13 @@ struct bug_entry {
* to provide better diagnostics.
*/
#ifndef __WARN_TAINT
-extern __printf(3, 4)
+extern __printf(3, 4) __nocapture(1)
void warn_slowpath_fmt(const char *file, const int line,
const char *fmt, ...);
-extern __printf(4, 5)
+extern __printf(4, 5) __nocapture(1)
void warn_slowpath_fmt_taint(const char *file, const int line, unsigned taint,
const char *fmt, ...);
-extern void warn_slowpath_null(const char *file, const int line);
+extern __nocapture(1) void warn_slowpath_null(const char *file, const int line);
#define WANT_WARN_ON_SLOWPATH
#define __WARN() warn_slowpath_null(__FILE__, __LINE__)
#define __WARN_printf(arg...) warn_slowpath_fmt(__FILE__, __LINE__, arg)
@@ -84,6 +84,7 @@ extern void warn_slowpath_null(const char *file, const int line);
/* used internally by panic.c */
struct warn_args;
+__nocapture(1, 0)
void __warn(const char *file, int line, void *caller, unsigned taint,
struct pt_regs *regs, struct warn_args *args);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 0968d13b3885..4e09b28b0a7b 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -551,6 +551,7 @@
MEM_DISCARD(init.data) \
KERNEL_CTORS() \
MCOUNT_REC() \
+ *(.init.rodata.str) \
*(.init.rodata) \
FTRACE_EVENTS() \
TRACE_SYSCALLS() \
@@ -579,6 +580,7 @@
*(.fini_array) \
*(.dtors) \
MEM_DISCARD(exit.data) \
+ *(.exit.rodata.str) \
MEM_DISCARD(exit.rodata)
#define EXIT_TEXT \
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 5699f42195fe..f2c0040ddc3a 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -436,7 +436,7 @@ struct drm_driver {
int dev_priv_size;
};
-extern __printf(6, 7)
+extern __printf(6, 7) __nocapture(4)
void drm_dev_printk(const struct device *dev, const char *level,
unsigned int category, const char *function_name,
const char *prefix, const char *format, ...);
diff --git a/include/linux/audit.h b/include/linux/audit.h
index f51fca8d0b6f..4b644ca7311b 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -135,7 +135,7 @@ extern void audit_log_n_hex(struct audit_buffer *ab,
size_t len);
extern void audit_log_n_string(struct audit_buffer *ab,
const char *buf,
- size_t n);
+ size_t n) __nocapture(2);
extern void audit_log_n_untrustedstring(struct audit_buffer *ab,
const char *string,
size_t n);
@@ -552,7 +552,8 @@ static inline bool audit_loginuid_set(struct task_struct *tsk)
return uid_valid(audit_get_loginuid(tsk));
}
-static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
+static inline __nocapture(2)
+void audit_log_string(struct audit_buffer *ab, const char *buf)
{
audit_log_n_string(ab, buf, strlen(buf));
}
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index fddd1a5eb322..cad999386d61 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -197,6 +197,28 @@
#endif
/*
+ * The initify gcc-plugin attempts to identify const arguments that are only
+ * used during init (see __init and __exit), so they can be moved to the
+ * .init.rodata/.exit.rodata section. If an argument is passed to a non-init
+ * function, it must normally be assumed that such an argument has been
+ * captured by that function and may be used in the future when .init/.exit has
+ * been unmapped from memory. In order to identify functions that are confirmed
+ * to not capture their arguments, the __nocapture() attribute is used so that
+ * initify can better identify candidate variables.
+ *
+ * Arguments marked in this way are verified by the plugin, but sometimes
+ * code complexity and other limitiations will cause initify to not be able
+ * to check it correctly. For these cases, the __unverified_nocapture
+ * attribute can be added to disable this checking, overriding the plugin
+ * logic for cases that have been manually verified. This should not need
+ * to be used very often.
+ */
+#ifdef INITIFY_PLUGIN
+#define __nocapture(...) __attribute__((nocapture(__VA_ARGS__)))
+#define __unverified_nocapture(...) __attribute__((unverified_nocapture(__VA_ARGS__)))
+#endif
+
+/*
* Mark a position in code as unreachable. This can be used to
* suppress control flow warnings after asm blocks that transfer
* control elsewhere.
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index cf0fa5d86059..1a1be710db52 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -27,7 +27,11 @@ extern void __chk_user_ptr(const volatile void __user *);
extern void __chk_io_ptr(const volatile void __iomem *);
# define ACCESS_PRIVATE(p, member) (*((typeof((p)->member) __force *) &(p)->member))
#else /* __CHECKER__ */
-# define __user
+# ifdef STRUCTLEAK_PLUGIN
+# define __user __attribute__((user))
+# else
+# define __user
+# endif
# define __kernel
# define __safe
# define __force
@@ -433,6 +437,14 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
# define __latent_entropy
#endif
+#ifndef __nocapture
+# define __nocapture(...)
+#endif
+
+#ifndef __unverified_nocapture
+# define __unverified_nocapture(...)
+#endif
+
/*
* Tell gcc if a function is cold. The compiler will assume any path
* directly leading to the call is unlikely.
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e4eb2546339a..c2d282764d5d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -545,7 +545,7 @@ static inline bool bpf_prog_was_classic(const struct bpf_prog *prog)
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
-#ifdef CONFIG_DEBUG_SET_MODULE_RONX
+#ifdef CONFIG_STRICT_MODULE_RWX
static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
{
set_memory_ro((unsigned long)fp, fp->pages);
@@ -563,7 +563,7 @@ static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
{
}
-#endif /* CONFIG_DEBUG_SET_MODULE_RONX */
+#endif /* CONFIG_STRICT_MODULE_RWX */
int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 06753478d8eb..5c4d66e77755 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2415,13 +2415,15 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
#define CHRDEV_MAJOR_HASH_SIZE 255
/* Marks the bottom of the first segment of free char majors */
#define CHRDEV_MAJOR_DYN_END 234
-extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
+extern int __nocapture(4)
+alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
extern int register_chrdev_region(dev_t, unsigned, const char *);
extern int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops);
-extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
- unsigned int count, const char *name);
+extern __nocapture(4) void __unregister_chrdev(unsigned int major,
+ unsigned int baseminor, unsigned int count,
+ const char *name);
extern void unregister_chrdev_region(dev_t, unsigned);
extern void chrdev_show(struct seq_file *,off_t);
diff --git a/include/linux/init.h b/include/linux/init.h
index 885c3e6d0f9d..79af0962fd52 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -126,10 +126,10 @@ void prepare_namespace(void);
void __init load_default_modules(void);
int __init init_rootfs(void);
-#if defined(CONFIG_DEBUG_RODATA) || defined(CONFIG_DEBUG_SET_MODULE_RONX)
+#if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_STRICT_MODULE_RWX)
extern bool rodata_enabled;
#endif
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
void mark_rodata_ro(void);
#endif
diff --git a/include/linux/module.h b/include/linux/module.h
index cc7cba219b20..e270da26f611 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -764,7 +764,7 @@ extern int module_sysfs_initialized;
#define __MODULE_STRING(x) __stringify(x)
-#ifdef CONFIG_DEBUG_SET_MODULE_RONX
+#ifdef CONFIG_STRICT_MODULE_RWX
extern void set_all_modules_text_rw(void);
extern void set_all_modules_text_ro(void);
extern void module_enable_ro(const struct module *mod, bool after_init);
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 571257e0f53d..3c635b610bdc 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -181,7 +181,7 @@ __printf(1, 2) __cold int printk_deferred(const char *fmt, ...);
* with all other unrelated printk_ratelimit() callsites. Instead use
* printk_ratelimited() or plain old __ratelimit().
*/
-extern int __printk_ratelimit(const char *func);
+extern int __printk_ratelimit(const char *func) __nocapture(1);
#define printk_ratelimit() __printk_ratelimit(__func__)
extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
unsigned int interval_msec);
diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h
index 56375edf2ed2..67864ad91978 100644
--- a/include/linux/ratelimit.h
+++ b/include/linux/ratelimit.h
@@ -72,7 +72,8 @@ ratelimit_set_flags(struct ratelimit_state *rs, unsigned long flags)
extern struct ratelimit_state printk_ratelimit_state;
-extern int ___ratelimit(struct ratelimit_state *rs, const char *func);
+extern __nocapture(2)
+int ___ratelimit(struct ratelimit_state *rs, const char *func);
#define __ratelimit(state) ___ratelimit(state, __func__)
#ifdef CONFIG_PRINTK
diff --git a/include/linux/string.h b/include/linux/string.h
index 26b6f6a66f83..040eab355533 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -18,51 +18,52 @@ extern void *memdup_user_nul(const void __user *, size_t);
#include <asm/string.h>
#ifndef __HAVE_ARCH_STRCPY
-extern char * strcpy(char *,const char *);
+extern char * strcpy(char *,const char *) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRNCPY
-extern char * strncpy(char *,const char *, __kernel_size_t);
+extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
+size_t strlcpy(char *, const char *, size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRSCPY
ssize_t __must_check strscpy(char *, const char *, size_t);
#endif
#ifndef __HAVE_ARCH_STRCAT
-extern char * strcat(char *, const char *);
+extern char * strcat(char *, const char *) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRNCAT
-extern char * strncat(char *, const char *, __kernel_size_t);
+extern char * strncat(char *, const char *, __kernel_size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRLCAT
-extern size_t strlcat(char *, const char *, __kernel_size_t);
+extern size_t strlcat(char *, const char *, __kernel_size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRCMP
-extern int strcmp(const char *,const char *);
+extern int strcmp(const char *,const char *) __nocapture();
#endif
#ifndef __HAVE_ARCH_STRNCMP
-extern int strncmp(const char *,const char *,__kernel_size_t);
+extern int strncmp(const char *,const char *,__kernel_size_t) __nocapture(1, 2);
#endif
#ifndef __HAVE_ARCH_STRCASECMP
-extern int strcasecmp(const char *s1, const char *s2);
+extern int strcasecmp(const char *s1, const char *s2) __nocapture();
#endif
#ifndef __HAVE_ARCH_STRNCASECMP
-extern int strncasecmp(const char *s1, const char *s2, size_t n);
+extern int
+strncasecmp(const char *s1, const char *s2, size_t n) __nocapture(1, 2);
#endif
#ifndef __HAVE_ARCH_STRCHR
-extern char * strchr(const char *,int);
+extern char * strchr(const char *,int) __nocapture(-1);
#endif
#ifndef __HAVE_ARCH_STRCHRNUL
-extern char * strchrnul(const char *,int);
+extern char * strchrnul(const char *,int) __nocapture(-1);
#endif
#ifndef __HAVE_ARCH_STRNCHR
-extern char * strnchr(const char *, size_t, int);
+extern char * strnchr(const char *, size_t, int) __nocapture(-1);
#endif
#ifndef __HAVE_ARCH_STRRCHR
-extern char * strrchr(const char *,int);
+extern char * strrchr(const char *,int) __nocapture(-1);
#endif
-extern char * __must_check skip_spaces(const char *);
+extern char * __must_check skip_spaces(const char *) __nocapture(-1);
extern char *strim(char *);
@@ -72,63 +73,63 @@ static inline __must_check char *strstrip(char *str)
}
#ifndef __HAVE_ARCH_STRSTR
-extern char * strstr(const char *, const char *);
+extern char * strstr(const char *, const char *) __nocapture(-1, 2);
#endif
#ifndef __HAVE_ARCH_STRNSTR
-extern char * strnstr(const char *, const char *, size_t);
+extern char * strnstr(const char *, const char *, size_t) __nocapture(-1, 2) __unverified_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(-1, 2);
#endif
#ifndef __HAVE_ARCH_STRSEP
-extern char * strsep(char **,const char *);
+extern char * strsep(char **,const char *) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_STRSPN
-extern __kernel_size_t strspn(const char *,const char *);
+extern __kernel_size_t strspn(const char *,const char *) __nocapture();
#endif
#ifndef __HAVE_ARCH_STRCSPN
-extern __kernel_size_t strcspn(const char *,const char *);
+extern __kernel_size_t strcspn(const char *,const char *) __nocapture();
#endif
#ifndef __HAVE_ARCH_MEMSET
extern void * memset(void *,int,__kernel_size_t);
#endif
#ifndef __HAVE_ARCH_MEMCPY
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_MEMMOVE
-extern void * memmove(void *,const void *,__kernel_size_t);
+extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
#endif
#ifndef __HAVE_ARCH_MEMSCAN
extern void * memscan(void *,int,__kernel_size_t);
#endif
#ifndef __HAVE_ARCH_MEMCMP
-extern int memcmp(const void *,const void *,__kernel_size_t);
+extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
#endif
#ifndef __HAVE_ARCH_MEMCHR
-extern void * memchr(const void *,int,__kernel_size_t);
+extern void * memchr(const void *,int,__kernel_size_t) __nocapture(-1);
#endif
-void *memchr_inv(const void *s, int c, size_t n);
+void *memchr_inv(const void *s, int c, size_t n) __nocapture(-1);
char *strreplace(char *s, char old, char new);
extern void kfree_const(const void *x);
-extern char *kstrdup(const char *s, gfp_t gfp) __malloc;
-extern const char *kstrdup_const(const char *s, gfp_t gfp);
-extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
-extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
+extern char *kstrdup(const char *s, gfp_t gfp) __malloc __nocapture(1);
+extern const char *kstrdup_const(const char *s, gfp_t gfp) __nocapture(1);
+extern char *kstrndup(const char *s, size_t len, gfp_t gfp) __nocapture(1);
+extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __nocapture(1);
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
-extern bool sysfs_streq(const char *s1, const char *s2);
-extern int kstrtobool(const char *s, bool *res);
+extern bool sysfs_streq(const char *s1, const char *s2) __nocapture();
+extern int kstrtobool(const char *s, bool *res) __nocapture(1);
static inline int strtobool(const char *s, bool *res)
{
return kstrtobool(s, res);
@@ -137,8 +138,10 @@ static inline int strtobool(const char *s, bool *res)
int match_string(const char * const *array, size_t n, const char *string);
#ifdef CONFIG_BINARY_PRINTF
-int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
-int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf);
+int vbin_printf(u32 *bin_buf, size_t size, const char *fmt,
+ va_list args) __nocapture(3);
+int bstr_printf(char *buf, size_t size, const char *fmt,
+ const u32 *bin_buf) __nocapture(3);
int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4);
#endif
diff --git a/init/main.c b/init/main.c
index b4ca17d9bdeb..86f5cf0a92a2 100644
--- a/init/main.c
+++ b/init/main.c
@@ -925,7 +925,7 @@ static int try_to_run_init_process(const char *init_filename)
static noinline void __init kernel_init_freeable(void);
-#if defined(CONFIG_DEBUG_RODATA) || defined(CONFIG_DEBUG_SET_MODULE_RONX)
+#if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_STRICT_MODULE_RWX)
bool rodata_enabled __ro_after_init = true;
static int __init set_debug_rodata(char *str)
{
@@ -934,7 +934,7 @@ static int __init set_debug_rodata(char *str)
__setup("rodata=", set_debug_rodata);
#endif
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
static void mark_readonly(void)
{
if (rodata_enabled)
diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config
index 297756be369c..99127edc5204 100644
--- a/kernel/configs/android-recommended.config
+++ b/kernel/configs/android-recommended.config
@@ -11,7 +11,7 @@ CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_COMPACTION=y
-CONFIG_DEBUG_RODATA=y
+CONFIG_STRICT_KERNEL_RWX=y
CONFIG_DM_CRYPT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
diff --git a/kernel/module.c b/kernel/module.c
index 3d8f126208e3..58856a46db80 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -74,9 +74,9 @@
/*
* Modules' sections will be aligned on page boundaries
* to ensure complete separation of code and data, but
- * only when CONFIG_DEBUG_SET_MODULE_RONX=y
+ * only when CONFIG_STRICT_MODULE_RWX=y
*/
-#ifdef CONFIG_DEBUG_SET_MODULE_RONX
+#ifdef CONFIG_STRICT_MODULE_RWX
# define debug_align(X) ALIGN(X, PAGE_SIZE)
#else
# define debug_align(X) (X)
@@ -1844,7 +1844,7 @@ static void mod_sysfs_teardown(struct module *mod)
mod_sysfs_fini(mod);
}
-#ifdef CONFIG_DEBUG_SET_MODULE_RONX
+#ifdef CONFIG_STRICT_MODULE_RWX
/*
* LKM RO/NX protection: protect module's text/ro-data
* from modification and any data from execution.
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index b26dbc48c75b..86385af1080f 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -1156,7 +1156,7 @@ static int __init hibernate_setup(char *str)
} else if (!strncmp(str, "no", 2)) {
noresume = 1;
nohibernate = 1;
- } else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
+ } else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)
&& !strncmp(str, "protect_image", 13)) {
enable_restore_image_protection();
}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 1dfa0da827d3..7fdc40d31b7d 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -61,12 +61,12 @@ extern int hibernation_snapshot(int platform_mode);
extern int hibernation_restore(int platform_mode);
extern int hibernation_platform_enter(void);
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
/* kernel/power/snapshot.c */
extern void enable_restore_image_protection(void);
#else
static inline void enable_restore_image_protection(void) {}
-#endif /* CONFIG_DEBUG_RODATA */
+#endif /* CONFIG_STRICT_KERNEL_RWX */
#else /* !CONFIG_HIBERNATION */
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 2d8e2b227db8..905d5bbd595f 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -38,7 +38,7 @@
#include "power.h"
-#ifdef CONFIG_DEBUG_RODATA
+#ifdef CONFIG_STRICT_KERNEL_RWX
static bool hibernate_restore_protection;
static bool hibernate_restore_protection_active;
@@ -73,7 +73,7 @@ static inline void hibernate_restore_protection_begin(void) {}
static inline void hibernate_restore_protection_end(void) {}
static inline void hibernate_restore_protect_page(void *page_address) {}
static inline void hibernate_restore_unprotect_page(void *page_address) {}
-#endif /* CONFIG_DEBUG_RODATA */
+#endif /* CONFIG_STRICT_KERNEL_RWX */
static int swsusp_page_is_free(struct page *);
static void swsusp_set_page_forbidden(struct page *);
diff --git a/lib/string.c b/lib/string.c
index ed83562a53ae..b3c22a285a78 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -870,7 +870,8 @@ void *memchr(const void *s, int c, size_t n)
EXPORT_SYMBOL(memchr);
#endif
-static void *check_bytes8(const u8 *start, u8 value, unsigned int bytes)
+static __nocapture(1)
+void *check_bytes8(const u8 *start, u8 value, unsigned int bytes)
{
while (bytes) {
if (*start != value)
diff --git a/lib/test_user_copy.c b/lib/test_user_copy.c
index 0ecef3e4690e..4a79f2c1cd6e 100644
--- a/lib/test_user_copy.c
+++ b/lib/test_user_copy.c
@@ -25,6 +25,22 @@
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
+/*
+ * Several 32-bit architectures support 64-bit {get,put}_user() calls.
+ * As there doesn't appear to be anything that can safely determine
+ * their capability at compile-time, we just have to opt-out certain archs.
+ */
+#if BITS_PER_LONG == 64 || (!defined(CONFIG_AVR32) && \
+ !defined(CONFIG_BLACKFIN) && \
+ !defined(CONFIG_M32R) && \
+ !defined(CONFIG_M68K) && \
+ !defined(CONFIG_MICROBLAZE) && \
+ !defined(CONFIG_MN10300) && \
+ !defined(CONFIG_NIOS2) && \
+ !defined(CONFIG_SUPERH))
+# define TEST_U64
+#endif
+
#define test(condition, msg) \
({ \
int cond = (condition); \
@@ -40,7 +56,12 @@ static int __init test_user_copy_init(void)
char __user *usermem;
char *bad_usermem;
unsigned long user_addr;
- unsigned long value = 0x5A;
+ u8 val_u8;
+ u16 val_u16;
+ u32 val_u32;
+#ifdef TEST_U64
+ u64 val_u64;
+#endif
kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!kmem)
@@ -58,33 +79,100 @@ static int __init test_user_copy_init(void)
usermem = (char __user *)user_addr;
bad_usermem = (char *)user_addr;
- /* Legitimate usage: none of these should fail. */
- ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
- "legitimate copy_from_user failed");
+ /*
+ * Legitimate usage: none of these copies should fail.
+ */
+ memset(kmem, 0x3a, PAGE_SIZE * 2);
ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
"legitimate copy_to_user failed");
- ret |= test(get_user(value, (unsigned long __user *)usermem),
- "legitimate get_user failed");
- ret |= test(put_user(value, (unsigned long __user *)usermem),
- "legitimate put_user failed");
-
- /* Invalid usage: none of these should succeed. */
+ memset(kmem, 0x0, PAGE_SIZE);
+ ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
+ "legitimate copy_from_user failed");
+ ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE),
+ "legitimate usercopy failed to copy data");
+
+#define test_legit(size, check) \
+ do { \
+ val_##size = check; \
+ ret |= test(put_user(val_##size, (size __user *)usermem), \
+ "legitimate put_user (" #size ") failed"); \
+ val_##size = 0; \
+ ret |= test(get_user(val_##size, (size __user *)usermem), \
+ "legitimate get_user (" #size ") failed"); \
+ ret |= test(val_##size != check, \
+ "legitimate get_user (" #size ") failed to do copy"); \
+ if (val_##size != check) { \
+ pr_info("0x%llx != 0x%llx\n", \
+ (unsigned long long)val_##size, \
+ (unsigned long long)check); \
+ } \
+ } while (0)
+
+ test_legit(u8, 0x5a);
+ test_legit(u16, 0x5a5b);
+ test_legit(u32, 0x5a5b5c5d);
+#ifdef TEST_U64
+ test_legit(u64, 0x5a5b5c5d6a6b6c6d);
+#endif
+#undef test_legit
+
+ /*
+ * Invalid usage: none of these copies should succeed.
+ */
+
+ /* Prepare kernel memory with check values. */
+ memset(kmem, 0x5a, PAGE_SIZE);
+ memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
+
+ /* Reject kernel-to-kernel copies through copy_from_user(). */
ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
PAGE_SIZE),
"illegal all-kernel copy_from_user passed");
+
+ /* Destination half of buffer should have been zeroed. */
+ ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE),
+ "zeroing failure for illegal all-kernel copy_from_user");
+
+#if 0
+ /*
+ * When running with SMAP/PAN/etc, this will Oops the kernel
+ * due to the zeroing of userspace memory on failure. This needs
+ * to be tested in LKDTM instead, since this test module does not
+ * expect to explode.
+ */
ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
PAGE_SIZE),
"illegal reversed copy_from_user passed");
+#endif
ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
PAGE_SIZE),
"illegal all-kernel copy_to_user passed");
ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
PAGE_SIZE),
"illegal reversed copy_to_user passed");
- ret |= test(!get_user(value, (unsigned long __user *)kmem),
- "illegal get_user passed");
- ret |= test(!put_user(value, (unsigned long __user *)kmem),
- "illegal put_user passed");
+
+#define test_illegal(size, check) \
+ do { \
+ val_##size = (check); \
+ ret |= test(!get_user(val_##size, (size __user *)kmem), \
+ "illegal get_user (" #size ") passed"); \
+ ret |= test(val_##size != (size)0, \
+ "zeroing failure for illegal get_user (" #size ")"); \
+ if (val_##size != (size)0) { \
+ pr_info("0x%llx != 0\n", \
+ (unsigned long long)val_##size); \
+ } \
+ ret |= test(!put_user(val_##size, (size __user *)kmem), \
+ "illegal put_user (" #size ") passed"); \
+ } while (0)
+
+ test_illegal(u8, 0x5a);
+ test_illegal(u16, 0x5a5b);
+ test_illegal(u32, 0x5a5b5c5d);
+#ifdef TEST_U64
+ test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
+#endif
+#undef test_illegal
vm_munmap(user_addr, PAGE_SIZE * 2);
kfree(kmem);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 0967771d8f7f..cb964b51f9f8 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -118,7 +118,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
}
EXPORT_SYMBOL(simple_strtoll);
-static noinline_for_stack
+static noinline_for_stack __nocapture(1) __unverified_nocapture(1)
int skip_atoi(const char **s)
{
int i = 0;
@@ -1570,7 +1570,7 @@ int kptr_restrict __read_mostly;
* function pointers are really function descriptors, which contain a
* pointer to the real address.
*/
-static noinline_for_stack
+static noinline_for_stack __nocapture(1) __unverified_nocapture(1)
char *pointer(const char *fmt, char *buf, char *end, void *ptr,
struct printf_spec spec)
{
@@ -1749,7 +1749,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
* @precision: precision of a number
* @qualifier: qualifier of a number (long, size_t, ...)
*/
-static noinline_for_stack
+static noinline_for_stack __nocapture(1)
int format_decode(const char *fmt, struct printf_spec *spec)
{
const char *start = fmt;
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index b2a0cff2bb35..82e27d52d67a 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -343,6 +343,7 @@ void *memset(void *addr, int c, size_t len)
}
#undef memmove
+__unverified_nocapture(2)
void *memmove(void *dest, const void *src, size_t len)
{
check_memory_region((unsigned long)src, len, false, _RET_IP_);
@@ -352,6 +353,7 @@ void *memmove(void *dest, const void *src, size_t len)
}
#undef memcpy
+__unverified_nocapture(2)
void *memcpy(void *dest, const void *src, size_t len)
{
check_memory_region((unsigned long)src, len, false, _RET_IP_);
diff --git a/mm/util.c b/mm/util.c
index 3cb2164f4099..e91250dabf49 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -17,10 +17,10 @@
#include "internal.h"
-static inline int is_kernel_rodata(unsigned long addr)
+static inline __nocapture(1) int is_kernel_rodata(const void *addr)
{
- return addr >= (unsigned long)__start_rodata &&
- addr < (unsigned long)__end_rodata;
+ return (unsigned long)addr >= (unsigned long)__start_rodata &&
+ (unsigned long)addr < (unsigned long)__end_rodata;
}
/**
@@ -31,7 +31,7 @@ static inline int is_kernel_rodata(unsigned long addr)
*/
void kfree_const(const void *x)
{
- if (!is_kernel_rodata((unsigned long)x))
+ if (!is_kernel_rodata(x))
kfree(x);
}
EXPORT_SYMBOL(kfree_const);
@@ -68,7 +68,7 @@ EXPORT_SYMBOL(kstrdup);
*/
const char *kstrdup_const(const char *s, gfp_t gfp)
{
- if (is_kernel_rodata((unsigned long)s))
+ if (is_kernel_rodata(s))
return s;
return kstrdup(s, gfp);
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 9835a757d52a..9e89d744c6a3 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -6,6 +6,19 @@ ifdef CONFIG_GCC_PLUGINS
gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so
+ gcc-plugin-$(CONFIG_GCC_PLUGIN_INITIFY) += initify_plugin.so
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY) += -DINITIFY_PLUGIN -fplugin-arg-initify_plugin-search_init_exit_functions
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY_VERBOSE)+= -fplugin-arg-initify_plugin-verbose -fplugin-arg-initify_plugin-print_missing_attr
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_INITIFY_INIT_EXIT) += -fplugin-arg-initify_plugin-enable_init_to_exit_moves
+ ifdef CONFIG_GCC_PLUGIN_INITIFY
+ INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS += -fplugin-arg-initify_plugin-disable_verify_nocapture_functions
+ endif
+
+ # The Latent Entropy instrumentation pass should already come after Initify
+ # ("optimized" vs "inline"), but just in case this changes, maintain the
+ # runtime ordering here too. Order matters so that Latent Entropy will
+ # instrument functions that have been newly moved into the .init section
+ # by Initify.
gcc-plugin-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += latent_entropy_plugin.so
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += -DLATENT_ENTROPY_PLUGIN
ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
@@ -25,10 +38,15 @@ ifdef CONFIG_GCC_PLUGINS
endif
endif
+ gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) += -fplugin-arg-structleak_plugin-verbose
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += -DSTRUCTLEAK_PLUGIN
+
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
+ export INITIFY_DISABLE_VERIFIY_NOCAPTURE_FUNCTIONS
ifneq ($(PLUGINCC),)
# SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
diff --git a/scripts/gcc-plugins/cyc_complexity_plugin.c b/scripts/gcc-plugins/cyc_complexity_plugin.c
index 8af7db06122d..1909ec617431 100644
--- a/scripts/gcc-plugins/cyc_complexity_plugin.c
+++ b/scripts/gcc-plugins/cyc_complexity_plugin.c
@@ -52,12 +52,8 @@ static unsigned int cyc_complexity_execute(void)
__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
{
const char * const plugin_name = plugin_info->base_name;
- struct register_pass_info cyc_complexity_pass_info;
- cyc_complexity_pass_info.pass = make_cyc_complexity_pass();
- cyc_complexity_pass_info.reference_pass_name = "ssa";
- cyc_complexity_pass_info.ref_pass_instance_number = 1;
- cyc_complexity_pass_info.pos_op = PASS_POS_INSERT_AFTER;
+ PASS_INFO(cyc_complexity, "ssa", 1, PASS_POS_INSERT_AFTER);
if (!plugin_default_version_check(version, &gcc_version)) {
error(G_("incompatible gcc/plugin versions"));
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 12262c0cc691..b232ab15624c 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -26,6 +26,9 @@
#include "except.h"
#include "function.h"
#include "toplev.h"
+#if BUILDING_GCC_VERSION >= 5000
+#include "expr.h"
+#endif
#include "basic-block.h"
#include "intl.h"
#include "ggc.h"
@@ -80,6 +83,9 @@
#include "diagnostic.h"
#include "tree-dump.h"
#include "tree-pass.h"
+#if BUILDING_GCC_VERSION >= 4009
+#include "pass_manager.h"
+#endif
#include "predict.h"
#include "ipa-utils.h"
@@ -119,20 +125,17 @@
#include "builtins.h"
#endif
-/* #include "expr.h" where are you... */
-extern rtx emit_move_insn(rtx x, rtx y);
-
/* missing from basic_block.h... */
-extern void debug_dominance_info(enum cdi_direction dir);
-extern void debug_dominance_tree(enum cdi_direction dir, basic_block root);
+void debug_dominance_info(enum cdi_direction dir);
+void debug_dominance_tree(enum cdi_direction dir, basic_block root);
#if BUILDING_GCC_VERSION == 4006
-extern void debug_gimple_stmt(gimple);
-extern void debug_gimple_seq(gimple_seq);
-extern void print_gimple_seq(FILE *, gimple_seq, int, int);
-extern void print_gimple_stmt(FILE *, gimple, int, int);
-extern void print_gimple_expr(FILE *, gimple, int, int);
-extern void dump_gimple_stmt(pretty_printer *, gimple, int, int);
+void debug_gimple_stmt(gimple);
+void debug_gimple_seq(gimple_seq);
+void print_gimple_seq(FILE *, gimple_seq, int, int);
+void print_gimple_stmt(FILE *, gimple, int, int);
+void print_gimple_expr(FILE *, gimple, int, int);
+void dump_gimple_stmt(pretty_printer *, gimple, int, int);
#endif
#define __unused __attribute__((__unused__))
@@ -146,6 +149,29 @@ extern void dump_gimple_stmt(pretty_printer *, gimple, int, int);
/* should come from c-tree.h if only it were installed for gcc 4.5... */
#define C_TYPE_FIELDS_READONLY(TYPE) TREE_LANG_FLAG_1(TYPE)
+static inline tree build_const_char_string(int len, const char *str)
+{
+ tree cstr, elem, index, type;
+
+ cstr = build_string(len, str);
+ elem = build_type_variant(char_type_node, 1, 0);
+ index = build_index_type(size_int(len - 1));
+ type = build_array_type(elem, index);
+ TREE_TYPE(cstr) = type;
+ TREE_CONSTANT(cstr) = 1;
+ TREE_READONLY(cstr) = 1;
+ TREE_STATIC(cstr) = 1;
+ return cstr;
+}
+
+#define PASS_INFO(NAME, REF, ID, POS) \
+struct register_pass_info NAME##_pass_info = { \
+ .pass = make_##NAME##_pass(), \
+ .reference_pass_name = REF, \
+ .ref_pass_instance_number = ID, \
+ .pos_op = POS, \
+}
+
#if BUILDING_GCC_VERSION == 4005
#define FOR_EACH_LOCAL_DECL(FUN, I, D) \
for (tree vars = (FUN)->local_decls, (I) = 0; \
@@ -527,6 +553,8 @@ static inline const greturn *as_a_const_greturn(const_gimple stmt)
#define section_name_prefix LTO_SECTION_NAME_PREFIX
#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__)
+rtx emit_move_insn(rtx x, rtx y);
+
typedef struct rtx_def rtx_insn;
static inline const char *get_decl_section_name(const_tree decl)
@@ -643,6 +671,11 @@ static inline const greturn *as_a_const_greturn(const_gimple stmt)
#define NODE_DECL(node) (node)->decl
#define cgraph_node_name(node) (node)->name()
#define NODE_IMPLICIT_ALIAS(node) (node)->cpp_implicit_alias
+
+static inline opt_pass *get_pass_for_id(int id)
+{
+ return g->get_passes()->get_pass_for_id(id);
+}
#endif
#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000
diff --git a/scripts/gcc-plugins/initify_plugin.c b/scripts/gcc-plugins/initify_plugin.c
new file mode 100644
index 000000000000..56ed878e2658
--- /dev/null
+++ b/scripts/gcc-plugins/initify_plugin.c
@@ -0,0 +1,1860 @@
+/*
+ * Copyright 2015-2017 by Emese Revfy <re.emese@gmail.com>
+ * Licensed under the GPL v2
+ *
+ * Homepage:
+ * https://github.com/ephox-gcc-plugins/initify
+ *
+ * This plugin has two passes. The first one tries to find all functions that
+ * can be become __init/__exit. The second one moves string constants
+ * (local variables and function string arguments marked by
+ * the nocapture attribute) only referenced in __init/__exit functions
+ * to __initconst/__exitconst sections.
+ * Based on an idea from Mathias Krause <minipli@ld-linux.so>.
+ *
+ * The instrumentation pass of the latent_entropy plugin must run after
+ * the initify plugin to increase coverage.
+ *
+ * Options:
+ * -fplugin-arg-initify_plugin-disable
+ * -fplugin-arg-initify_plugin-verbose
+ * -fplugin-arg-initify_plugin-print_missing_attr
+ * -fplugin-arg-initify_plugin-search_init_exit_functions
+ * -fplugin-arg-initify_plugin-enable_init_to_exit_moves
+ * -fplugin-arg-initify_plugin-disable_verify_nocapture_functions
+ *
+ * Attribute: __attribute__((nocapture(x, y ...)))
+ * The nocapture gcc attribute can be on functions only.
+ * The attribute takes one or more unsigned integer constants as parameters
+ * that specify the function argument(s) of const char* type to initify.
+ * If the marked argument is a vararg then the plugin initifies
+ * all vararg arguments.
+ * There can be one negative value which means that the return of the function
+ * will be followed to find it is a nocapture attribute or not.
+ *
+ * Attribute: __attribute__((unverified_nocapture(x, y ...)))
+ * This attribute disables the compile data flow verification of the designated
+ * nocapture parameters of the function. Use it only on function parameters
+ * that are difficult for the plugin to analyze.
+ *
+ * Usage:
+ * $ make
+ * $ make run
+ */
+
+#include "gcc-common.h"
+
+__visible int plugin_is_GPL_compatible;
+
+static struct plugin_info initify_plugin_info = {
+ .version = "20170119vanilla",
+ .help = "disable\tturn off the initify plugin\n"
+ "verbose\tprint all initified strings and all"
+ " functions which should be __init/__exit\n"
+ "print_missing_attr\tprint functions which"
+ " can be marked by nocapture attribute\n"
+ "search_init_exit_functions\tfind functions"
+ " which should be marked by __init or __exit"
+ " attribute\n"
+ "enable_init_to_exit_moves\tmove a function"
+ " to the exit section if it is called by __init"
+ " and __exit functions too\n"
+ "disable_verify_nocapture_functions\tdisable"
+ " the search of capture uses in nocapture"
+ " functions\n"
+};
+
+#define ARGNUM_NONE 0
+static bool verbose, print_missing_attr, search_init_exit_functions;
+static bool enable_init_to_exit_moves, disable_verify_nocapture_functions;
+
+enum section_type {
+ INIT, EXIT, BOTH, NONE
+};
+
+enum attribute_type {
+ UNVERIFIED, NOCAPTURE, PRINTF, BUILTINS, SYSCALL, NONE_ATTRIBUTE
+};
+
+
+#if BUILDING_GCC_VERSION >= 5000
+typedef struct hash_set<const_gimple> gimple_set;
+
+static inline bool pointer_set_insert(gimple_set *visited, const_gimple stmt)
+{
+ return visited->add(stmt);
+}
+
+static inline 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;
+}
+
+typedef struct hash_set<const_tree> tree_set;
+
+static inline bool pointer_set_insert(tree_set *visited, const_tree node)
+{
+ return visited->add(node);
+}
+
+static inline tree_set* tree_pointer_set_create(void)
+{
+ return new hash_set<const_tree>;
+}
+
+static inline void pointer_set_destroy(tree_set *visited)
+{
+ delete visited;
+}
+
+typedef struct hash_set<struct cgraph_node *> cgraph_set;
+
+static inline bool pointer_set_insert(cgraph_set *visited, struct cgraph_node *node)
+{
+ return visited->add(node);
+}
+
+static inline cgraph_set* cgraph_pointer_set_create(void)
+{
+ return new hash_set<struct cgraph_node *>;
+}
+
+static inline void pointer_set_destroy(cgraph_set *visited)
+{
+ delete visited;
+}
+#else
+typedef struct pointer_set_t gimple_set;
+typedef struct pointer_set_t tree_set;
+typedef struct pointer_set_t cgraph_set;
+
+static inline tree_set *tree_pointer_set_create(void)
+{
+ return pointer_set_create();
+}
+
+static inline cgraph_set *cgraph_pointer_set_create(void)
+{
+ return pointer_set_create();
+}
+#endif
+
+static gimple initify_get_def_stmt(const_tree node)
+{
+ gcc_assert(node != NULL_TREE);
+
+ if (TREE_CODE(node) != SSA_NAME)
+ return NULL;
+ return SSA_NAME_DEF_STMT(node);
+}
+
+static void search_constant_strings(bool *has_str_cst, gimple_set *visited, tree node);
+static bool has_capture_use_local_var(const_tree vardecl);
+static bool search_capture_ssa_use(gimple_set *visited_defs, tree node);
+
+#define FUNCTION_PTR_P(node) \
+ (TREE_CODE(TREE_TYPE(node)) == POINTER_TYPE && \
+ (TREE_CODE(TREE_TYPE(TREE_TYPE(node))) == FUNCTION_TYPE || \
+ TREE_CODE(TREE_TYPE(TREE_TYPE(node))) == METHOD_TYPE))
+
+static bool is_vararg_arg(tree arg_list, unsigned int num)
+{
+ if (tree_last(arg_list) == void_list_node)
+ return false;
+
+ return num >= (unsigned int)list_length(arg_list);
+}
+
+static const_tree get_ptr_type(const_tree type)
+{
+ gcc_assert(type != NULL_TREE);
+
+ if (TREE_CODE(type) != POINTER_TYPE)
+ return type;
+ return get_ptr_type(TREE_TYPE(type));
+}
+
+static bool check_parameter(tree *node, tree type_args, int idx)
+{
+ const_tree type_arg, type, type_type, type_name, ptr_type;
+
+ if (is_vararg_arg(type_args, idx))
+ return true;
+
+ type_arg = chain_index(idx - 1, type_args);
+ type = TREE_VALUE(type_arg);
+ gcc_assert(type != NULL_TREE);
+ type_type = TREE_TYPE(type);
+ gcc_assert(type_type != NULL_TREE);
+
+ type_name = TYPE_NAME(type_type);
+ if (type_name != NULL_TREE && TREE_CODE(type_name) == IDENTIFIER_NODE && !strcmp(TYPE_NAME_POINTER(type_type), "va_format"))
+ return true;
+
+ if (TREE_CODE(type) != POINTER_TYPE) {
+ error("%u. parameter of the %qE function must be a pointer", idx, *node);
+ return false;
+ }
+
+ ptr_type = get_ptr_type(type_type);
+ if (!TYPE_READONLY(ptr_type)) {
+ error("%u. parameter of the %qE function must be readonly", idx, *node);
+ return false;
+ }
+
+ if (TREE_THIS_VOLATILE(ptr_type)) {
+ error("%u. parameter of the %qE function can't be volatile", idx, *node);
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_marked_parameters(tree *node, tree type_args, const_tree args, const_tree name)
+{
+ const_tree arg;
+ bool negative_val;
+
+ negative_val = false;
+ for (arg = args; arg; arg = TREE_CHAIN(arg)) {
+ int idx;
+ unsigned int abs_idx;
+ tree position = TREE_VALUE(arg);
+
+ if (TREE_CODE(position) != INTEGER_CST) {
+ error("%qE parameter of the %qE attribute isn't an integer (fn: %qE)", position, name, *node);
+ return false;
+ }
+
+ idx = (int)tree_to_shwi(position);
+ if (negative_val && idx < 0) {
+ error("Only one negative attribute value is supported (attribute: %qE fn: %qE)", name, *node);
+ return false;
+ }
+
+ if (idx < 0)
+ negative_val = true;
+
+ abs_idx = abs(idx);
+ if (abs_idx == 0)
+ continue;
+
+ if (!check_parameter(node, type_args, abs_idx))
+ return false;
+ }
+ return true;
+}
+
+static bool check_all_parameters(tree *node, tree type_args)
+{
+ int arg, len = list_length(type_args);
+
+ if (tree_last(type_args) == void_list_node)
+ len -= 1;
+
+ for (arg = 1; arg <= len; arg++) {
+ if (!check_parameter(node, type_args, arg))
+ return false;
+ }
+ return true;
+}
+
+/* nocapture attribute:
+ * * to mark nocapture function arguments. If used on a vararg argument
+ * it applies to all of them that have no other uses.
+ * * attribute value 0 is ignored to allow reusing print attribute arguments
+ */
+static bool handle_initify_attributes(tree *node, tree name, tree args)
+{
+ tree type_args = NULL_TREE;
+
+ switch (TREE_CODE(*node)) {
+ case FUNCTION_DECL:
+ type_args = TYPE_ARG_TYPES(TREE_TYPE(*node));
+ break;
+
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ type_args = TYPE_ARG_TYPES(*node);
+ break;
+
+ case TYPE_DECL: {
+ enum tree_code fn_code;
+ const_tree fntype = TREE_TYPE(*node);
+
+ fn_code = TREE_CODE(fntype);
+ if (fn_code == POINTER_TYPE)
+ fntype = TREE_TYPE(fntype);
+ fn_code = TREE_CODE(fntype);
+ if (fn_code == FUNCTION_TYPE || fn_code == METHOD_TYPE) {
+ type_args = TYPE_ARG_TYPES(fntype);
+ break;
+ }
+ /* FALLTHROUGH */
+ }
+
+ default:
+ debug_tree(*node);
+ error("%s: %qE attribute only applies to functions", __func__, name);
+ return false;
+ }
+
+ gcc_assert(type_args != NULL_TREE);
+
+ if (!check_marked_parameters(node, type_args, args, name))
+ return false;
+ return args != NULL_TREE || check_all_parameters(node, type_args);
+}
+
+static tree handle_nocapture_attribute(tree *node, tree name, tree args, int __unused flags, bool *no_add_attrs)
+{
+ tree nocapture_attr;
+
+ *no_add_attrs = true;
+
+ if (!handle_initify_attributes(node, name, args))
+ return NULL_TREE;
+
+ nocapture_attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(*node));
+ if (nocapture_attr)
+ chainon(TREE_VALUE(nocapture_attr), args);
+ else
+ *no_add_attrs = false;
+
+ return NULL_TREE;
+}
+
+static tree handle_unverified_nocapture_attribute(tree *node, tree name, tree args, int __unused flags, bool *no_add_attrs)
+{
+ tree unverified_attr;
+
+ *no_add_attrs = true;
+
+ if (!handle_initify_attributes(node, name, args))
+ return NULL_TREE;
+
+ unverified_attr = lookup_attribute("unverified_nocapture", DECL_ATTRIBUTES(*node));
+ if (unverified_attr)
+ chainon(TREE_VALUE(unverified_attr), args);
+ else
+ *no_add_attrs = false;
+
+ return NULL_TREE;
+}
+
+static struct attribute_spec nocapture_attr = {
+ .name = "nocapture",
+ .min_length = 0,
+ .max_length = -1,
+ .decl_required = true,
+ .type_required = false,
+ .function_type_required = false,
+ .handler = handle_nocapture_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = false
+#endif
+};
+
+static struct attribute_spec unverified_nocapture_attr = {
+ .name = "unverified_nocapture",
+ .min_length = 0,
+ .max_length = -1,
+ .decl_required = true,
+ .type_required = false,
+ .function_type_required = false,
+ .handler = handle_unverified_nocapture_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = false
+#endif
+};
+
+static void register_attributes(void __unused *event_data, void __unused *data)
+{
+ register_attribute(&nocapture_attr);
+ register_attribute(&unverified_nocapture_attr);
+}
+
+/* Determine whether the function is in the init or exit sections. */
+static enum section_type get_init_exit_section(const_tree decl)
+{
+ const char *str;
+ const_tree section, attr_value;
+
+ section = lookup_attribute("section", DECL_ATTRIBUTES(decl));
+ if (!section)
+ return NONE;
+
+ attr_value = TREE_VALUE(section);
+ gcc_assert(attr_value != NULL_TREE);
+ gcc_assert(list_length(attr_value) == 1);
+
+ str = TREE_STRING_POINTER(TREE_VALUE(attr_value));
+
+ if (!strncmp(str, ".init.", 6))
+ return INIT;
+ if (!strncmp(str, ".exit.", 6))
+ return EXIT;
+ return NONE;
+}
+
+static tree get_string_cst(tree var)
+{
+ if (var == NULL_TREE)
+ return NULL_TREE;
+
+ if (TREE_CODE(var) == STRING_CST)
+ return var;
+
+ switch (TREE_CODE_CLASS(TREE_CODE(var))) {
+ case tcc_expression:
+ case tcc_reference: {
+ int i;
+
+ for (i = 0; i < TREE_OPERAND_LENGTH(var); i++) {
+ tree ret = get_string_cst(TREE_OPERAND(var, i));
+ if (ret != NULL_TREE)
+ return ret;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return NULL_TREE;
+}
+
+static bool set_init_exit_section(tree decl)
+{
+ gcc_assert(DECL_P(decl));
+
+ if (get_init_exit_section(decl) != NONE)
+ return false;
+
+ if (get_init_exit_section(current_function_decl) == INIT)
+ set_decl_section_name(decl, ".init.rodata.str");
+ else
+ set_decl_section_name(decl, ".exit.rodata.str");
+ return true;
+}
+
+/* Syscalls are always nocapture functions. */
+static bool is_syscall(const_tree fn)
+{
+ const char *name = DECL_NAME_POINTER(fn);
+
+ if (!strncmp(name, "sys_", 4))
+ return true;
+
+ if (!strncmp(name, "sys32_", 6))
+ return true;
+
+ if (!strncmp(name, "compat_sys_", 11))
+ return true;
+
+ return false;
+}
+
+/* These builtins are nocapture functions. */
+static bool allowed_builtins(const_tree fn)
+{
+ const char *name = DECL_NAME_POINTER(fn);
+
+ if (!strcmp(name, "__builtin_va_start"))
+ return true;
+ if (!strcmp(name, "__builtin_expect"))
+ return true;
+ if (!strcmp(name, "__builtin_memcpy"))
+ return true;
+ return false;
+}
+
+static enum attribute_type search_argnum_in_attribute_params(const_tree attr, int fn_arg_num, int fntype_arg_len)
+{
+ const_tree attr_val;
+
+ for (attr_val = TREE_VALUE(attr); attr_val; attr_val = TREE_CHAIN(attr_val)) {
+ int attr_arg_val;
+
+ if (TREE_CODE(TREE_VALUE(attr_val)) == IDENTIFIER_NODE)
+ continue;
+
+ attr_arg_val = (int)abs(tree_to_shwi(TREE_VALUE(attr_val)));
+ if (attr_arg_val == fn_arg_num)
+ return NOCAPTURE;
+ if (attr_arg_val > fntype_arg_len && fn_arg_num >= attr_arg_val)
+ return NOCAPTURE;
+ }
+ return NONE_ATTRIBUTE;
+}
+
+/* Check that fn_arg_num is a nocapture argument, handle cloned functions too. */
+static enum attribute_type lookup_nocapture_argument(const_tree fndecl, const_tree attr, int fn_arg_num, int fntype_arg_len)
+{
+ const_tree orig_decl, clone_arg, orig_arg;
+ tree decl_list, orig_decl_list;
+ enum attribute_type orig_attribute;
+ struct cgraph_node *node = cgraph_get_node(fndecl);
+
+ orig_attribute = search_argnum_in_attribute_params(attr, fn_arg_num, fntype_arg_len);
+ if (orig_attribute == NONE_ATTRIBUTE)
+ return orig_attribute;
+
+ gcc_assert(node);
+ if (node->clone_of && node->clone.tree_map)
+ gcc_assert(!node->clone.args_to_skip);
+
+ if (!DECL_ARTIFICIAL(fndecl) && DECL_ABSTRACT_ORIGIN(fndecl) == NULL_TREE)
+ return orig_attribute;
+
+ orig_decl = DECL_ABSTRACT_ORIGIN(fndecl);
+ gcc_assert(orig_decl != NULL_TREE);
+
+ decl_list = DECL_ARGUMENTS(fndecl);
+ orig_decl_list = DECL_ARGUMENTS(orig_decl);
+
+ if (decl_list == NULL_TREE || orig_decl_list == NULL_TREE)
+ return NONE_ATTRIBUTE;
+
+ if (list_length(decl_list) == list_length(orig_decl_list))
+ return orig_attribute;
+
+ clone_arg = chain_index(fn_arg_num - 1, decl_list);
+ gcc_assert(clone_arg != NULL_TREE);
+
+ orig_arg = chain_index(fn_arg_num - 1, orig_decl_list);
+ gcc_assert(orig_arg != NULL_TREE);
+
+ if (!strcmp(DECL_NAME_POINTER(clone_arg), DECL_NAME_POINTER(orig_arg)))
+ return orig_attribute;
+ return NONE_ATTRIBUTE;
+}
+
+/* Check whether the function argument is nocapture. */
+static enum attribute_type is_fndecl_nocapture_arg(const_tree fndecl, int fn_arg_num)
+{
+ int fntype_arg_len;
+ const_tree type, attr = NULL_TREE;
+ bool fnptr = FUNCTION_PTR_P(fndecl);
+
+ if (!fnptr && is_syscall(fndecl))
+ return SYSCALL;
+
+ if (!fnptr && DECL_BUILT_IN(fndecl) && allowed_builtins(fndecl))
+ return BUILTINS;
+
+ if (fnptr)
+ type = TREE_TYPE(TREE_TYPE(fndecl));
+ else
+ type = TREE_TYPE(fndecl);
+
+ fntype_arg_len = type_num_arguments(type);
+
+ if (!fnptr)
+ attr = lookup_attribute("unverified_nocapture", DECL_ATTRIBUTES(fndecl));
+ if (attr != NULL_TREE && lookup_nocapture_argument(fndecl, attr, fn_arg_num, fntype_arg_len) != NONE_ATTRIBUTE)
+ return UNVERIFIED;
+
+ attr = lookup_attribute("format", TYPE_ATTRIBUTES(type));
+ if (attr != NULL_TREE && lookup_nocapture_argument(fndecl, attr, fn_arg_num, fntype_arg_len) != NONE_ATTRIBUTE)
+ return PRINTF;
+
+ if (fnptr)
+ return NONE_ATTRIBUTE;
+
+ attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+ if (attr == NULL_TREE)
+ return NONE_ATTRIBUTE;
+
+ if (TREE_VALUE(attr) == NULL_TREE)
+ return NOCAPTURE;
+
+ return lookup_nocapture_argument(fndecl, attr, fn_arg_num, fntype_arg_len);
+}
+
+/* Check whether arg_num is a nocapture argument that can be returned. */
+static bool is_negative_nocapture_arg(const_tree fndecl, int arg_num)
+{
+ const_tree attr, attr_val;
+
+ gcc_assert(arg_num <= 0);
+
+ if (FUNCTION_PTR_P(fndecl))
+ return false;
+
+ attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+ if (attr == NULL_TREE)
+ return false;
+
+ for (attr_val = TREE_VALUE(attr); attr_val; attr_val = TREE_CHAIN(attr_val)) {
+ int attr_arg_val;
+
+ if (arg_num == 0 && tree_int_cst_lt(TREE_VALUE(attr_val), integer_zero_node))
+ return true;
+
+ attr_arg_val = (int)tree_to_shwi(TREE_VALUE(attr_val));
+ if (attr_arg_val == arg_num)
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_same_vardecl(const_tree op, const_tree vardecl)
+{
+ const_tree decl;
+
+ if (op == vardecl)
+ return true;
+ if (TREE_CODE(op) == SSA_NAME)
+ decl = SSA_NAME_VAR(op);
+ else
+ decl = op;
+
+ if (decl == NULL_TREE || !DECL_P(decl))
+ return false;
+
+ if (TREE_CODE(decl) != TREE_CODE(vardecl))
+ return false;
+
+ return DECL_NAME(decl) && !strcmp(DECL_NAME_POINTER(decl), DECL_NAME_POINTER(vardecl));
+}
+
+static bool search_same_vardecl(const_tree value, const_tree vardecl)
+{
+ int i;
+
+ for (i = 0; i < TREE_OPERAND_LENGTH(value); i++) {
+ const_tree op = TREE_OPERAND(value, i);
+
+ if (op == NULL_TREE)
+ continue;
+ if (is_same_vardecl(op, vardecl))
+ return true;
+ if (search_same_vardecl(op, vardecl))
+ return true;
+ }
+ return false;
+}
+
+static bool check_constructor(const_tree constructor, const_tree vardecl)
+{
+ unsigned HOST_WIDE_INT cnt __unused;
+ tree value;
+
+ FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(constructor), cnt, value) {
+ if (TREE_CODE(value) == CONSTRUCTOR)
+ return check_constructor(value, vardecl);
+ if (is_gimple_constant(value))
+ continue;
+
+ gcc_assert(TREE_OPERAND_LENGTH(value) > 0);
+ if (search_same_vardecl(value, vardecl))
+ return true;
+ }
+ return false;
+}
+
+static bool compare_ops(const_tree vardecl, tree op)
+{
+ if (TREE_CODE(op) == TREE_LIST)
+ op = TREE_VALUE(op);
+ if (TREE_CODE(op) == SSA_NAME)
+ op = SSA_NAME_VAR(op);
+ if (op == NULL_TREE)
+ return false;
+
+ switch (TREE_CODE_CLASS(TREE_CODE(op))) {
+ case tcc_declaration:
+ return is_same_vardecl(op, vardecl);
+
+ case tcc_exceptional:
+ return check_constructor(op, vardecl);
+
+ case tcc_constant:
+ case tcc_statement:
+ case tcc_comparison:
+ return false;
+
+ default:
+ break;
+ }
+
+ gcc_assert(TREE_OPERAND_LENGTH(op) > 0);
+ return search_same_vardecl(op, vardecl);
+}
+
+static bool is_stmt_nocapture_arg(const gcall *stmt, int arg_num)
+{
+ tree fndecl;
+
+ fndecl = gimple_call_fndecl(stmt);
+ if (fndecl == NULL_TREE)
+ fndecl = gimple_call_fn(stmt);
+
+ gcc_assert(fndecl != NULL_TREE);
+ if (is_fndecl_nocapture_arg(fndecl, arg_num) != NONE_ATTRIBUTE)
+ return true;
+
+ /*
+ * These are potentially nocapture functions that must be checked
+ * manually.
+ */
+ if (print_missing_attr)
+ inform(gimple_location(stmt), "nocapture attribute is missing (fn: %E, arg: %u)\n", fndecl, arg_num);
+ return false;
+}
+
+/* Find the argument position of arg. */
+static int get_arg_num(const gcall *call, const_tree arg)
+{
+ int idx;
+
+ for (idx = 0; idx < (int)gimple_call_num_args(call); idx++) {
+ const_tree cur_arg = gimple_call_arg(call, idx);
+
+ if (cur_arg == arg)
+ return idx + 1;
+ }
+
+ debug_tree(arg);
+ debug_gimple_stmt(call);
+ gcc_unreachable();
+}
+
+/* Determine if the variable uses are only in nocapture functions. */
+static bool only_nocapture_call(const_tree decl)
+{
+ struct cgraph_edge *e;
+ struct cgraph_node *caller;
+ bool has_call = false;
+
+ gcc_assert(TREE_CODE(decl) == VAR_DECL);
+
+ caller = cgraph_get_node(current_function_decl);
+ for (e = caller->callees; e; e = e->next_callee) {
+ int idx;
+ const gcall *call = as_a_const_gcall(e->call_stmt);
+
+ for (idx = 0; idx < (int)gimple_call_num_args(call); idx++) {
+ const_tree arg = gimple_call_arg(call, idx);
+
+ if (TREE_CODE(arg) != ADDR_EXPR)
+ continue;
+ if (TREE_OPERAND(arg, 0) != decl)
+ continue;
+
+ has_call = true;
+ if (!is_stmt_nocapture_arg(call, idx + 1))
+ return false;
+ }
+ }
+
+ gcc_assert(has_call);
+ return has_call;
+}
+
+/* Determine if all uses of a va_format typed variable are nocapture. */
+static bool is_va_format_use_nocapture(const_tree node)
+{
+ const_tree decl, type;
+
+ if (TREE_CODE(node) != COMPONENT_REF)
+ return false;
+
+ decl = TREE_OPERAND(node, 0);
+ type = TREE_TYPE(decl);
+ gcc_assert(TREE_CODE(type) == RECORD_TYPE);
+
+ if (!TYPE_NAME(type) || strcmp(TYPE_NAME_POINTER(type), "va_format"))
+ return false;
+
+ return only_nocapture_call(decl);
+}
+
+/* If there is a cast to integer (from const char) then it is a nocapture data flow */
+static bool is_cast_to_integer_type(gassign *assign)
+{
+ const_tree lhs_type, lhs;
+
+ if (!gimple_assign_cast_p(assign))
+ return false;
+
+ lhs = gimple_assign_rhs1(assign);
+ lhs_type = TREE_TYPE(lhs);
+ return TYPE_MODE(lhs_type) != QImode;
+}
+
+/* Search the uses of a return value. */
+static bool is_return_value_captured(gimple_set *visited_defs, const gcall *call)
+{
+ tree ret = gimple_call_lhs(call);
+
+ gcc_assert(ret != NULL_TREE);
+ return search_capture_ssa_use(visited_defs, ret);
+}
+
+/* Check if arg_num is a nocapture argument. */
+static bool is_call_arg_nocapture(gimple_set *visited_defs, const gcall *call, int arg_num)
+{
+ tree fndecl = gimple_call_fndecl(call);
+
+ if (fndecl == NULL_TREE)
+ fndecl = gimple_call_fn(call);
+ gcc_assert(fndecl != NULL_TREE);
+
+ if (is_negative_nocapture_arg(fndecl, -arg_num) && is_return_value_captured(visited_defs, call))
+ return false;
+
+ return is_stmt_nocapture_arg(call, arg_num);
+}
+
+/* Determine whether the function has at least one nocapture argument. */
+static bool has_nocapture_param(const_tree fndecl)
+{
+ const_tree attr;
+
+ if (fndecl == NULL_TREE)
+ return false;
+
+ if (is_syscall(fndecl))
+ return true;
+
+ attr = lookup_attribute("nocapture", DECL_ATTRIBUTES(fndecl));
+ if (attr == NULL_TREE)
+ attr = lookup_attribute("format", TYPE_ATTRIBUTES(TREE_TYPE(fndecl)));
+ return attr != NULL_TREE;
+}
+
+static void walk_def_stmt(bool *has_capture_use, gimple_set *visited, tree node)
+{
+ gimple def_stmt;
+ const_tree parm_decl;
+
+ if (*has_capture_use)
+ return;
+
+ if (TREE_CODE(node) != SSA_NAME)
+ goto true_out;
+
+ parm_decl = SSA_NAME_VAR(node);
+ if (parm_decl != NULL_TREE && TREE_CODE(parm_decl) == PARM_DECL)
+ return;
+
+ def_stmt = initify_get_def_stmt(node);
+ if (pointer_set_insert(visited, def_stmt))
+ return;
+
+ switch (gimple_code(def_stmt)) {
+ case GIMPLE_CALL: {
+ tree fndecl = gimple_call_fndecl(def_stmt);
+
+ if (fndecl == NULL_TREE)
+ fndecl = gimple_call_fn(def_stmt);
+
+ gcc_assert(fndecl != NULL_TREE);
+ if (has_nocapture_param(fndecl))
+ goto true_out;
+ return;
+ }
+
+ case GIMPLE_ASM:
+ case GIMPLE_ASSIGN:
+ goto true_out;
+
+ case GIMPLE_NOP:
+ return;
+
+ case GIMPLE_PHI: {
+ unsigned int i;
+
+ for (i = 0; i < gimple_phi_num_args(def_stmt); i++) {
+ tree arg = gimple_phi_arg_def(def_stmt, i);
+
+ walk_def_stmt(has_capture_use, visited, arg);
+ }
+ return;
+ }
+
+ default:
+ debug_gimple_stmt(def_stmt);
+ error("%s: unknown gimple code", __func__);
+ gcc_unreachable();
+ }
+ gcc_unreachable();
+
+true_out:
+ *has_capture_use = true;
+}
+
+static bool search_return_capture_use(const greturn *ret_stmt)
+{
+ gimple_set *def_visited;
+ tree ret;
+ bool has_capture_use;
+
+ if (is_negative_nocapture_arg(current_function_decl, 0))
+ return false;
+
+ def_visited = pointer_set_create();
+ ret = gimple_return_retval(ret_stmt);
+ has_capture_use = false;
+ walk_def_stmt(&has_capture_use, def_visited, ret);
+ pointer_set_destroy(def_visited);
+
+ return has_capture_use;
+}
+
+static bool lhs_is_a_nocapture_parm_decl(const_tree lhs)
+{
+ int arg_idx, len;
+ tree arg_list;
+
+ if (TREE_CODE(lhs) != PARM_DECL)
+ return false;
+
+ arg_list = DECL_ARGUMENTS(current_function_decl);
+ len = list_length(arg_list);
+
+ for (arg_idx = 0; arg_idx < len; arg_idx++) {
+ const_tree arg = chain_index(arg_idx, arg_list);
+
+ if (arg == lhs)
+ return is_fndecl_nocapture_arg(current_function_decl, arg_idx + 1) != NONE_ATTRIBUTE;
+ }
+
+ debug_tree(current_function_decl);
+ debug_tree(lhs);
+ gcc_unreachable();
+}
+
+static void has_capture_use_ssa_var(bool *has_capture_use, gimple_set *visited_defs, tree_set *use_visited, tree node)
+{
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+
+ if (pointer_set_insert(use_visited, node))
+ return;
+
+ if (*has_capture_use)
+ return;
+
+ if (is_va_format_use_nocapture(node))
+ return;
+
+ if (lhs_is_a_nocapture_parm_decl(node))
+ return;
+
+ if (TREE_CODE(node) != SSA_NAME)
+ goto true_out;
+
+ FOR_EACH_IMM_USE_FAST(use_p, imm_iter, node) {
+ gimple use_stmt = USE_STMT(use_p);
+
+ if (use_stmt == NULL)
+ return;
+ if (is_gimple_debug(use_stmt))
+ continue;
+
+ if (pointer_set_insert(visited_defs, use_stmt))
+ continue;
+
+ switch (gimple_code(use_stmt)) {
+ case GIMPLE_COND:
+ case GIMPLE_SWITCH:
+ return;
+
+ case GIMPLE_ASM:
+ goto true_out;
+
+ case GIMPLE_CALL: {
+ const gcall *call = as_a_const_gcall(use_stmt);
+ int arg_num = get_arg_num(call, node);
+
+ if (is_call_arg_nocapture(visited_defs, call, arg_num))
+ return;
+ goto true_out;
+ }
+
+ case GIMPLE_ASSIGN: {
+ tree lhs;
+ gassign *assign = as_a_gassign(use_stmt);
+ const_tree rhs = gimple_assign_rhs1(assign);
+
+ if (TREE_CODE(rhs) == INDIRECT_REF)
+ return;
+#if BUILDING_GCC_VERSION >= 4006
+ if (TREE_CODE(rhs) == MEM_REF)
+ return;
+#endif
+ if (is_cast_to_integer_type(assign))
+ return;
+
+ lhs = gimple_assign_lhs(assign);
+ has_capture_use_ssa_var(has_capture_use, visited_defs, use_visited, lhs);
+ return;
+ }
+
+ case GIMPLE_PHI: {
+ tree result = gimple_phi_result(use_stmt);
+
+ has_capture_use_ssa_var(has_capture_use, visited_defs, use_visited, result);
+ return;
+ }
+
+ case GIMPLE_RETURN:
+ if (search_return_capture_use(as_a_const_greturn(use_stmt)))
+ goto true_out;
+ return;
+
+ default:
+ debug_tree(node);
+ debug_gimple_stmt(use_stmt);
+ gcc_unreachable();
+ }
+ }
+ return;
+
+true_out:
+ *has_capture_use = true;
+}
+
+static bool search_capture_ssa_use(gimple_set *visited_defs, tree node)
+{
+ tree_set *use_visited;
+ bool has_capture_use = false;
+
+ use_visited = tree_pointer_set_create();
+ has_capture_use_ssa_var(&has_capture_use, visited_defs, use_visited, node);
+ pointer_set_destroy(use_visited);
+
+ return has_capture_use;
+}
+
+static bool search_capture_use(const_tree vardecl, gimple stmt)
+{
+ unsigned int i;
+ gimple_set *visited_defs = pointer_set_create();
+
+ for (i = 0; i < gimple_num_ops(stmt); i++) {
+ int arg_num;
+ tree op = *(gimple_op_ptr(stmt, i));
+
+ if (op == NULL_TREE)
+ continue;
+ if (is_gimple_constant(op))
+ continue;
+
+ if (!compare_ops(vardecl, op))
+ continue;
+
+ switch (gimple_code(stmt)) {
+ case GIMPLE_COND:
+ break;
+
+ case GIMPLE_ASM:
+ gcc_assert(get_init_exit_section(vardecl) == NONE);
+ goto true_out;
+
+ case GIMPLE_CALL:
+ if (i == 0)
+ break;
+ /* return, fndecl */
+ gcc_assert(i >= 3);
+ arg_num = i - 2;
+
+ if (is_call_arg_nocapture(visited_defs, as_a_const_gcall(stmt), arg_num))
+ break;
+ goto true_out;
+
+ case GIMPLE_ASSIGN: {
+ tree lhs;
+ const_tree rhs = gimple_assign_rhs1(stmt);
+
+ if (TREE_CODE(rhs) == INDIRECT_REF)
+ break;
+#if BUILDING_GCC_VERSION >= 4006
+ if (TREE_CODE(rhs) == MEM_REF)
+ break;
+#endif
+
+ lhs = gimple_assign_lhs(stmt);
+ if (lhs_is_a_nocapture_parm_decl(lhs))
+ break;
+
+ if (!search_capture_ssa_use(visited_defs, lhs))
+ break;
+ gcc_assert(get_init_exit_section(vardecl) == NONE);
+ goto true_out;
+ }
+
+ case GIMPLE_RETURN:
+ if (search_return_capture_use(as_a_const_greturn(stmt)))
+ goto true_out;
+ break;
+ default:
+ debug_tree(vardecl);
+ debug_gimple_stmt(stmt);
+ gcc_unreachable();
+ }
+ }
+
+ pointer_set_destroy(visited_defs);
+ return false;
+
+true_out:
+ pointer_set_destroy(visited_defs);
+ return true;
+
+}
+
+/* Check all initialized local variables for nocapture uses. */
+static bool is_in_capture_init(const_tree vardecl)
+{
+ unsigned int i __unused;
+ tree var;
+
+ if (TREE_CODE(vardecl) == PARM_DECL)
+ return false;
+
+ FOR_EACH_LOCAL_DECL(cfun, i, var) {
+ const_tree type, initial = DECL_INITIAL(var);
+
+ if (DECL_EXTERNAL(var))
+ continue;
+ if (initial == NULL_TREE)
+ continue;
+ if (TREE_CODE(initial) != CONSTRUCTOR)
+ continue;
+
+ type = TREE_TYPE(var);
+ gcc_assert(TREE_CODE(type) == RECORD_TYPE || DECL_P(var));
+ if (check_constructor(initial, vardecl))
+ return true;
+ }
+ return false;
+}
+
+static bool has_capture_use_local_var(const_tree vardecl)
+{
+ basic_block bb;
+ enum tree_code code = TREE_CODE(vardecl);
+
+ gcc_assert(code == VAR_DECL || code == PARM_DECL);
+
+ if (is_in_capture_init(vardecl))
+ return true;
+
+ FOR_EACH_BB_FN(bb, cfun) {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+ if (search_capture_use(vardecl, gsi_stmt(gsi)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Search local variables that have only nocapture uses. */
+static void find_local_str(void)
+{
+ unsigned int i __unused;
+ tree var;
+
+ FOR_EACH_LOCAL_DECL(cfun, i, var) {
+ tree str, init_val;
+
+ if (TREE_CODE(TREE_TYPE(var)) != ARRAY_TYPE)
+ continue;
+
+ init_val = DECL_INITIAL(var);
+ if (init_val == NULL_TREE || init_val == error_mark_node)
+ continue;
+ if (TREE_CODE(init_val) != STRING_CST)
+ continue;
+
+ if (has_capture_use_local_var(var))
+ continue;
+
+ str = get_string_cst(init_val);
+ gcc_assert(str);
+
+ if (set_init_exit_section(var) && verbose)
+ inform(DECL_SOURCE_LOCATION(var), "initified local var: %s: %s", DECL_NAME_POINTER(current_function_decl), TREE_STRING_POINTER(str));
+ }
+}
+
+static tree create_decl(tree node)
+{
+ tree str, decl, type, name, type_type;
+ location_t loc;
+
+ str = get_string_cst(node);
+ type = TREE_TYPE(str);
+ gcc_assert(TREE_CODE(type) == ARRAY_TYPE);
+
+ type_type = TREE_TYPE(type);
+ gcc_assert(type_type != NULL_TREE && TREE_CODE(type_type) == INTEGER_TYPE);
+
+ name = create_tmp_var_name("initify");
+ loc = DECL_SOURCE_LOCATION(current_function_decl);
+ decl = build_decl(loc, VAR_DECL, name, type);
+
+ DECL_INITIAL(decl) = str;
+ DECL_CONTEXT(decl) = current_function_decl;
+ DECL_ARTIFICIAL(decl) = 1;
+
+ TREE_STATIC(decl) = 1;
+ TREE_READONLY(decl) = 1;
+ TREE_ADDRESSABLE(decl) = 1;
+ TREE_USED(decl) = 1;
+
+ add_referenced_var(decl);
+ add_local_decl(cfun, decl);
+
+ varpool_add_new_variable(decl);
+ varpool_mark_needed_node(varpool_node(decl));
+
+ DECL_CHAIN(decl) = BLOCK_VARS(DECL_INITIAL(current_function_decl));
+ BLOCK_VARS(DECL_INITIAL(current_function_decl)) = decl;
+
+ return build_fold_addr_expr_loc(loc, decl);
+}
+
+static void set_section_call_assign(gimple stmt, tree node, unsigned int num)
+{
+ tree decl;
+
+ decl = create_decl(node);
+
+ switch (gimple_code(stmt)) {
+ case GIMPLE_ASSIGN:
+ gcc_assert(gimple_num_ops(stmt) == 2);
+ gimple_assign_set_rhs1(stmt, decl);
+ break;
+
+ case GIMPLE_CALL:
+ gimple_call_set_arg(stmt, num, decl);
+ break;
+
+ default:
+ debug_gimple_stmt(stmt);
+ error("%s: unknown gimple code", __func__);
+ gcc_unreachable();
+ }
+
+ update_stmt(stmt);
+
+ if (set_init_exit_section(TREE_OPERAND(decl, 0)) && verbose)
+ inform(gimple_location(stmt), "initified function arg: %E: [%E]", current_function_decl, get_string_cst(node));
+}
+
+static tree initify_create_new_var(tree type)
+{
+ tree new_var = create_tmp_var(type, "initify");
+
+ add_referenced_var(new_var);
+ mark_sym_for_renaming(new_var);
+ return new_var;
+}
+
+static void initify_create_new_phi_arg(gimple_set *visited_defs, tree ssa_var, gphi *stmt, unsigned int i)
+{
+ gassign *assign;
+ gimple_stmt_iterator gsi;
+ basic_block arg_bb;
+ tree decl, arg;
+ const_tree str;
+ location_t loc;
+
+ arg = gimple_phi_arg_def(stmt, i);
+
+ if (search_capture_ssa_use(visited_defs, arg))
+ return;
+
+ decl = create_decl(arg);
+
+ assign = gimple_build_assign(ssa_var, decl);
+
+ arg_bb = gimple_phi_arg_edge(stmt, i)->src;
+ gcc_assert(arg_bb->index != 0);
+
+ gsi = gsi_after_labels(arg_bb);
+ gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
+ update_stmt(assign);
+
+ if (!set_init_exit_section(TREE_OPERAND(decl, 0)) || !verbose)
+ return;
+
+ loc = gimple_location(stmt);
+ str = get_string_cst(arg);
+ inform(loc, "initified local var, phi arg: %E: [%E]", current_function_decl, str);
+}
+
+static void set_section_phi(bool *has_str_cst, gimple_set *visited, gphi *stmt)
+{
+ tree result, ssa_var;
+ unsigned int i;
+
+ result = gimple_phi_result(stmt);
+ ssa_var = initify_create_new_var(TREE_TYPE(result));
+
+ for (i = 0; i < gimple_phi_num_args(stmt); i++) {
+ tree arg = gimple_phi_arg_def(stmt, i);
+
+ if (get_string_cst(arg) == NULL_TREE)
+ search_constant_strings(has_str_cst, visited, arg);
+ else
+ initify_create_new_phi_arg(visited, ssa_var, stmt, i);
+ }
+}
+
+static void search_constant_strings(bool *has_str_cst, gimple_set *visited, tree node)
+{
+ gimple def_stmt;
+ const_tree parm_decl;
+
+ if (!*has_str_cst)
+ return;
+
+ if (TREE_CODE(node) != SSA_NAME)
+ goto false_out;
+
+ parm_decl = SSA_NAME_VAR(node);
+ if (parm_decl != NULL_TREE && TREE_CODE(parm_decl) == PARM_DECL)
+ goto false_out;
+
+ def_stmt = initify_get_def_stmt(node);
+ if (pointer_set_insert(visited, def_stmt))
+ return;
+
+ switch (gimple_code(def_stmt)) {
+ case GIMPLE_NOP:
+ case GIMPLE_CALL:
+ case GIMPLE_ASM:
+ case GIMPLE_RETURN:
+ goto false_out;
+
+ case GIMPLE_PHI:
+ set_section_phi(has_str_cst, visited, as_a_gphi(def_stmt));
+ return;
+
+ case GIMPLE_ASSIGN: {
+ tree rhs1, str;
+
+ if (gimple_num_ops(def_stmt) != 2)
+ goto false_out;
+
+ rhs1 = gimple_assign_rhs1(def_stmt);
+ search_constant_strings(has_str_cst, visited, rhs1);
+ if (!*has_str_cst)
+ return;
+
+ if (search_capture_ssa_use(visited, node))
+ goto false_out;
+
+ str = get_string_cst(rhs1);
+ gcc_assert(str != NULL_TREE);
+ set_section_call_assign(def_stmt, rhs1, 0);
+ return;
+ }
+
+ default:
+ debug_gimple_stmt(def_stmt);
+ error("%s: unknown gimple code", __func__);
+ gcc_unreachable();
+ }
+ gcc_unreachable();
+
+false_out:
+ *has_str_cst = false;
+}
+
+/* Search constant strings assigned to variables. */
+static void search_var_param(gcall *stmt)
+{
+ int num;
+ gimple_set *visited = pointer_set_create();
+
+ pointer_set_insert(visited, stmt);
+
+ for (num = 0; num < (int)gimple_call_num_args(stmt); num++) {
+ const_tree type, fndecl;
+ bool has_str_cst = true;
+ tree str, arg = gimple_call_arg(stmt, num);
+
+ str = get_string_cst(arg);
+ if (str != NULL_TREE)
+ continue;
+
+ if (TREE_CODE(TREE_TYPE(arg)) != POINTER_TYPE)
+ continue;
+ type = TREE_TYPE(TREE_TYPE(arg));
+ if (!TYPE_STRING_FLAG(type))
+ continue;
+
+ fndecl = gimple_call_fndecl(stmt);
+ if (is_negative_nocapture_arg(fndecl, -(num + 1)) && is_return_value_captured(visited, stmt))
+ continue;
+
+ if (is_fndecl_nocapture_arg(fndecl, num + 1) != NONE_ATTRIBUTE)
+ search_constant_strings(&has_str_cst, visited, arg);
+ }
+
+ pointer_set_destroy(visited);
+}
+
+/* Search constant strings passed as arguments. */
+static void search_str_param(gcall *stmt)
+{
+ int num;
+ gimple_set *visited = pointer_set_create();
+
+ pointer_set_insert(visited, stmt);
+
+ for (num = 0; num < (int)gimple_call_num_args(stmt); num++) {
+ const_tree fndecl;
+ tree str, arg = gimple_call_arg(stmt, num);
+
+ str = get_string_cst(arg);
+ if (str == NULL_TREE)
+ continue;
+
+ fndecl = gimple_call_fndecl(stmt);
+ if (is_negative_nocapture_arg(fndecl, -(num + 1)) && is_return_value_captured(visited, stmt))
+ continue;
+
+ if (is_fndecl_nocapture_arg(fndecl, num + 1) != NONE_ATTRIBUTE)
+ set_section_call_assign(stmt, arg, num);
+ }
+
+ pointer_set_destroy(visited);
+}
+
+/* Search constant strings in arguments of nocapture functions. */
+static void search_const_strs(void)
+{
+ basic_block bb;
+
+ FOR_EACH_BB_FN(bb, cfun) {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+ gcall *call_stmt;
+ gimple stmt = gsi_stmt(gsi);
+
+ if (!is_gimple_call(stmt))
+ continue;
+
+ call_stmt = as_a_gcall(stmt);
+ if (!has_nocapture_param(gimple_call_fndecl(call_stmt)))
+ continue;
+ search_str_param(call_stmt);
+ search_var_param(call_stmt);
+ }
+ }
+}
+
+/*
+ * Verify the data flows of the uses of function arguments marked by the nocapture attribute.
+ * The printf attribute is ignored temporarily.
+ */
+static void verify_nocapture_functions(void)
+{
+ int i, len;
+ tree arg_list;
+
+ if (disable_verify_nocapture_functions)
+ return;
+
+ if (is_syscall(current_function_decl))
+ return;
+
+ if (!has_nocapture_param(current_function_decl))
+ return;
+
+ arg_list = DECL_ARGUMENTS(current_function_decl);
+ len = list_length(arg_list);
+ for (i = 0; i < len; i++) {
+ const_tree arg;
+
+ if (is_fndecl_nocapture_arg(current_function_decl, i + 1) != NOCAPTURE)
+ continue;
+
+ arg = chain_index(i, arg_list);
+ gcc_assert(arg != NULL_TREE);
+
+ if (has_capture_use_local_var(arg))
+ warning(0, "%qE captures its %u (%qD) parameter, please remove it from the nocapture attribute.", current_function_decl, i + 1, arg);
+ }
+}
+
+/* Find and move constant strings to the proper init or exit read-only data section. */
+static unsigned int initify_function_transform(struct cgraph_node *node __unused)
+{
+ verify_nocapture_functions();
+
+ if (get_init_exit_section(current_function_decl) == NONE)
+ return 0;
+
+ find_local_str();
+ search_const_strs();
+
+ return TODO_dump_func | TODO_verify_ssa | TODO_verify_stmts
+ | TODO_remove_unused_locals | TODO_cleanup_cfg
+ | TODO_ggc_collect | TODO_verify_flow | TODO_update_ssa;
+}
+
+static void __unused debug_print_section_type(struct cgraph_node *node)
+{
+ enum section_type section;
+
+ section = (enum section_type)(unsigned long)NODE_SYMBOL(node)->aux;
+ switch (section) {
+ case INIT:
+ fprintf(stderr, "init\n");
+ break;
+
+ case EXIT:
+ fprintf(stderr, "exit\n");
+ break;
+
+ case BOTH:
+ fprintf(stderr, "init and exit\n");
+ break;
+
+ case NONE:
+ fprintf(stderr, "none\n");
+ break;
+ }
+}
+
+static bool has_non_init_caller(struct cgraph_node *callee)
+{
+ struct cgraph_edge *e = callee->callers;
+
+ if (!e)
+ return 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)
+ return true;
+ }
+
+ return false;
+}
+
+static bool has_non_init_clone(cgraph_set *visited, struct cgraph_node *node)
+{
+ if (!node)
+ return false;
+
+ if (pointer_set_insert(visited, node))
+ return false;
+
+ if (has_non_init_caller(node))
+ return true;
+
+ if (has_non_init_clone(visited, node->clones))
+ return true;
+
+ return has_non_init_clone(visited, node->clone_of);
+}
+
+/*
+ * 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)
+{
+ cgraph_set *visited;
+ bool has_non_init;
+ 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;
+
+ visited = cgraph_pointer_set_create();
+ has_non_init = has_non_init_clone(visited, callee);
+ pointer_set_destroy(visited);
+
+ return !has_non_init;
+}
+
+static bool inherit_section(struct cgraph_node *callee, struct cgraph_node *caller, enum section_type caller_section)
+{
+ enum section_type callee_section;
+
+ if (caller_section == NONE)
+ caller_section = (enum section_type)(unsigned long)NODE_SYMBOL(caller)->aux;
+
+ callee_section = (enum section_type)(unsigned long)NODE_SYMBOL(callee)->aux;
+ if (caller_section == INIT && callee_section == EXIT)
+ goto both_section;
+
+ if (caller_section == EXIT && callee_section == INIT)
+ goto both_section;
+
+ if (caller_section == BOTH && (callee_section == INIT || callee_section == EXIT))
+ goto both_section;
+
+ if (!should_init_exit(callee))
+ return false;
+
+ gcc_assert(callee_section == NONE);
+ NODE_SYMBOL(callee)->aux = (void *)caller_section;
+ return true;
+
+both_section:
+ NODE_SYMBOL(callee)->aux = (void *)BOTH;
+ return true;
+}
+
+/*
+ * Try to propagate __init/__exit to callees in __init/__exit functions.
+ * If a function is called by __init and __exit functions as well then it can be
+ * an __exit function at most.
+ */
+static bool search_init_exit_callers(void)
+{
+ struct cgraph_node *node;
+ bool change = false;
+
+ FOR_EACH_FUNCTION(node) {
+ struct cgraph_edge *e;
+ enum section_type section;
+ const_tree cur_fndecl = NODE_DECL(node);
+
+ if (DECL_BUILT_IN(cur_fndecl))
+ continue;
+
+ section = get_init_exit_section(cur_fndecl);
+ if (section == NONE && NODE_SYMBOL(node)->aux == (void *)NONE)
+ continue;
+
+ for (e = node->callees; e; e = e->next_callee) {
+ if (e->callee->global.inlined_to)
+ continue;
+
+ if (inherit_section(e->callee, node, section))
+ change = true;
+ }
+ }
+
+ return change;
+}
+
+/* We can't move functions to the init/exit sections from certain sections. */
+static bool can_move_to_init_exit(const_tree fndecl)
+{
+ const char *section_name = get_decl_section_name(fndecl);
+
+ if (!section_name)
+ return true;
+
+ if (!strcmp(section_name, ".ref.text"))
+ return false;
+
+ if (!strcmp(section_name, ".meminit.text"))
+ return false;
+
+ inform(DECL_SOURCE_LOCATION(fndecl), "Section of %qE: %s\n", fndecl, section_name);
+ gcc_unreachable();
+}
+
+static void move_function_to_init_exit_text(struct cgraph_node *node)
+{
+ const char *section_name;
+ tree id, attr;
+ tree section_str, attr_args, fndecl = NODE_DECL(node);
+
+ /*
+ * If the function is a candidate for both __init and __exit and enable_init_to_exit_moves is false
+ * then these functions arent't moved to the exit section.
+ */
+ if (NODE_SYMBOL(node)->aux == (void *)BOTH) {
+ if (enable_init_to_exit_moves)
+ NODE_SYMBOL(node)->aux = (void *)EXIT;
+ else
+ return;
+ }
+
+ if (NODE_SYMBOL(node)->aux == (void *)NONE)
+ return;
+
+ if (!can_move_to_init_exit(fndecl))
+ return;
+
+ if (verbose) {
+ const char *attr_name;
+ location_t loc = DECL_SOURCE_LOCATION(fndecl);
+
+ attr_name = NODE_SYMBOL(node)->aux == (void *)INIT ? "__init" : "__exit";
+
+ if (in_lto_p && TREE_PUBLIC(fndecl))
+ inform(loc, "%s attribute is missing from the %qE function (public)", attr_name, fndecl);
+
+ if (!in_lto_p && !TREE_PUBLIC(fndecl))
+ inform(loc, "%s attribute is missing from the %qE function (static)", attr_name, fndecl);
+ }
+
+ if (in_lto_p)
+ return;
+
+ /* Add the init/exit section attribute to the function declaration. */
+ DECL_ATTRIBUTES(fndecl) = copy_list(DECL_ATTRIBUTES(fndecl));
+
+ section_name = NODE_SYMBOL(node)->aux == (void *)INIT ? ".init.text" : ".exit.text";
+ section_str = build_const_char_string(strlen(section_name) + 1, section_name);
+ TREE_READONLY(section_str) = 1;
+ TREE_STATIC(section_str) = 1;
+ attr_args = build_tree_list(NULL_TREE, section_str);
+
+ id = get_identifier("__section__");
+ attr = DECL_ATTRIBUTES(fndecl);
+ DECL_ATTRIBUTES(fndecl) = tree_cons(id, attr_args, attr);
+
+#if BUILDING_GCC_VERSION < 5000
+ DECL_SECTION_NAME(fndecl) = section_str;
+#endif
+ set_decl_section_name(fndecl, section_name);
+}
+
+/* Find all functions that can become __init/__exit functions */
+static unsigned int initify_execute(void)
+{
+ struct cgraph_node *node;
+
+ if (!search_init_exit_functions)
+ return 0;
+
+ if (flag_lto && !in_lto_p)
+ return 0;
+
+ FOR_EACH_FUNCTION(node)
+ NODE_SYMBOL(node)->aux = (void *)NONE;
+
+ while (search_init_exit_callers()) {};
+
+ FOR_EACH_FUNCTION(node) {
+ move_function_to_init_exit_text(node);
+
+ NODE_SYMBOL(node)->aux = NULL;
+ }
+
+ return 0;
+}
+
+#define PASS_NAME initify
+#define NO_WRITE_SUMMARY
+#define NO_GENERATE_SUMMARY
+#define NO_READ_SUMMARY
+#define NO_READ_OPTIMIZATION_SUMMARY
+#define NO_WRITE_OPTIMIZATION_SUMMARY
+#define NO_STMT_FIXUP
+#define NO_VARIABLE_TRANSFORM
+#define NO_GATE
+
+#include "gcc-generate-ipa-pass.h"
+
+static unsigned int (*old_section_type_flags)(tree decl, const char *name, int reloc);
+
+static unsigned int initify_section_type_flags(tree decl, const char *name, int reloc)
+{
+ if (!strcmp(name, ".init.rodata.str") || !strcmp(name, ".exit.rodata.str")) {
+ gcc_assert(TREE_CODE(decl) == VAR_DECL);
+ gcc_assert(DECL_INITIAL(decl));
+ gcc_assert(TREE_CODE(DECL_INITIAL(decl)) == STRING_CST);
+
+ return 1 | SECTION_MERGE | SECTION_STRINGS;
+ }
+
+ return old_section_type_flags(decl, name, reloc);
+}
+
+static void initify_start_unit(void __unused *gcc_data, void __unused *user_data)
+{
+ old_section_type_flags = targetm.section_type_flags;
+ targetm.section_type_flags = initify_section_type_flags;
+}
+
+__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+{
+ 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;
+
+ PASS_INFO(initify, "inline", 1, PASS_POS_INSERT_AFTER);
+
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ if (!(strcmp(argv[i].key, "disable"))) {
+ enabled = false;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "verbose")) {
+ verbose = true;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "print_missing_attr")) {
+ print_missing_attr = true;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "search_init_exit_functions")) {
+ search_init_exit_functions = true;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "enable_init_to_exit_moves")) {
+ enable_init_to_exit_moves = true;
+ continue;
+ }
+
+ if (!strcmp(argv[i].key, "disable_verify_nocapture_functions")) {
+ disable_verify_nocapture_functions = true;
+ continue;
+ }
+
+ error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
+ }
+
+ register_callback(plugin_name, PLUGIN_INFO, NULL, &initify_plugin_info);
+ if (enabled) {
+ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &initify_pass_info);
+ register_callback(plugin_name, PLUGIN_START_UNIT, initify_start_unit, NULL);
+ }
+ register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
+
+ return 0;
+}
diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c
index 8ff203ad4809..65264960910d 100644
--- a/scripts/gcc-plugins/latent_entropy_plugin.c
+++ b/scripts/gcc-plugins/latent_entropy_plugin.c
@@ -592,12 +592,6 @@ __visible int plugin_init(struct plugin_name_args *plugin_info,
const struct plugin_argument * const argv = plugin_info->argv;
int i;
- struct register_pass_info latent_entropy_pass_info;
-
- latent_entropy_pass_info.pass = make_latent_entropy_pass();
- latent_entropy_pass_info.reference_pass_name = "optimized";
- latent_entropy_pass_info.ref_pass_instance_number = 1;
- latent_entropy_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
{
.base = &latent_entropy_decl,
@@ -609,6 +603,8 @@ __visible int plugin_init(struct plugin_name_args *plugin_info,
LAST_GGC_ROOT_TAB
};
+ PASS_INFO(latent_entropy, "optimized", 1, PASS_POS_INSERT_BEFORE);
+
if (!plugin_default_version_check(version, &gcc_version)) {
error(G_("incompatible gcc/plugin versions"));
return 1;
diff --git a/scripts/gcc-plugins/sancov_plugin.c b/scripts/gcc-plugins/sancov_plugin.c
index 70f5fe0d590a..9b0b5cbc5b89 100644
--- a/scripts/gcc-plugins/sancov_plugin.c
+++ b/scripts/gcc-plugins/sancov_plugin.c
@@ -89,7 +89,6 @@ static void sancov_start_unit(void __unused *gcc_data, void __unused *user_data)
__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
{
int i;
- struct register_pass_info sancov_plugin_pass_info;
const char * const plugin_name = plugin_info->base_name;
const int argc = plugin_info->argc;
const struct plugin_argument * const argv = plugin_info->argv;
@@ -107,14 +106,11 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gc
};
/* BBs can be split afterwards?? */
- sancov_plugin_pass_info.pass = make_sancov_pass();
#if BUILDING_GCC_VERSION >= 4009
- sancov_plugin_pass_info.reference_pass_name = "asan";
+ PASS_INFO(sancov, "asan", 0, PASS_POS_INSERT_BEFORE);
#else
- sancov_plugin_pass_info.reference_pass_name = "nrv";
+ PASS_INFO(sancov, "nrv", 1, PASS_POS_INSERT_BEFORE);
#endif
- sancov_plugin_pass_info.ref_pass_instance_number = 0;
- sancov_plugin_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
if (!plugin_default_version_check(version, &gcc_version)) {
error(G_("incompatible gcc/plugin versions"));
diff --git a/scripts/gcc-plugins/structleak_plugin.c b/scripts/gcc-plugins/structleak_plugin.c
new file mode 100644
index 000000000000..deddb7291a26
--- /dev/null
+++ b/scripts/gcc-plugins/structleak_plugin.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2013-2017 by PaX Team <pageexec@freemail.hu>
+ * Licensed under the GPL v2
+ *
+ * Note: the choice of the license means that the compilation process is
+ * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
+ * but for the kernel it doesn't matter since it doesn't link against
+ * any of the gcc libraries
+ *
+ * gcc plugin to forcibly initialize certain local variables that could
+ * otherwise leak kernel stack to userland if they aren't properly initialized
+ * by later code
+ *
+ * Homepage: http://pax.grsecurity.net/
+ *
+ * Options:
+ * -fplugin-arg-structleak_plugin-disable
+ * -fplugin-arg-structleak_plugin-verbose
+ *
+ * Usage:
+ * $ # for 4.5/4.6/C based 4.7
+ * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o structleak_plugin.so structleak_plugin.c
+ * $ # for C++ based 4.7/4.8+
+ * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o structleak_plugin.so structleak_plugin.c
+ * $ gcc -fplugin=./structleak_plugin.so test.c -O2
+ *
+ * TODO: eliminate redundant initializers
+ * increase type coverage
+ */
+
+#include "gcc-common.h"
+
+/* unused C type flag in all versions 4.5-6 */
+#define TYPE_USERSPACE(TYPE) TYPE_LANG_FLAG_5(TYPE)
+
+__visible int plugin_is_GPL_compatible;
+
+static struct plugin_info structleak_plugin_info = {
+ .version = "201607271510vanilla",
+ .help = "disable\tdo not activate plugin\n"
+ "verbose\tprint all initialized variables\n",
+};
+
+static bool verbose;
+
+static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
+{
+ *no_add_attrs = true;
+
+ /* check for types? for now accept everything linux has to offer */
+ if (TREE_CODE(*node) != FIELD_DECL)
+ return NULL_TREE;
+
+ *no_add_attrs = false;
+ return NULL_TREE;
+}
+
+static struct attribute_spec user_attr = {
+ .name = "user",
+ .min_length = 0,
+ .max_length = 0,
+ .decl_required = false,
+ .type_required = false,
+ .function_type_required = false,
+ .handler = handle_user_attribute,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = true
+#endif
+};
+
+static void register_attributes(void *event_data, void *data)
+{
+ register_attribute(&user_attr);
+}
+
+static tree get_field_type(tree field)
+{
+ return strip_array_types(TREE_TYPE(field));
+}
+
+static bool is_userspace_type(tree type)
+{
+ tree field;
+
+ for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) {
+ tree fieldtype = get_field_type(field);
+ enum tree_code code = TREE_CODE(fieldtype);
+
+ if (code == RECORD_TYPE || code == UNION_TYPE)
+ if (is_userspace_type(fieldtype))
+ return true;
+
+ if (lookup_attribute("user", DECL_ATTRIBUTES(field)))
+ return true;
+ }
+ return false;
+}
+
+static void finish_type(void *event_data, void *data)
+{
+ tree type = (tree)event_data;
+
+ if (type == NULL_TREE || type == error_mark_node)
+ return;
+
+#if BUILDING_GCC_VERSION >= 5000
+ if (TREE_CODE(type) == ENUMERAL_TYPE)
+ return;
+#endif
+
+ if (TYPE_USERSPACE(type))
+ return;
+
+ if (is_userspace_type(type))
+ TYPE_USERSPACE(type) = 1;
+}
+
+static void initialize(tree var)
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+ tree initializer;
+ gimple init_stmt;
+
+ /* this is the original entry bb before the forced split */
+ bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+
+ /* first check if variable is already initialized, warn otherwise */
+ for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+ gimple stmt = gsi_stmt(gsi);
+ tree rhs1;
+
+ /* we're looking for an assignment of a single rhs... */
+ if (!gimple_assign_single_p(stmt))
+ continue;
+ rhs1 = gimple_assign_rhs1(stmt);
+#if BUILDING_GCC_VERSION >= 4007
+ /* ... of a non-clobbering expression... */
+ if (TREE_CLOBBER_P(rhs1))
+ continue;
+#endif
+ /* ... to our variable... */
+ if (gimple_get_lhs(stmt) != var)
+ continue;
+ /* if it's an initializer then we're good */
+ if (TREE_CODE(rhs1) == CONSTRUCTOR)
+ return;
+ }
+
+ /* these aren't the 0days you're looking for */
+ if (verbose)
+ inform(DECL_SOURCE_LOCATION(var),
+ "userspace variable will be forcibly initialized");
+
+ /* build the initializer expression */
+ initializer = build_constructor(TREE_TYPE(var), NULL);
+
+ /* build the initializer stmt */
+ init_stmt = gimple_build_assign(var, initializer);
+ gsi = gsi_after_labels(single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+ gsi_insert_before(&gsi, init_stmt, GSI_NEW_STMT);
+ update_stmt(init_stmt);
+}
+
+static unsigned int structleak_execute(void)
+{
+ basic_block bb;
+ unsigned int ret = 0;
+ tree var;
+ unsigned int i;
+
+ /* split the first bb where we can put the forced initializers */
+ gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+ bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+ if (!single_pred_p(bb)) {
+ split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+ gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+ }
+
+ /* enumarate all local variables and forcibly initialize our targets */
+ FOR_EACH_LOCAL_DECL(cfun, i, var) {
+ tree type = TREE_TYPE(var);
+
+ gcc_assert(DECL_P(var));
+ if (!auto_var_in_fn_p(var, current_function_decl))
+ continue;
+
+ /* only care about structure types */
+ if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
+ continue;
+
+ /* if the type is of interest, examine the variable */
+ if (TYPE_USERSPACE(type))
+ initialize(var);
+ }
+
+ return ret;
+}
+
+#define PASS_NAME structleak
+#define NO_GATE
+#define PROPERTIES_REQUIRED PROP_cfg
+#define TODO_FLAGS_FINISH TODO_verify_il | TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_ggc_collect | TODO_verify_flow
+#include "gcc-generate-gimple-pass.h"
+
+__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+{
+ int i;
+ const char * const plugin_name = plugin_info->base_name;
+ const int argc = plugin_info->argc;
+ const struct plugin_argument * const argv = plugin_info->argv;
+ bool enable = true;
+
+ PASS_INFO(structleak, "early_optimizations", 1, PASS_POS_INSERT_BEFORE);
+
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+ if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
+ inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
+ enable = false;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ if (!strcmp(argv[i].key, "disable")) {
+ enable = false;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "verbose")) {
+ verbose = true;
+ continue;
+ }
+ error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
+ }
+
+ register_callback(plugin_name, PLUGIN_INFO, NULL, &structleak_plugin_info);
+ if (enable) {
+ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &structleak_pass_info);
+ register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
+ }
+ register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
+
+ return 0;
+}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 24520b4ef3b0..b89448a69cf4 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -177,7 +177,7 @@ static inline void evm_load_x509(void)
#ifdef CONFIG_INTEGRITY_AUDIT
/* declarations */
-void integrity_audit_msg(int audit_msgno, struct inode *inode,
+void __nocapture(4) integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info);
#else