increased structleak coverage

- And scalar and array initialization coverage
 - Refactor Kconfig to make options more clear
 - Add self-test module for testing automatic initialization
 -----BEGIN PGP SIGNATURE-----
 Comment: Kees Cook <kees@outflux.net>
 
 iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAlx9YaIWHGtlZXNjb29r
 QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJuJ3D/93rm0lxwlokyZH7ik//G8ha6c/
 eH2EelxybyHeK39syY6TG1KeSP1LhvvyHrhuJMnMHfvd7wHJrMyIWZWhbqLTk/+e
 CzrlFg0gbeLacmT5+mwSiyl+iZgpwREyHI96R6cW1AQC/gCh4d828uRKsDB2btGg
 89h6F4vp2AmjbEJgdembPHk8RmdrhStbqxc53WON1217huC8f1fmLsTpPlBSJHV5
 AZFjbmG5bSoWbRD/0NnsKbctO1XTE+WBvZPAWhCqhTjIVL2a/k0OybvlJw26mcmV
 zKOj35uzZ5S6ZBSd23EsAlJNzC9LO2sLQdT+iX9sBKeRqfdcoP7eoeM4KXsXzSHD
 gQ2zcSqYEyNSxJWxtdOX02Yx8rowHAcFB3ZIxK/dN91JAVhF22EAkeenT8Uus0SB
 NkIkp70bHaAscvJ18Ahdkd7GOCk06BWyb/K4Lejy9TBMGXFztZRIHg1YwLiYlSiW
 RNr0STU+vcK56v4sixcNeeLKFVIcne4RbBlaJMv5y5PygVuN3xZTGsg2lhvJNnHA
 EwsPV6D8fx5U8w0taX+U/5IpigIIxfLQU6VTnjydDk1EScpXLy4JCFqE4N9aksqy
 F9PfrP3XXuwULyNd/cRxhHVwyXoQA6xaMZ4Sf4Sp7YHfxMRIWlN/aYfZFanvxQMA
 HJaoHZfjLt/NKCI3JQ==
 =6iu3
 -----END PGP SIGNATURE-----

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:
Linus Torvalds 2019-03-09 09:06:15 -08:00
commit 2bb995405f
6 changed files with 462 additions and 21 deletions

View file

@ -15,6 +15,8 @@ gcc-plugin-$(CONFIG_GCC_PLUGIN_SANCOV) += sancov_plugin.so
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_BYREF) \
+= -fplugin-arg-structleak_plugin-byref
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL) \
+= -fplugin-arg-structleak_plugin-byref-all
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) \

View file

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

View file

@ -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);