linux/lib/test_stackinit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Test cases for compiler-based stack variable zeroing via
   4 * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
   5 *
   6 * External build example:
   7 *      clang -O2 -Wall -ftrivial-auto-var-init=pattern \
   8 *              -o test_stackinit test_stackinit.c
   9 */
  10#ifdef __KERNEL__
  11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  12
  13#include <linux/init.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/string.h>
  17
  18#else
  19
  20/* Userspace headers. */
  21#include <stdio.h>
  22#include <stdint.h>
  23#include <string.h>
  24#include <stdbool.h>
  25#include <errno.h>
  26#include <sys/types.h>
  27
  28/* Linux kernel-ism stubs for stand-alone userspace build. */
  29#define KBUILD_MODNAME          "stackinit"
  30#define pr_fmt(fmt)             KBUILD_MODNAME ": " fmt
  31#define pr_err(fmt, ...)        fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
  32#define pr_warn(fmt, ...)       fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
  33#define pr_info(fmt, ...)       fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__)
  34#define __init                  /**/
  35#define __exit                  /**/
  36#define __user                  /**/
  37#define noinline                __attribute__((__noinline__))
  38#define __aligned(x)            __attribute__((__aligned__(x)))
  39#ifdef __clang__
  40# define __compiletime_error(message) /**/
  41#else
  42# define __compiletime_error(message) __attribute__((__error__(message)))
  43#endif
  44#define __compiletime_assert(condition, msg, prefix, suffix)            \
  45        do {                                                            \
  46                extern void prefix ## suffix(void) __compiletime_error(msg); \
  47                if (!(condition))                                       \
  48                        prefix ## suffix();                             \
  49        } while (0)
  50#define _compiletime_assert(condition, msg, prefix, suffix) \
  51        __compiletime_assert(condition, msg, prefix, suffix)
  52#define compiletime_assert(condition, msg) \
  53        _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
  54#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
  55#define BUILD_BUG_ON(condition) \
  56        BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
  57typedef uint8_t                 u8;
  58typedef uint16_t                u16;
  59typedef uint32_t                u32;
  60typedef uint64_t                u64;
  61
  62#define module_init(func)       static int (*do_init)(void) = func
  63#define module_exit(func)       static void (*do_exit)(void) = func
  64#define MODULE_LICENSE(str)     int main(void) {                \
  65                                        int rc;                 \
  66                                        /* License: str */      \
  67                                        rc = do_init();         \
  68                                        if (rc == 0)            \
  69                                                do_exit();      \
  70                                        return rc;              \
  71                                }
  72
  73#endif /* __KERNEL__ */
  74
  75/* Exfiltration buffer. */
  76#define MAX_VAR_SIZE    128
  77static u8 check_buf[MAX_VAR_SIZE];
  78
  79/* Character array to trigger stack protector in all functions. */
  80#define VAR_BUFFER       32
  81
  82/* Volatile mask to convince compiler to copy memory with 0xff. */
  83static volatile u8 forced_mask = 0xff;
  84
  85/* Location and size tracking to validate fill and test are colocated. */
  86static void *fill_start, *target_start;
  87static size_t fill_size, target_size;
  88
  89static bool range_contains(char *haystack_start, size_t haystack_size,
  90                           char *needle_start, size_t needle_size)
  91{
  92        if (needle_start >= haystack_start &&
  93            needle_start + needle_size <= haystack_start + haystack_size)
  94                return true;
  95        return false;
  96}
  97
  98/* Whether the test is expected to fail. */
  99#define WANT_SUCCESS                            0
 100#define XFAIL                                   1
 101
 102#define DO_NOTHING_TYPE_SCALAR(var_type)        var_type
 103#define DO_NOTHING_TYPE_STRING(var_type)        void
 104#define DO_NOTHING_TYPE_STRUCT(var_type)        void
 105
 106#define DO_NOTHING_RETURN_SCALAR(ptr)           *(ptr)
 107#define DO_NOTHING_RETURN_STRING(ptr)           /**/
 108#define DO_NOTHING_RETURN_STRUCT(ptr)           /**/
 109
 110#define DO_NOTHING_CALL_SCALAR(var, name)                       \
 111                (var) = do_nothing_ ## name(&(var))
 112#define DO_NOTHING_CALL_STRING(var, name)                       \
 113                do_nothing_ ## name(var)
 114#define DO_NOTHING_CALL_STRUCT(var, name)                       \
 115                do_nothing_ ## name(&(var))
 116
 117#define FETCH_ARG_SCALAR(var)           &var
 118#define FETCH_ARG_STRING(var)           var
 119#define FETCH_ARG_STRUCT(var)           &var
 120
 121#define FILL_SIZE_STRING                16
 122
 123#define INIT_CLONE_SCALAR               /**/
 124#define INIT_CLONE_STRING               [FILL_SIZE_STRING]
 125#define INIT_CLONE_STRUCT               /**/
 126
 127#define ZERO_CLONE_SCALAR(zero)         memset(&(zero), 0x00, sizeof(zero))
 128#define ZERO_CLONE_STRING(zero)         memset(&(zero), 0x00, sizeof(zero))
 129/*
 130 * For the struct, intentionally poison padding to see if it gets
 131 * copied out in direct assignments.
 132 * */
 133#define ZERO_CLONE_STRUCT(zero)                         \
 134        do {                                            \
 135                memset(&(zero), 0xFF, sizeof(zero));    \
 136                zero.one = 0;                           \
 137                zero.two = 0;                           \
 138                zero.three = 0;                         \
 139                zero.four = 0;                          \
 140        } while (0)
 141
 142#define INIT_SCALAR_none(var_type)      /**/
 143#define INIT_SCALAR_zero(var_type)      = 0
 144
 145#define INIT_STRING_none(var_type)      [FILL_SIZE_STRING] /**/
 146#define INIT_STRING_zero(var_type)      [FILL_SIZE_STRING] = { }
 147
 148#define INIT_STRUCT_none(var_type)      /**/
 149#define INIT_STRUCT_zero(var_type)      = { }
 150
 151
 152#define __static_partial                { .two = 0, }
 153#define __static_all                    { .one = 0,                     \
 154                                          .two = 0,                     \
 155                                          .three = 0,                   \
 156                                          .four = 0,                    \
 157                                        }
 158#define __dynamic_partial               { .two = arg->two, }
 159#define __dynamic_all                   { .one = arg->one,              \
 160                                          .two = arg->two,              \
 161                                          .three = arg->three,          \
 162                                          .four = arg->four,            \
 163                                        }
 164#define __runtime_partial               var.two = 0
 165#define __runtime_all                   var.one = 0;                    \
 166                                        var.two = 0;                    \
 167                                        var.three = 0;                  \
 168                                        var.four = 0
 169
 170#define INIT_STRUCT_static_partial(var_type)                            \
 171                                        = __static_partial
 172#define INIT_STRUCT_static_all(var_type)                                \
 173                                        = __static_all
 174#define INIT_STRUCT_dynamic_partial(var_type)                           \
 175                                        = __dynamic_partial
 176#define INIT_STRUCT_dynamic_all(var_type)                               \
 177                                        = __dynamic_all
 178#define INIT_STRUCT_runtime_partial(var_type)                           \
 179                                        ; __runtime_partial
 180#define INIT_STRUCT_runtime_all(var_type)                               \
 181                                        ; __runtime_all
 182
 183#define INIT_STRUCT_assigned_static_partial(var_type)                   \
 184                                        ; var = (var_type)__static_partial
 185#define INIT_STRUCT_assigned_static_all(var_type)                       \
 186                                        ; var = (var_type)__static_all
 187#define INIT_STRUCT_assigned_dynamic_partial(var_type)                  \
 188                                        ; var = (var_type)__dynamic_partial
 189#define INIT_STRUCT_assigned_dynamic_all(var_type)                      \
 190                                        ; var = (var_type)__dynamic_all
 191
 192#define INIT_STRUCT_assigned_copy(var_type)                             \
 193                                        ; var = *(arg)
 194
 195/*
 196 * @name: unique string name for the test
 197 * @var_type: type to be tested for zeroing initialization
 198 * @which: is this a SCALAR, STRING, or STRUCT type?
 199 * @init_level: what kind of initialization is performed
 200 * @xfail: is this test expected to fail?
 201 */
 202#define DEFINE_TEST_DRIVER(name, var_type, which, xfail)        \
 203/* Returns 0 on success, 1 on failure. */                       \
 204static noinline __init int test_ ## name (void)                 \
 205{                                                               \
 206        var_type zero INIT_CLONE_ ## which;                     \
 207        int ignored;                                            \
 208        u8 sum = 0, i;                                          \
 209                                                                \
 210        /* Notice when a new test is larger than expected. */   \
 211        BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);              \
 212                                                                \
 213        /* Fill clone type with zero for per-field init. */     \
 214        ZERO_CLONE_ ## which(zero);                             \
 215        /* Clear entire check buffer for 0xFF overlap test. */  \
 216        memset(check_buf, 0x00, sizeof(check_buf));             \
 217        /* Fill stack with 0xFF. */                             \
 218        ignored = leaf_ ##name((unsigned long)&ignored, 1,      \
 219                                FETCH_ARG_ ## which(zero));     \
 220        /* Verify all bytes overwritten with 0xFF. */           \
 221        for (sum = 0, i = 0; i < target_size; i++)              \
 222                sum += (check_buf[i] != 0xFF);                  \
 223        if (sum) {                                              \
 224                pr_err(#name ": leaf fill was not 0xFF!?\n");   \
 225                return 1;                                       \
 226        }                                                       \
 227        /* Clear entire check buffer for later bit tests. */    \
 228        memset(check_buf, 0x00, sizeof(check_buf));             \
 229        /* Extract stack-defined variable contents. */          \
 230        ignored = leaf_ ##name((unsigned long)&ignored, 0,      \
 231                                FETCH_ARG_ ## which(zero));     \
 232                                                                \
 233        /* Validate that compiler lined up fill and target. */  \
 234        if (!range_contains(fill_start, fill_size,              \
 235                            target_start, target_size)) {       \
 236                pr_err(#name ": stack fill missed target!?\n"); \
 237                pr_err(#name ": fill %zu wide\n", fill_size);   \
 238                pr_err(#name ": target offset by %d\n", \
 239                        (int)((ssize_t)(uintptr_t)fill_start -  \
 240                        (ssize_t)(uintptr_t)target_start));     \
 241                return 1;                                       \
 242        }                                                       \
 243                                                                \
 244        /* Look for any bytes still 0xFF in check region. */    \
 245        for (sum = 0, i = 0; i < target_size; i++)              \
 246                sum += (check_buf[i] == 0xFF);                  \
 247                                                                \
 248        if (sum == 0) {                                         \
 249                pr_info(#name " ok\n");                         \
 250                return 0;                                       \
 251        } else {                                                \
 252                pr_warn(#name " %sFAIL (uninit bytes: %d)\n",   \
 253                        (xfail) ? "X" : "", sum);               \
 254                return (xfail) ? 0 : 1;                         \
 255        }                                                       \
 256}
 257#define DEFINE_TEST(name, var_type, which, init_level, xfail)   \
 258/* no-op to force compiler into ignoring "uninitialized" vars */\
 259static noinline __init DO_NOTHING_TYPE_ ## which(var_type)      \
 260do_nothing_ ## name(var_type *ptr)                              \
 261{                                                               \
 262        /* Will always be true, but compiler doesn't know. */   \
 263        if ((unsigned long)ptr > 0x2)                           \
 264                return DO_NOTHING_RETURN_ ## which(ptr);        \
 265        else                                                    \
 266                return DO_NOTHING_RETURN_ ## which(ptr + 1);    \
 267}                                                               \
 268static noinline __init int leaf_ ## name(unsigned long sp,      \
 269                                         bool fill,             \
 270                                         var_type *arg)         \
 271{                                                               \
 272        char buf[VAR_BUFFER];                                   \
 273        var_type var                                            \
 274                INIT_ ## which ## _ ## init_level(var_type);    \
 275                                                                \
 276        target_start = &var;                                    \
 277        target_size = sizeof(var);                              \
 278        /*                                                      \
 279         * Keep this buffer around to make sure we've got a     \
 280         * stack frame of SOME kind...                          \
 281         */                                                     \
 282        memset(buf, (char)(sp & 0xff), sizeof(buf));            \
 283        /* Fill variable with 0xFF. */                          \
 284        if (fill) {                                             \
 285                fill_start = &var;                              \
 286                fill_size = sizeof(var);                        \
 287                memset(fill_start,                              \
 288                       (char)((sp & 0xff) | forced_mask),       \
 289                       fill_size);                              \
 290        }                                                       \
 291                                                                \
 292        /* Silence "never initialized" warnings. */             \
 293        DO_NOTHING_CALL_ ## which(var, name);                   \
 294                                                                \
 295        /* Exfiltrate "var". */                                 \
 296        memcpy(check_buf, target_start, target_size);           \
 297                                                                \
 298        return (int)buf[0] | (int)buf[sizeof(buf) - 1];         \
 299}                                                               \
 300DEFINE_TEST_DRIVER(name, var_type, which, xfail)
 301
 302/* Structure with no padding. */
 303struct test_packed {
 304        unsigned long one;
 305        unsigned long two;
 306        unsigned long three;
 307        unsigned long four;
 308};
 309
 310/* Simple structure with padding likely to be covered by compiler. */
 311struct test_small_hole {
 312        size_t one;
 313        char two;
 314        /* 3 byte padding hole here. */
 315        int three;
 316        unsigned long four;
 317};
 318
 319/* Trigger unhandled padding in a structure. */
 320struct test_big_hole {
 321        u8 one;
 322        u8 two;
 323        u8 three;
 324        /* 61 byte padding hole here. */
 325        u8 four __aligned(64);
 326} __aligned(64);
 327
 328struct test_trailing_hole {
 329        char *one;
 330        char *two;
 331        char *three;
 332        char four;
 333        /* "sizeof(unsigned long) - 1" byte padding hole here. */
 334};
 335
 336/* Test if STRUCTLEAK is clearing structs with __user fields. */
 337struct test_user {
 338        u8 one;
 339        unsigned long two;
 340        char __user *three;
 341        unsigned long four;
 342};
 343
 344#define DEFINE_SCALAR_TEST(name, init, xfail)                   \
 345                DEFINE_TEST(name ## _ ## init, name, SCALAR,    \
 346                            init, xfail)
 347
 348#define DEFINE_SCALAR_TESTS(init, xfail)                        \
 349                DEFINE_SCALAR_TEST(u8, init, xfail);            \
 350                DEFINE_SCALAR_TEST(u16, init, xfail);           \
 351                DEFINE_SCALAR_TEST(u32, init, xfail);           \
 352                DEFINE_SCALAR_TEST(u64, init, xfail);           \
 353                DEFINE_TEST(char_array_ ## init, unsigned char, \
 354                            STRING, init, xfail)
 355
 356#define DEFINE_STRUCT_TEST(name, init, xfail)                   \
 357                DEFINE_TEST(name ## _ ## init,                  \
 358                            struct test_ ## name, STRUCT, init, \
 359                            xfail)
 360
 361#define DEFINE_STRUCT_TESTS(init, xfail)                        \
 362                DEFINE_STRUCT_TEST(small_hole, init, xfail);    \
 363                DEFINE_STRUCT_TEST(big_hole, init, xfail);      \
 364                DEFINE_STRUCT_TEST(trailing_hole, init, xfail); \
 365                DEFINE_STRUCT_TEST(packed, init, xfail)
 366
 367#define DEFINE_STRUCT_INITIALIZER_TESTS(base)                   \
 368                DEFINE_STRUCT_TESTS(base ## _ ## partial,       \
 369                                    WANT_SUCCESS);              \
 370                DEFINE_STRUCT_TESTS(base ## _ ## all,           \
 371                                    WANT_SUCCESS)
 372
 373/* These should be fully initialized all the time! */
 374DEFINE_SCALAR_TESTS(zero, WANT_SUCCESS);
 375DEFINE_STRUCT_TESTS(zero, WANT_SUCCESS);
 376/* Struct initializers: padding may be left uninitialized. */
 377DEFINE_STRUCT_INITIALIZER_TESTS(static);
 378DEFINE_STRUCT_INITIALIZER_TESTS(dynamic);
 379DEFINE_STRUCT_INITIALIZER_TESTS(runtime);
 380DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static);
 381DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic);
 382DEFINE_STRUCT_TESTS(assigned_copy, XFAIL);
 383/* No initialization without compiler instrumentation. */
 384DEFINE_SCALAR_TESTS(none, WANT_SUCCESS);
 385DEFINE_STRUCT_TESTS(none, WANT_SUCCESS);
 386/* Initialization of members with __user attribute. */
 387DEFINE_TEST(user, struct test_user, STRUCT, none, WANT_SUCCESS);
 388
 389/*
 390 * Check two uses through a variable declaration outside either path,
 391 * which was noticed as a special case in porting earlier stack init
 392 * compiler logic.
 393 */
 394static int noinline __leaf_switch_none(int path, bool fill)
 395{
 396        switch (path) {
 397                /*
 398                 * This is intentionally unreachable. To silence the
 399                 * warning, build with -Wno-switch-unreachable
 400                 */
 401                uint64_t var;
 402
 403        case 1:
 404                target_start = &var;
 405                target_size = sizeof(var);
 406                if (fill) {
 407                        fill_start = &var;
 408                        fill_size = sizeof(var);
 409
 410                        memset(fill_start, forced_mask | 0x55, fill_size);
 411                }
 412                memcpy(check_buf, target_start, target_size);
 413                break;
 414        case 2:
 415                target_start = &var;
 416                target_size = sizeof(var);
 417                if (fill) {
 418                        fill_start = &var;
 419                        fill_size = sizeof(var);
 420
 421                        memset(fill_start, forced_mask | 0xaa, fill_size);
 422                }
 423                memcpy(check_buf, target_start, target_size);
 424                break;
 425        default:
 426                var = 5;
 427                return var & forced_mask;
 428        }
 429        return 0;
 430}
 431
 432static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
 433                                              uint64_t *arg)
 434{
 435        return __leaf_switch_none(1, fill);
 436}
 437
 438static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
 439                                              uint64_t *arg)
 440{
 441        return __leaf_switch_none(2, fill);
 442}
 443
 444/*
 445 * These are expected to fail for most configurations because neither
 446 * GCC nor Clang have a way to perform initialization of variables in
 447 * non-code areas (i.e. in a switch statement before the first "case").
 448 * https://bugs.llvm.org/show_bug.cgi?id=44916
 449 */
 450DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, XFAIL);
 451DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, XFAIL);
 452
 453static int __init test_stackinit_init(void)
 454{
 455        unsigned int failures = 0;
 456
 457#define test_scalars(init)      do {                            \
 458                failures += test_u8_ ## init ();                \
 459                failures += test_u16_ ## init ();               \
 460                failures += test_u32_ ## init ();               \
 461                failures += test_u64_ ## init ();               \
 462                failures += test_char_array_ ## init ();        \
 463        } while (0)
 464
 465#define test_structs(init)      do {                            \
 466                failures += test_small_hole_ ## init ();        \
 467                failures += test_big_hole_ ## init ();          \
 468                failures += test_trailing_hole_ ## init ();     \
 469                failures += test_packed_ ## init ();            \
 470        } while (0)
 471
 472        /* These are explicitly initialized and should always pass. */
 473        test_scalars(zero);
 474        test_structs(zero);
 475        /* Padding here appears to be accidentally always initialized? */
 476        test_structs(dynamic_partial);
 477        test_structs(assigned_dynamic_partial);
 478        /* Padding initialization depends on compiler behaviors. */
 479        test_structs(static_partial);
 480        test_structs(static_all);
 481        test_structs(dynamic_all);
 482        test_structs(runtime_partial);
 483        test_structs(runtime_all);
 484        test_structs(assigned_static_partial);
 485        test_structs(assigned_static_all);
 486        test_structs(assigned_dynamic_all);
 487        /* Everything fails this since it effectively performs a memcpy(). */
 488        test_structs(assigned_copy);
 489
 490        /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
 491        test_scalars(none);
 492        failures += test_switch_1_none();
 493        failures += test_switch_2_none();
 494
 495        /* STRUCTLEAK_BYREF should cover from here down. */
 496        test_structs(none);
 497
 498        /* STRUCTLEAK will only cover this. */
 499        failures += test_user();
 500
 501        if (failures == 0)
 502                pr_info("all tests passed!\n");
 503        else
 504                pr_err("failures: %u\n", failures);
 505
 506        return failures ? -EINVAL : 0;
 507}
 508module_init(test_stackinit_init);
 509
 510static void __exit test_stackinit_exit(void)
 511{ }
 512module_exit(test_stackinit_exit);
 513
 514MODULE_LICENSE("GPL");
 515