linux/lib/test_stackinit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Test cases for compiler-based stack variable zeroing via future
   4 * compiler flags or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
   5 */
   6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   7
   8#include <linux/init.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/string.h>
  12
  13/* Exfiltration buffer. */
  14#define MAX_VAR_SIZE    128
  15static u8 check_buf[MAX_VAR_SIZE];
  16
  17/* Character array to trigger stack protector in all functions. */
  18#define VAR_BUFFER       32
  19
  20/* Volatile mask to convince compiler to copy memory with 0xff. */
  21static volatile u8 forced_mask = 0xff;
  22
  23/* Location and size tracking to validate fill and test are colocated. */
  24static void *fill_start, *target_start;
  25static size_t fill_size, target_size;
  26
  27static bool range_contains(char *haystack_start, size_t haystack_size,
  28                           char *needle_start, size_t needle_size)
  29{
  30        if (needle_start >= haystack_start &&
  31            needle_start + needle_size <= haystack_start + haystack_size)
  32                return true;
  33        return false;
  34}
  35
  36#define DO_NOTHING_TYPE_SCALAR(var_type)        var_type
  37#define DO_NOTHING_TYPE_STRING(var_type)        void
  38#define DO_NOTHING_TYPE_STRUCT(var_type)        void
  39
  40#define DO_NOTHING_RETURN_SCALAR(ptr)           *(ptr)
  41#define DO_NOTHING_RETURN_STRING(ptr)           /**/
  42#define DO_NOTHING_RETURN_STRUCT(ptr)           /**/
  43
  44#define DO_NOTHING_CALL_SCALAR(var, name)                       \
  45                (var) = do_nothing_ ## name(&(var))
  46#define DO_NOTHING_CALL_STRING(var, name)                       \
  47                do_nothing_ ## name(var)
  48#define DO_NOTHING_CALL_STRUCT(var, name)                       \
  49                do_nothing_ ## name(&(var))
  50
  51#define FETCH_ARG_SCALAR(var)           &var
  52#define FETCH_ARG_STRING(var)           var
  53#define FETCH_ARG_STRUCT(var)           &var
  54
  55#define FILL_SIZE_STRING                16
  56
  57#define INIT_CLONE_SCALAR               /**/
  58#define INIT_CLONE_STRING               [FILL_SIZE_STRING]
  59#define INIT_CLONE_STRUCT               /**/
  60
  61#define INIT_SCALAR_none                /**/
  62#define INIT_SCALAR_zero                = 0
  63
  64#define INIT_STRING_none                [FILL_SIZE_STRING] /**/
  65#define INIT_STRING_zero                [FILL_SIZE_STRING] = { }
  66
  67#define INIT_STRUCT_none                /**/
  68#define INIT_STRUCT_zero                = { }
  69#define INIT_STRUCT_static_partial      = { .two = 0, }
  70#define INIT_STRUCT_static_all          = { .one = arg->one,            \
  71                                            .two = arg->two,            \
  72                                            .three = arg->three,        \
  73                                            .four = arg->four,          \
  74                                        }
  75#define INIT_STRUCT_dynamic_partial     = { .two = arg->two, }
  76#define INIT_STRUCT_dynamic_all         = { .one = arg->one,            \
  77                                            .two = arg->two,            \
  78                                            .three = arg->three,        \
  79                                            .four = arg->four,          \
  80                                        }
  81#define INIT_STRUCT_runtime_partial     ;                               \
  82                                        var.two = 0
  83#define INIT_STRUCT_runtime_all         ;                               \
  84                                        var.one = 0;                    \
  85                                        var.two = 0;                    \
  86                                        var.three = 0;                  \
  87                                        memset(&var.four, 0,            \
  88                                               sizeof(var.four))
  89
  90/*
  91 * @name: unique string name for the test
  92 * @var_type: type to be tested for zeroing initialization
  93 * @which: is this a SCALAR, STRING, or STRUCT type?
  94 * @init_level: what kind of initialization is performed
  95 * @xfail: is this test expected to fail?
  96 */
  97#define DEFINE_TEST_DRIVER(name, var_type, which, xfail)        \
  98/* Returns 0 on success, 1 on failure. */                       \
  99static noinline __init int test_ ## name (void)                 \
 100{                                                               \
 101        var_type zero INIT_CLONE_ ## which;                     \
 102        int ignored;                                            \
 103        u8 sum = 0, i;                                          \
 104                                                                \
 105        /* Notice when a new test is larger than expected. */   \
 106        BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);              \
 107                                                                \
 108        /* Fill clone type with zero for per-field init. */     \
 109        memset(&zero, 0x00, sizeof(zero));                      \
 110        /* Clear entire check buffer for 0xFF overlap test. */  \
 111        memset(check_buf, 0x00, sizeof(check_buf));             \
 112        /* Fill stack with 0xFF. */                             \
 113        ignored = leaf_ ##name((unsigned long)&ignored, 1,      \
 114                                FETCH_ARG_ ## which(zero));     \
 115        /* Verify all bytes overwritten with 0xFF. */           \
 116        for (sum = 0, i = 0; i < target_size; i++)              \
 117                sum += (check_buf[i] != 0xFF);                  \
 118        if (sum) {                                              \
 119                pr_err(#name ": leaf fill was not 0xFF!?\n");   \
 120                return 1;                                       \
 121        }                                                       \
 122        /* Clear entire check buffer for later bit tests. */    \
 123        memset(check_buf, 0x00, sizeof(check_buf));             \
 124        /* Extract stack-defined variable contents. */          \
 125        ignored = leaf_ ##name((unsigned long)&ignored, 0,      \
 126                                FETCH_ARG_ ## which(zero));     \
 127                                                                \
 128        /* Validate that compiler lined up fill and target. */  \
 129        if (!range_contains(fill_start, fill_size,              \
 130                            target_start, target_size)) {       \
 131                pr_err(#name ": stack fill missed target!?\n"); \
 132                pr_err(#name ": fill %zu wide\n", fill_size);   \
 133                pr_err(#name ": target offset by %d\n", \
 134                        (int)((ssize_t)(uintptr_t)fill_start -  \
 135                        (ssize_t)(uintptr_t)target_start));     \
 136                return 1;                                       \
 137        }                                                       \
 138                                                                \
 139        /* Look for any bytes still 0xFF in check region. */    \
 140        for (sum = 0, i = 0; i < target_size; i++)              \
 141                sum += (check_buf[i] == 0xFF);                  \
 142                                                                \
 143        if (sum == 0) {                                         \
 144                pr_info(#name " ok\n");                         \
 145                return 0;                                       \
 146        } else {                                                \
 147                pr_warn(#name " %sFAIL (uninit bytes: %d)\n",   \
 148                        (xfail) ? "X" : "", sum);               \
 149                return (xfail) ? 0 : 1;                         \
 150        }                                                       \
 151}
 152#define DEFINE_TEST(name, var_type, which, init_level)          \
 153/* no-op to force compiler into ignoring "uninitialized" vars */\
 154static noinline __init DO_NOTHING_TYPE_ ## which(var_type)      \
 155do_nothing_ ## name(var_type *ptr)                              \
 156{                                                               \
 157        /* Will always be true, but compiler doesn't know. */   \
 158        if ((unsigned long)ptr > 0x2)                           \
 159                return DO_NOTHING_RETURN_ ## which(ptr);        \
 160        else                                                    \
 161                return DO_NOTHING_RETURN_ ## which(ptr + 1);    \
 162}                                                               \
 163static noinline __init int leaf_ ## name(unsigned long sp,      \
 164                                         bool fill,             \
 165                                         var_type *arg)         \
 166{                                                               \
 167        char buf[VAR_BUFFER];                                   \
 168        var_type var INIT_ ## which ## _ ## init_level;         \
 169                                                                \
 170        target_start = &var;                                    \
 171        target_size = sizeof(var);                              \
 172        /*                                                      \
 173         * Keep this buffer around to make sure we've got a     \
 174         * stack frame of SOME kind...                          \
 175         */                                                     \
 176        memset(buf, (char)(sp & 0xff), sizeof(buf));            \
 177        /* Fill variable with 0xFF. */                          \
 178        if (fill) {                                             \
 179                fill_start = &var;                              \
 180                fill_size = sizeof(var);                        \
 181                memset(fill_start,                              \
 182                       (char)((sp & 0xff) | forced_mask),       \
 183                       fill_size);                              \
 184        }                                                       \
 185                                                                \
 186        /* Silence "never initialized" warnings. */             \
 187        DO_NOTHING_CALL_ ## which(var, name);                   \
 188                                                                \
 189        /* Exfiltrate "var". */                                 \
 190        memcpy(check_buf, target_start, target_size);           \
 191                                                                \
 192        return (int)buf[0] | (int)buf[sizeof(buf) - 1];         \
 193}                                                               \
 194DEFINE_TEST_DRIVER(name, var_type, which, 0)
 195
 196/* Structure with no padding. */
 197struct test_packed {
 198        unsigned long one;
 199        unsigned long two;
 200        unsigned long three;
 201        unsigned long four;
 202};
 203
 204/* Simple structure with padding likely to be covered by compiler. */
 205struct test_small_hole {
 206        size_t one;
 207        char two;
 208        /* 3 byte padding hole here. */
 209        int three;
 210        unsigned long four;
 211};
 212
 213/* Try to trigger unhandled padding in a structure. */
 214struct test_aligned {
 215        u32 internal1;
 216        u64 internal2;
 217} __aligned(64);
 218
 219struct test_big_hole {
 220        u8 one;
 221        u8 two;
 222        u8 three;
 223        /* 61 byte padding hole here. */
 224        struct test_aligned four;
 225} __aligned(64);
 226
 227struct test_trailing_hole {
 228        char *one;
 229        char *two;
 230        char *three;
 231        char four;
 232        /* "sizeof(unsigned long) - 1" byte padding hole here. */
 233};
 234
 235/* Test if STRUCTLEAK is clearing structs with __user fields. */
 236struct test_user {
 237        u8 one;
 238        unsigned long two;
 239        char __user *three;
 240        unsigned long four;
 241};
 242
 243#define DEFINE_SCALAR_TEST(name, init)                          \
 244                DEFINE_TEST(name ## _ ## init, name, SCALAR, init)
 245
 246#define DEFINE_SCALAR_TESTS(init)                               \
 247                DEFINE_SCALAR_TEST(u8, init);                   \
 248                DEFINE_SCALAR_TEST(u16, init);                  \
 249                DEFINE_SCALAR_TEST(u32, init);                  \
 250                DEFINE_SCALAR_TEST(u64, init);                  \
 251                DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init)
 252
 253#define DEFINE_STRUCT_TEST(name, init)                          \
 254                DEFINE_TEST(name ## _ ## init,                  \
 255                            struct test_ ## name, STRUCT, init)
 256
 257#define DEFINE_STRUCT_TESTS(init)                               \
 258                DEFINE_STRUCT_TEST(small_hole, init);           \
 259                DEFINE_STRUCT_TEST(big_hole, init);             \
 260                DEFINE_STRUCT_TEST(trailing_hole, init);        \
 261                DEFINE_STRUCT_TEST(packed, init)
 262
 263/* These should be fully initialized all the time! */
 264DEFINE_SCALAR_TESTS(zero);
 265DEFINE_STRUCT_TESTS(zero);
 266/* Static initialization: padding may be left uninitialized. */
 267DEFINE_STRUCT_TESTS(static_partial);
 268DEFINE_STRUCT_TESTS(static_all);
 269/* Dynamic initialization: padding may be left uninitialized. */
 270DEFINE_STRUCT_TESTS(dynamic_partial);
 271DEFINE_STRUCT_TESTS(dynamic_all);
 272/* Runtime initialization: padding may be left uninitialized. */
 273DEFINE_STRUCT_TESTS(runtime_partial);
 274DEFINE_STRUCT_TESTS(runtime_all);
 275/* No initialization without compiler instrumentation. */
 276DEFINE_SCALAR_TESTS(none);
 277DEFINE_STRUCT_TESTS(none);
 278DEFINE_TEST(user, struct test_user, STRUCT, none);
 279
 280/*
 281 * Check two uses through a variable declaration outside either path,
 282 * which was noticed as a special case in porting earlier stack init
 283 * compiler logic.
 284 */
 285static int noinline __leaf_switch_none(int path, bool fill)
 286{
 287        switch (path) {
 288                uint64_t var;
 289
 290        case 1:
 291                target_start = &var;
 292                target_size = sizeof(var);
 293                if (fill) {
 294                        fill_start = &var;
 295                        fill_size = sizeof(var);
 296
 297                        memset(fill_start, forced_mask | 0x55, fill_size);
 298                }
 299                memcpy(check_buf, target_start, target_size);
 300                break;
 301        case 2:
 302                target_start = &var;
 303                target_size = sizeof(var);
 304                if (fill) {
 305                        fill_start = &var;
 306                        fill_size = sizeof(var);
 307
 308                        memset(fill_start, forced_mask | 0xaa, fill_size);
 309                }
 310                memcpy(check_buf, target_start, target_size);
 311                break;
 312        default:
 313                var = 5;
 314                return var & forced_mask;
 315        }
 316        return 0;
 317}
 318
 319static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
 320                                              uint64_t *arg)
 321{
 322        return __leaf_switch_none(1, fill);
 323}
 324
 325static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
 326                                              uint64_t *arg)
 327{
 328        return __leaf_switch_none(2, fill);
 329}
 330
 331/*
 332 * These are expected to fail for most configurations because neither
 333 * GCC nor Clang have a way to perform initialization of variables in
 334 * non-code areas (i.e. in a switch statement before the first "case").
 335 * https://bugs.llvm.org/show_bug.cgi?id=44916
 336 */
 337DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1);
 338DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1);
 339
 340static int __init test_stackinit_init(void)
 341{
 342        unsigned int failures = 0;
 343
 344#define test_scalars(init)      do {                            \
 345                failures += test_u8_ ## init ();                \
 346                failures += test_u16_ ## init ();               \
 347                failures += test_u32_ ## init ();               \
 348                failures += test_u64_ ## init ();               \
 349                failures += test_char_array_ ## init ();        \
 350        } while (0)
 351
 352#define test_structs(init)      do {                            \
 353                failures += test_small_hole_ ## init ();        \
 354                failures += test_big_hole_ ## init ();          \
 355                failures += test_trailing_hole_ ## init ();     \
 356                failures += test_packed_ ## init ();            \
 357        } while (0)
 358
 359        /* These are explicitly initialized and should always pass. */
 360        test_scalars(zero);
 361        test_structs(zero);
 362        /* Padding here appears to be accidentally always initialized? */
 363        test_structs(dynamic_partial);
 364        /* Padding initialization depends on compiler behaviors. */
 365        test_structs(static_partial);
 366        test_structs(static_all);
 367        test_structs(dynamic_all);
 368        test_structs(runtime_partial);
 369        test_structs(runtime_all);
 370
 371        /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
 372        test_scalars(none);
 373        failures += test_switch_1_none();
 374        failures += test_switch_2_none();
 375
 376        /* STRUCTLEAK_BYREF should cover from here down. */
 377        test_structs(none);
 378
 379        /* STRUCTLEAK will only cover this. */
 380        failures += test_user();
 381
 382        if (failures == 0)
 383                pr_info("all tests passed!\n");
 384        else
 385                pr_err("failures: %u\n", failures);
 386
 387        return failures ? -EINVAL : 0;
 388}
 389module_init(test_stackinit_init);
 390
 391static void __exit test_stackinit_exit(void)
 392{ }
 393module_exit(test_stackinit_exit);
 394
 395MODULE_LICENSE("GPL");
 396