diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2017-02-17 11:21:50 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2017-02-17 11:21:50 +1100 |
commit | cd73d1675614a817ff3ea3fbf32e9d8251d7828b (patch) | |
tree | cfb12928c3022310328d2fbc16326332b72d2a70 | |
parent | 3e8e4b94d54386657b5786965967b5089f9a3402 (diff) | |
parent | 5fe5cb271594c90f11730a24ceaca5bebe9f82af (diff) |
Merge remote-tracking branch 'kspp/for-next/kspp'
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 |