Merge tag 'gcc-plugins-v5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull gcc-plugins updates from Kees Cook: "This adds additional type coverage to the existing structleak plugin and adds a large set of selftests to help evaluate stack variable zero-initialization coverage. That can be used to test whatever instrumentation might be performing zero-initialization: either with the structleak plugin or with Clang's coming "-ftrivial-auto-var-init=zero" option. Summary: - Add scalar and array initialization coverage - Refactor Kconfig to make options more clear - Add self-test module for testing automatic initialization" * tag 'gcc-plugins-v5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: lib: Introduce test_stackinit module gcc-plugins: structleak: Generalize to all variable types
This commit is contained in:
@@ -67,23 +67,59 @@ config GCC_PLUGIN_LATENT_ENTROPY
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK
|
||||
bool "Force initialization of variables containing userspace addresses"
|
||||
bool "Zero initialize stack variables"
|
||||
help
|
||||
This plugin zero-initializes any structures containing a
|
||||
__user attribute. This can prevent some classes of information
|
||||
exposures.
|
||||
While the kernel is built with warnings enabled for any missed
|
||||
stack variable initializations, this warning is silenced for
|
||||
anything passed by reference to another function, under the
|
||||
occasionally misguided assumption that the function will do
|
||||
the initialization. As this regularly leads to exploitable
|
||||
flaws, this plugin is available to identify and zero-initialize
|
||||
such variables, depending on the chosen level of coverage.
|
||||
|
||||
This plugin was ported from grsecurity/PaX. More information at:
|
||||
This plugin was originally ported from grsecurity/PaX. More
|
||||
information at:
|
||||
* https://grsecurity.net/
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
|
||||
bool "Force initialize all struct type variables passed by reference"
|
||||
choice
|
||||
prompt "Coverage"
|
||||
depends on GCC_PLUGIN_STRUCTLEAK
|
||||
depends on !COMPILE_TEST
|
||||
default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
|
||||
help
|
||||
Zero initialize any struct type local variable that may be passed by
|
||||
reference without having been initialized.
|
||||
This chooses the level of coverage over classes of potentially
|
||||
uninitialized variables. The selected class will be
|
||||
zero-initialized before use.
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK_USER
|
||||
bool "structs marked for userspace"
|
||||
help
|
||||
Zero-initialize any structures on the stack containing
|
||||
a __user attribute. This can prevent some classes of
|
||||
uninitialized stack variable exploits and information
|
||||
exposures, like CVE-2013-2141:
|
||||
https://git.kernel.org/linus/b9e146d8eb3b9eca
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK_BYREF
|
||||
bool "structs passed by reference"
|
||||
help
|
||||
Zero-initialize any structures on the stack that may
|
||||
be passed by reference and had not already been
|
||||
explicitly initialized. This can prevent most classes
|
||||
of uninitialized stack variable exploits and information
|
||||
exposures, like CVE-2017-1000410:
|
||||
https://git.kernel.org/linus/06e7e776ca4d3654
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
|
||||
bool "anything passed by reference"
|
||||
help
|
||||
Zero-initialize any stack variables that may be passed
|
||||
by reference and had not already been explicitly
|
||||
initialized. This is intended to eliminate all classes
|
||||
of uninitialized stack variable exploits and information
|
||||
exposures.
|
||||
|
||||
endchoice
|
||||
|
||||
config GCC_PLUGIN_STRUCTLEAK_VERBOSE
|
||||
bool "Report forcefully initialized variables"
|
||||
|
@@ -16,6 +16,7 @@
|
||||
* Options:
|
||||
* -fplugin-arg-structleak_plugin-disable
|
||||
* -fplugin-arg-structleak_plugin-verbose
|
||||
* -fplugin-arg-structleak_plugin-byref
|
||||
* -fplugin-arg-structleak_plugin-byref-all
|
||||
*
|
||||
* Usage:
|
||||
@@ -26,7 +27,6 @@
|
||||
* $ gcc -fplugin=./structleak_plugin.so test.c -O2
|
||||
*
|
||||
* TODO: eliminate redundant initializers
|
||||
* increase type coverage
|
||||
*/
|
||||
|
||||
#include "gcc-common.h"
|
||||
@@ -37,13 +37,18 @@
|
||||
__visible int plugin_is_GPL_compatible;
|
||||
|
||||
static struct plugin_info structleak_plugin_info = {
|
||||
.version = "201607271510vanilla",
|
||||
.version = "20190125vanilla",
|
||||
.help = "disable\tdo not activate plugin\n"
|
||||
"verbose\tprint all initialized variables\n",
|
||||
"byref\tinit structs passed by reference\n"
|
||||
"byref-all\tinit anything passed by reference\n"
|
||||
"verbose\tprint all initialized variables\n",
|
||||
};
|
||||
|
||||
#define BYREF_STRUCT 1
|
||||
#define BYREF_ALL 2
|
||||
|
||||
static bool verbose;
|
||||
static bool byref_all;
|
||||
static int byref;
|
||||
|
||||
static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
|
||||
{
|
||||
@@ -118,6 +123,7 @@ static void initialize(tree var)
|
||||
gimple_stmt_iterator gsi;
|
||||
tree initializer;
|
||||
gimple init_stmt;
|
||||
tree type;
|
||||
|
||||
/* this is the original entry bb before the forced split */
|
||||
bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
|
||||
@@ -148,11 +154,15 @@ static void initialize(tree var)
|
||||
if (verbose)
|
||||
inform(DECL_SOURCE_LOCATION(var),
|
||||
"%s variable will be forcibly initialized",
|
||||
(byref_all && TREE_ADDRESSABLE(var)) ? "byref"
|
||||
: "userspace");
|
||||
(byref && TREE_ADDRESSABLE(var)) ? "byref"
|
||||
: "userspace");
|
||||
|
||||
/* build the initializer expression */
|
||||
initializer = build_constructor(TREE_TYPE(var), NULL);
|
||||
type = TREE_TYPE(var);
|
||||
if (AGGREGATE_TYPE_P(type))
|
||||
initializer = build_constructor(type, NULL);
|
||||
else
|
||||
initializer = fold_convert(type, integer_zero_node);
|
||||
|
||||
/* build the initializer stmt */
|
||||
init_stmt = gimple_build_assign(var, initializer);
|
||||
@@ -184,13 +194,13 @@ static unsigned int structleak_execute(void)
|
||||
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)
|
||||
/* only care about structure types unless byref-all */
|
||||
if (byref != BYREF_ALL && TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
|
||||
continue;
|
||||
|
||||
/* if the type is of interest, examine the variable */
|
||||
if (TYPE_USERSPACE(type) ||
|
||||
(byref_all && TREE_ADDRESSABLE(var)))
|
||||
(byref && TREE_ADDRESSABLE(var)))
|
||||
initialize(var);
|
||||
}
|
||||
|
||||
@@ -232,8 +242,12 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gc
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i].key, "byref")) {
|
||||
byref = BYREF_STRUCT;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i].key, "byref-all")) {
|
||||
byref_all = true;
|
||||
byref = BYREF_ALL;
|
||||
continue;
|
||||
}
|
||||
error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
|
||||
|
Reference in New Issue
Block a user