Merge remote-tracking branch 'spi/for-5.14' into spi-next
[linux-block.git] / kernel / kcsan / selftest.c
CommitLineData
dfd402a4 1// SPDX-License-Identifier: GPL-2.0
bd0ccc4a
ME
2/*
3 * KCSAN short boot-time selftests.
4 *
5 * Copyright (C) 2019, Google LLC.
6 */
dfd402a4 7
178a1877
ME
8#define pr_fmt(fmt) "kcsan: " fmt
9
dfd402a4
ME
10#include <linux/init.h>
11#include <linux/kernel.h>
12#include <linux/printk.h>
13#include <linux/random.h>
14#include <linux/types.h>
15
16#include "encoding.h"
17
18#define ITERS_PER_TEST 2000
19
20/* Test requirements. */
21static bool test_requires(void)
22{
23 /* random should be initialized for the below tests */
24 return prandom_u32() + prandom_u32() != 0;
25}
26
27/*
28 * Test watchpoint encode and decode: check that encoding some access's info,
29 * and then subsequent decode preserves the access's info.
30 */
31static bool test_encode_decode(void)
32{
33 int i;
34
35 for (i = 0; i < ITERS_PER_TEST; ++i) {
36 size_t size = prandom_u32_max(MAX_ENCODABLE_SIZE) + 1;
37 bool is_write = !!prandom_u32_max(2);
38 unsigned long addr;
39
40 prandom_bytes(&addr, sizeof(addr));
4761612f
ME
41 if (addr < PAGE_SIZE)
42 addr = PAGE_SIZE;
43
dfd402a4
ME
44 if (WARN_ON(!check_encodable(addr, size)))
45 return false;
46
5cbaefe9 47 /* Encode and decode */
dfd402a4
ME
48 {
49 const long encoded_watchpoint =
50 encode_watchpoint(addr, size, is_write);
51 unsigned long verif_masked_addr;
52 size_t verif_size;
53 bool verif_is_write;
54
5cbaefe9 55 /* Check special watchpoints */
dfd402a4
ME
56 if (WARN_ON(decode_watchpoint(
57 INVALID_WATCHPOINT, &verif_masked_addr,
58 &verif_size, &verif_is_write)))
59 return false;
60 if (WARN_ON(decode_watchpoint(
61 CONSUMED_WATCHPOINT, &verif_masked_addr,
62 &verif_size, &verif_is_write)))
63 return false;
64
5cbaefe9 65 /* Check decoding watchpoint returns same data */
dfd402a4
ME
66 if (WARN_ON(!decode_watchpoint(
67 encoded_watchpoint, &verif_masked_addr,
68 &verif_size, &verif_is_write)))
69 return false;
70 if (WARN_ON(verif_masked_addr !=
71 (addr & WATCHPOINT_ADDR_MASK)))
72 goto fail;
73 if (WARN_ON(verif_size != size))
74 goto fail;
75 if (WARN_ON(is_write != verif_is_write))
76 goto fail;
77
78 continue;
79fail:
80 pr_err("%s fail: %s %zu bytes @ %lx -> encoded: %lx -> %s %zu bytes @ %lx\n",
81 __func__, is_write ? "write" : "read", size,
82 addr, encoded_watchpoint,
83 verif_is_write ? "write" : "read", verif_size,
84 verif_masked_addr);
85 return false;
86 }
87 }
88
89 return true;
90}
91
92/* Test access matching function. */
93static bool test_matching_access(void)
94{
95 if (WARN_ON(!matching_access(10, 1, 10, 1)))
96 return false;
97 if (WARN_ON(!matching_access(10, 2, 11, 1)))
98 return false;
99 if (WARN_ON(!matching_access(10, 1, 9, 2)))
100 return false;
101 if (WARN_ON(matching_access(10, 1, 11, 1)))
102 return false;
103 if (WARN_ON(matching_access(9, 1, 10, 1)))
104 return false;
ed95f95c
ME
105
106 /*
107 * An access of size 0 could match another access, as demonstrated here.
108 * Rather than add more comparisons to 'matching_access()', which would
109 * end up in the fast-path for *all* checks, check_access() simply
110 * returns for all accesses of size 0.
111 */
112 if (WARN_ON(!matching_access(8, 8, 12, 0)))
113 return false;
114
dfd402a4
ME
115 return true;
116}
117
118static int __init kcsan_selftest(void)
119{
120 int passed = 0;
121 int total = 0;
122
123#define RUN_TEST(do_test) \
124 do { \
125 ++total; \
126 if (do_test()) \
127 ++passed; \
128 else \
178a1877 129 pr_err("selftest: " #do_test " failed"); \
dfd402a4
ME
130 } while (0)
131
132 RUN_TEST(test_requires);
133 RUN_TEST(test_encode_decode);
134 RUN_TEST(test_matching_access);
135
178a1877 136 pr_info("selftest: %d/%d tests passed\n", passed, total);
dfd402a4 137 if (passed != total)
178a1877 138 panic("selftests failed");
dfd402a4
ME
139 return 0;
140}
141postcore_initcall(kcsan_selftest);