gcc-plugins: structleak: add option to init all vars used as byref args
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Sun, 6 Aug 2017 11:06:27 +0000 (12:06 +0100)
committerKees Cook <keescook@chromium.org>
Mon, 7 Aug 2017 18:20:57 +0000 (11:20 -0700)
In the Linux kernel, struct type variables are rarely passed by-value,
and so functions that initialize such variables typically take an input
reference to the variable rather than returning a value that can
subsequently be used in an assignment.

If the initalization function is not part of the same compilation unit,
the lack of an assignment operation defeats any analysis the compiler
can perform as to whether the variable may be used before having been
initialized. This means we may end up passing on such variables
uninitialized, resulting in potential information leaks.

So extend the existing structleak GCC plugin so it will [optionally]
apply to all struct type variables that have their address taken at any
point, rather than only to variables of struct types that have a __user
annotation.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
arch/Kconfig
scripts/Makefile.gcc-plugins
scripts/gcc-plugins/structleak_plugin.c

index 21d0089117fe957be2e32f97d5ab92b3086abd1c..0f1621489bf00c87baecf311114f7939670ff58c 100644 (file)
@@ -458,6 +458,13 @@ config GCC_PLUGIN_STRUCTLEAK
           * https://grsecurity.net/
           * https://pax.grsecurity.net/
 
+config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
+       bool "Force initialize all struct type variables passed by reference"
+       depends on GCC_PLUGIN_STRUCTLEAK
+       help
+         Zero initialize any struct type local variable that may be passed by
+         reference without having been initialized.
+
 config GCC_PLUGIN_STRUCTLEAK_VERBOSE
        bool "Report forcefully initialized variables"
        depends on GCC_PLUGIN_STRUCTLEAK
index 2e0e2eaa397fa05c6b25247092ba0035fd6417f8..d1f7b0d6be66da15585058dfbdbca4a7b13378ac 100644 (file)
@@ -27,6 +27,7 @@ ifdef CONFIG_GCC_PLUGINS
 
   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_ALL)  += -fplugin-arg-structleak_plugin-byref-all
   gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK)    += -DSTRUCTLEAK_PLUGIN
 
   gcc-plugin-$(CONFIG_GCC_PLUGIN_RANDSTRUCT)   += randomize_layout_plugin.so
index fa3d7a4b26f2f9299f6ad34909332475f37d5a63..3f8dd486817814c5d96bdfbd1753a909562c6829 100644 (file)
@@ -16,6 +16,7 @@
  * Options:
  * -fplugin-arg-structleak_plugin-disable
  * -fplugin-arg-structleak_plugin-verbose
+ * -fplugin-arg-structleak_plugin-byref-all
  *
  * Usage:
  * $ # for 4.5/4.6/C based 4.7
@@ -42,6 +43,7 @@ static struct plugin_info structleak_plugin_info = {
 };
 
 static bool verbose;
+static bool byref_all;
 
 static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
 {
@@ -150,7 +152,9 @@ static void initialize(tree var)
        /* these aren't the 0days you're looking for */
        if (verbose)
                inform(DECL_SOURCE_LOCATION(var),
-                       "userspace variable will be forcibly initialized");
+                       "%s variable will be forcibly initialized",
+                       (byref_all && TREE_ADDRESSABLE(var)) ? "byref"
+                                                            : "userspace");
 
        /* build the initializer expression */
        initializer = build_constructor(TREE_TYPE(var), NULL);
@@ -190,7 +194,8 @@ static unsigned int structleak_execute(void)
                        continue;
 
                /* if the type is of interest, examine the variable */
-               if (TYPE_USERSPACE(type))
+               if (TYPE_USERSPACE(type) ||
+                   (byref_all && TREE_ADDRESSABLE(var)))
                        initialize(var);
        }
 
@@ -232,6 +237,10 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gc
                        verbose = true;
                        continue;
                }
+               if (!strcmp(argv[i].key, "byref-all")) {
+                       byref_all = true;
+                       continue;
+               }
                error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
        }