Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'for-next/gcc-plugin/structleak' into for-linus/gcc-plugins

+277 -1
+22
arch/Kconfig
··· 410 410 * https://grsecurity.net/ 411 411 * https://pax.grsecurity.net/ 412 412 413 + config GCC_PLUGIN_STRUCTLEAK 414 + bool "Force initialization of variables containing userspace addresses" 415 + depends on GCC_PLUGINS 416 + help 417 + This plugin zero-initializes any structures that containing a 418 + __user attribute. This can prevent some classes of information 419 + exposures. 420 + 421 + This plugin was ported from grsecurity/PaX. More information at: 422 + * https://grsecurity.net/ 423 + * https://pax.grsecurity.net/ 424 + 425 + config GCC_PLUGIN_STRUCTLEAK_VERBOSE 426 + bool "Report forcefully initialized variables" 427 + depends on GCC_PLUGIN_STRUCTLEAK 428 + depends on !COMPILE_TEST 429 + help 430 + This option will cause a warning to be printed each time the 431 + structleak plugin finds a variable it thinks needs to be 432 + initialized. Since not all existing initializers are detected 433 + by the plugin, this can produce false positive warnings. 434 + 413 435 config HAVE_CC_STACKPROTECTOR 414 436 bool 415 437 help
+5 -1
include/linux/compiler.h
··· 27 27 extern void __chk_io_ptr(const volatile void __iomem *); 28 28 # define ACCESS_PRIVATE(p, member) (*((typeof((p)->member) __force *) &(p)->member)) 29 29 #else /* __CHECKER__ */ 30 - # define __user 30 + # ifdef STRUCTLEAK_PLUGIN 31 + # define __user __attribute__((user)) 32 + # else 33 + # define __user 34 + # endif 31 35 # define __kernel 32 36 # define __safe 33 37 # define __force
+4
scripts/Makefile.gcc-plugins
··· 25 25 endif 26 26 endif 27 27 28 + gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so 29 + gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) += -fplugin-arg-structleak_plugin-verbose 30 + gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += -DSTRUCTLEAK_PLUGIN 31 + 28 32 GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) 29 33 30 34 export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
+246
scripts/gcc-plugins/structleak_plugin.c
··· 1 + /* 2 + * Copyright 2013-2017 by PaX Team <pageexec@freemail.hu> 3 + * Licensed under the GPL v2 4 + * 5 + * Note: the choice of the license means that the compilation process is 6 + * NOT 'eligible' as defined by gcc's library exception to the GPL v3, 7 + * but for the kernel it doesn't matter since it doesn't link against 8 + * any of the gcc libraries 9 + * 10 + * gcc plugin to forcibly initialize certain local variables that could 11 + * otherwise leak kernel stack to userland if they aren't properly initialized 12 + * by later code 13 + * 14 + * Homepage: http://pax.grsecurity.net/ 15 + * 16 + * Options: 17 + * -fplugin-arg-structleak_plugin-disable 18 + * -fplugin-arg-structleak_plugin-verbose 19 + * 20 + * Usage: 21 + * $ # for 4.5/4.6/C based 4.7 22 + * $ 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 23 + * $ # for C++ based 4.7/4.8+ 24 + * $ 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 25 + * $ gcc -fplugin=./structleak_plugin.so test.c -O2 26 + * 27 + * TODO: eliminate redundant initializers 28 + * increase type coverage 29 + */ 30 + 31 + #include "gcc-common.h" 32 + 33 + /* unused C type flag in all versions 4.5-6 */ 34 + #define TYPE_USERSPACE(TYPE) TYPE_LANG_FLAG_5(TYPE) 35 + 36 + __visible int plugin_is_GPL_compatible; 37 + 38 + static struct plugin_info structleak_plugin_info = { 39 + .version = "201607271510vanilla", 40 + .help = "disable\tdo not activate plugin\n" 41 + "verbose\tprint all initialized variables\n", 42 + }; 43 + 44 + static bool verbose; 45 + 46 + static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs) 47 + { 48 + *no_add_attrs = true; 49 + 50 + /* check for types? for now accept everything linux has to offer */ 51 + if (TREE_CODE(*node) != FIELD_DECL) 52 + return NULL_TREE; 53 + 54 + *no_add_attrs = false; 55 + return NULL_TREE; 56 + } 57 + 58 + static struct attribute_spec user_attr = { 59 + .name = "user", 60 + .min_length = 0, 61 + .max_length = 0, 62 + .decl_required = false, 63 + .type_required = false, 64 + .function_type_required = false, 65 + .handler = handle_user_attribute, 66 + #if BUILDING_GCC_VERSION >= 4007 67 + .affects_type_identity = true 68 + #endif 69 + }; 70 + 71 + static void register_attributes(void *event_data, void *data) 72 + { 73 + register_attribute(&user_attr); 74 + } 75 + 76 + static tree get_field_type(tree field) 77 + { 78 + return strip_array_types(TREE_TYPE(field)); 79 + } 80 + 81 + static bool is_userspace_type(tree type) 82 + { 83 + tree field; 84 + 85 + for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) { 86 + tree fieldtype = get_field_type(field); 87 + enum tree_code code = TREE_CODE(fieldtype); 88 + 89 + if (code == RECORD_TYPE || code == UNION_TYPE) 90 + if (is_userspace_type(fieldtype)) 91 + return true; 92 + 93 + if (lookup_attribute("user", DECL_ATTRIBUTES(field))) 94 + return true; 95 + } 96 + return false; 97 + } 98 + 99 + static void finish_type(void *event_data, void *data) 100 + { 101 + tree type = (tree)event_data; 102 + 103 + if (type == NULL_TREE || type == error_mark_node) 104 + return; 105 + 106 + #if BUILDING_GCC_VERSION >= 5000 107 + if (TREE_CODE(type) == ENUMERAL_TYPE) 108 + return; 109 + #endif 110 + 111 + if (TYPE_USERSPACE(type)) 112 + return; 113 + 114 + if (is_userspace_type(type)) 115 + TYPE_USERSPACE(type) = 1; 116 + } 117 + 118 + static void initialize(tree var) 119 + { 120 + basic_block bb; 121 + gimple_stmt_iterator gsi; 122 + tree initializer; 123 + gimple init_stmt; 124 + 125 + /* this is the original entry bb before the forced split */ 126 + bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); 127 + 128 + /* first check if variable is already initialized, warn otherwise */ 129 + for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { 130 + gimple stmt = gsi_stmt(gsi); 131 + tree rhs1; 132 + 133 + /* we're looking for an assignment of a single rhs... */ 134 + if (!gimple_assign_single_p(stmt)) 135 + continue; 136 + rhs1 = gimple_assign_rhs1(stmt); 137 + #if BUILDING_GCC_VERSION >= 4007 138 + /* ... of a non-clobbering expression... */ 139 + if (TREE_CLOBBER_P(rhs1)) 140 + continue; 141 + #endif 142 + /* ... to our variable... */ 143 + if (gimple_get_lhs(stmt) != var) 144 + continue; 145 + /* if it's an initializer then we're good */ 146 + if (TREE_CODE(rhs1) == CONSTRUCTOR) 147 + return; 148 + } 149 + 150 + /* these aren't the 0days you're looking for */ 151 + if (verbose) 152 + inform(DECL_SOURCE_LOCATION(var), 153 + "userspace variable will be forcibly initialized"); 154 + 155 + /* build the initializer expression */ 156 + initializer = build_constructor(TREE_TYPE(var), NULL); 157 + 158 + /* build the initializer stmt */ 159 + init_stmt = gimple_build_assign(var, initializer); 160 + gsi = gsi_after_labels(single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun))); 161 + gsi_insert_before(&gsi, init_stmt, GSI_NEW_STMT); 162 + update_stmt(init_stmt); 163 + } 164 + 165 + static unsigned int structleak_execute(void) 166 + { 167 + basic_block bb; 168 + unsigned int ret = 0; 169 + tree var; 170 + unsigned int i; 171 + 172 + /* split the first bb where we can put the forced initializers */ 173 + gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); 174 + bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); 175 + if (!single_pred_p(bb)) { 176 + split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun))); 177 + gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); 178 + } 179 + 180 + /* enumerate all local variables and forcibly initialize our targets */ 181 + FOR_EACH_LOCAL_DECL(cfun, i, var) { 182 + tree type = TREE_TYPE(var); 183 + 184 + gcc_assert(DECL_P(var)); 185 + if (!auto_var_in_fn_p(var, current_function_decl)) 186 + continue; 187 + 188 + /* only care about structure types */ 189 + if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE) 190 + continue; 191 + 192 + /* if the type is of interest, examine the variable */ 193 + if (TYPE_USERSPACE(type)) 194 + initialize(var); 195 + } 196 + 197 + return ret; 198 + } 199 + 200 + #define PASS_NAME structleak 201 + #define NO_GATE 202 + #define PROPERTIES_REQUIRED PROP_cfg 203 + #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 204 + #include "gcc-generate-gimple-pass.h" 205 + 206 + __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) 207 + { 208 + int i; 209 + const char * const plugin_name = plugin_info->base_name; 210 + const int argc = plugin_info->argc; 211 + const struct plugin_argument * const argv = plugin_info->argv; 212 + bool enable = true; 213 + 214 + PASS_INFO(structleak, "early_optimizations", 1, PASS_POS_INSERT_BEFORE); 215 + 216 + if (!plugin_default_version_check(version, &gcc_version)) { 217 + error(G_("incompatible gcc/plugin versions")); 218 + return 1; 219 + } 220 + 221 + if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) { 222 + inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name); 223 + enable = false; 224 + } 225 + 226 + for (i = 0; i < argc; ++i) { 227 + if (!strcmp(argv[i].key, "disable")) { 228 + enable = false; 229 + continue; 230 + } 231 + if (!strcmp(argv[i].key, "verbose")) { 232 + verbose = true; 233 + continue; 234 + } 235 + error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); 236 + } 237 + 238 + register_callback(plugin_name, PLUGIN_INFO, NULL, &structleak_plugin_info); 239 + if (enable) { 240 + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &structleak_pass_info); 241 + register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL); 242 + } 243 + register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL); 244 + 245 + return 0; 246 + }