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 */
  96#define DEFINE_TEST_DRIVER(name, var_type, which)               \
  97/* Returns 0 on success, 1 on failure. */                       \
  98static noinline __init int test_ ## name (void)                 \
  99{                                                               \
 100        var_type zero INIT_CLONE_ ## which;                     \
 101        int ignored;                                            \
 102        u8 sum = 0, i;                                          \
 103                                                                \
 104        /* Notice when a new test is larger than expected. */   \
 105        BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);              \
 106                                                                \
 107        /* Fill clone type with zero for per-field init. */     \
 108        memset(&zero, 0x00, sizeof(zero));                      \
 109        /* Clear entire check buffer for 0xFF overlap test. */  \
 110        memset(check_buf, 0x00, sizeof(check_buf));             \
 111        /* Fill stack with 0xFF. */                             \
 112        ignored = leaf_ ##name((unsigned long)&ignored, 1,      \
 113                                FETCH_ARG_ ## which(zero));     \
 114        /* Verify all bytes overwritten with 0xFF. */           \
 115        for (sum = 0, i = 0; i < target_size; i++)              \
 116                sum += (check_buf[i] != 0xFF);                  \
 117        if (sum) {                                              \
 118                pr_err(#name ": leaf fill was not 0xFF!?\n");   \
 119                return 1;                                       \
 120        }                                                       \
 121        /* Clear entire check buffer for later bit tests. */    \
 122        memset(check_buf, 0x00, sizeof(check_buf));             \
 123        /* Extract stack-defined variable contents. */          \
 124        ignored = leaf_ ##name((unsigned long)&ignored, 0,      \
 125                                FETCH_ARG_ ## which(zero));     \
 126                                                                \
 127        /* Validate that compiler lined up fill and target. */  \
 128        if (!range_contains(fill_start, fill_size,              \
 129                            target_start, target_size)) {       \
 130                pr_err(#name ": stack fill missed target!?\n"); \
 131                pr_err(#name ": fill %zu wide\n", fill_size);   \
 132                pr_err(#name ": target offset by %d\n", \
 133                        (int)((ssize_t)(uintptr_t)fill_start -  \
 134                        (ssize_t)(uintptr_t)target_start));     \
 135                return 1;                                       \
 136        }                                                       \
 137                                                                \
 138        /* Look for any bytes still 0xFF in check region. */    \
 139        for (sum = 0, i = 0; i < target_size; i++)              \
 140                sum += (check_buf[i] == 0xFF);                  \
 141                                                                \
 142        if (sum == 0)                                           \
 143                pr_info(#name " ok\n");                         \
 144        else                                                    \
 145                pr_warn(#name " FAIL (uninit bytes: %d)\n",     \
 146                        sum);                                   \
 147                                                                \
 148        return (sum != 0);                                      \
 149}
 150#define DEFINE_TEST(name, var_type, which, init_level)          \
 151/* no-op to force compiler into ignoring "uninitialized" vars */\
 152static noinline __init DO_NOTHING_TYPE_ ## which(var_type)      \
 153do_nothing_ ## name(var_type *ptr)                              \
 154{                                                               \
 155        /* Will always be true, but compiler doesn't know. */   \
 156        if ((unsigned long)ptr > 0x2)                           \
 157                return DO_NOTHING_RETURN_ ## which(ptr);        \
 158        else                                                    \
 159                return DO_NOTHING_RETURN_ ## which(ptr + 1);    \
 160}                                                               \
 161static noinline __init int leaf_ ## name(unsigned long sp,      \
 162                                         bool fill,             \
 163                                         var_type *arg)         \
 164{                                                               \
 165        char buf[VAR_BUFFER];                                   \
 166        var_type var INIT_ ## which ## _ ## init_level;         \
 167                                                                \
 168        target_start = &var;                                    \
 169        target_size = sizeof(var);                              \
 170        /*                                                      \
 171         * Keep this buffer around to make sure we've got a     \
 172         * stack frame of SOME kind...                          \
 173         */                                                     \
 174        memset(buf, (char)(sp & 0xff), sizeof(buf));            \
 175        /* Fill variable with 0xFF. */                          \
 176        if (fill) {                                             \
 177                fill_start = &var;                              \
 178                fill_size = sizeof(var);                        \
 179                memset(fill_start,                              \
 180                       (char)((sp & 0xff) | forced_mask),       \
 181                       fill_size);                              \
 182        }                                                       \
 183                                                                \
 184        /* Silence "never initialized" warnings. */             \
 185        DO_NOTHING_CALL_ ## which(var, name);                   \
 186                                                                \
 187        /* Exfiltrate "var". */                                 \
 188        memcpy(check_buf, target_start, target_size);           \
 189                                                                \
 190        return (int)buf[0] | (int)buf[sizeof(buf) - 1];         \
 191}                                                               \
 192DEFINE_TEST_DRIVER(name, var_type, which)
 193
 194/* Structure with no padding. */
 195struct test_packed {
 196        unsigned long one;
 197        unsigned long two;
 198        unsigned long three;
 199        unsigned long four;
 200};
 201
 202/* Simple structure with padding likely to be covered by compiler. */
 203struct test_small_hole {
 204        size_t one;
 205        char two;
 206        /* 3 byte padding hole here. */
 207        int three;
 208        unsigned long four;
 209};
 210
 211/* Try to trigger unhandled padding in a structure. */
 212struct test_aligned {
 213        u32 internal1;
 214        u64 internal2;
 215} __aligned(64);
 216
 217struct test_big_hole {
 218        u8 one;
 219        u8 two;
 220        u8 three;
 221        /* 61 byte padding hole here. */
 222        struct test_aligned four;
 223} __aligned(64);
 224
 225struct test_trailing_hole {
 226        char *one;
 227        char *two;
 228        char *three;
 229        char four;
 230        /* "sizeof(unsigned long) - 1" byte padding hole here. */
 231};
 232
 233/* Test if STRUCTLEAK is clearing structs with __user fields. */
 234struct test_user {
 235        u8 one;
 236        unsigned long two;
 237        char __user *three;
 238        unsigned long four;
 239};
 240
 241#define DEFINE_SCALAR_TEST(name, init)                          \
 242                DEFINE_TEST(name ## _ ## init, name, SCALAR, init)
 243
 244#define DEFINE_SCALAR_TESTS(init)                               \
 245                DEFINE_SCALAR_TEST(u8, init);                   \
 246                DEFINE_SCALAR_TEST(u16, init);                  \
 247                DEFINE_SCALAR_TEST(u32, init);                  \
 248                DEFINE_SCALAR_TEST(u64, init);                  \
 249                DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init)
 250
 251#define DEFINE_STRUCT_TEST(name, init)                          \
 252                DEFINE_TEST(name ## _ ## init,                  \
 253                            struct test_ ## name, STRUCT, init)
 254
 255#define DEFINE_STRUCT_TESTS(init)                               \
 256                DEFINE_STRUCT_TEST(small_hole, init);           \
 257                DEFINE_STRUCT_TEST(big_hole, init);             \
 258                DEFINE_STRUCT_TEST(trailing_hole, init);        \
 259                DEFINE_STRUCT_TEST(packed, init)
 260
 261/* These should be fully initialized all the time! */
 262DEFINE_SCALAR_TESTS(zero);
 263DEFINE_STRUCT_TESTS(zero);
 264/* Static initialization: padding may be left uninitialized. */
 265DEFINE_STRUCT_TESTS(static_partial);
 266DEFINE_STRUCT_TESTS(static_all);
 267/* Dynamic initialization: padding may be left uninitialized. */
 268DEFINE_STRUCT_TESTS(dynamic_partial);
 269DEFINE_STRUCT_TESTS(dynamic_all);
 270/* Runtime initialization: padding may be left uninitialized. */
 271DEFINE_STRUCT_TESTS(runtime_partial);
 272DEFINE_STRUCT_TESTS(runtime_all);
 273/* No initialization without compiler instrumentation. */
 274DEFINE_SCALAR_TESTS(none);
 275DEFINE_STRUCT_TESTS(none);
 276DEFINE_TEST(user, struct test_user, STRUCT, none);
 277
 278/*
 279 * Check two uses through a variable declaration outside either path,
 280 * which was noticed as a special case in porting earlier stack init
 281 * compiler logic.
 282 */
 283static int noinline __leaf_switch_none(int path, bool fill)
 284{
 285        switch (path) {
 286                uint64_t var;
 287
 288        case 1:
 289                target_start = &var;
 290                target_size = sizeof(var);
 291                if (fill) {
 292                        fill_start = &var;
 293                        fill_size = sizeof(var);
 294
 295                        memset(fill_start, forced_mask | 0x55, fill_size);
 296                }
 297                memcpy(check_buf, target_start, target_size);
 298                break;
 299        case 2:
 300                target_start = &var;
 301                target_size = sizeof(var);
 302                if (fill) {
 303                        fill_start = &var;
 304                        fill_size = sizeof(var);
 305
 306                        memset(fill_start, forced_mask | 0xaa, fill_size);
 307                }
 308                memcpy(check_buf, target_start, target_size);
 309                break;
 310        default:
 311                var = 5;
 312                return var & forced_mask;
 313        }
 314        return 0;
 315}
 316
 317static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
 318                                              uint64_t *arg)
 319{
 320        return __leaf_switch_none(1, fill);
 321}
 322
 323static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
 324                                              uint64_t *arg)
 325{
 326        return __leaf_switch_none(2, fill);
 327}
 328
 329DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR);
 330DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR);
 331
 332static int __init test_stackinit_init(void)
 333{
 334        unsigned int failures = 0;
 335
 336#define test_scalars(init)      do {                            \
 337                failures += test_u8_ ## init ();                \
 338                failures += test_u16_ ## init ();               \
 339                failures += test_u32_ ## init ();               \
 340                failures += test_u64_ ## init ();               \
 341                failures += test_char_array_ ## init ();        \
 342        } while (0)
 343
 344#define test_structs(init)      do {                            \
 345                failures += test_small_hole_ ## init ();        \
 346                failures += test_big_hole_ ## init ();          \
 347                failures += test_trailing_hole_ ## init ();     \
 348                failures += test_packed_ ## init ();            \
 349        } while (0)
 350
 351        /* These are explicitly initialized and should always pass. */
 352        test_scalars(zero);
 353        test_structs(zero);
 354        /* Padding here appears to be accidentally always initialized? */
 355        test_structs(dynamic_partial);
 356        /* Padding initialization depends on compiler behaviors. */
 357        test_structs(static_partial);
 358        test_structs(static_all);
 359        test_structs(dynamic_all);
 360        test_structs(runtime_partial);
 361        test_structs(runtime_all);
 362
 363        /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
 364        test_scalars(none);
 365        failures += test_switch_1_none();
 366        failures += test_switch_2_none();
 367
 368        /* STRUCTLEAK_BYREF should cover from here down. */
 369        test_structs(none);
 370
 371        /* STRUCTLEAK will only cover this. */
 372        failures += test_user();
 373
 374        if (failures == 0)
 375                pr_info("all tests passed!\n");
 376        else
 377                pr_err("failures: %u\n", failures);
 378
 379        return failures ? -EINVAL : 0;
 380}
 381module_init(test_stackinit_init);
 382
 383static void __exit test_stackinit_exit(void)
 384{ }
 385module_exit(test_stackinit_exit);
 386
 387MODULE_LICENSE("GPL");
 388