Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/misc/lkdtm/bugs.c: add arithmetic overflow and array bounds checks

Adds LKDTM tests for arithmetic overflow (both signed and unsigned), as
well as array bounds checking.

Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Elena Petrova <lenaptr@google.com>
Cc: "Gustavo A. R. Silva" <gustavo@embeddedor.com>
Link: http://lkml.kernel.org/r/20200227193516.32566-4-keescook@chromium.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Kees Cook and committed by
Linus Torvalds
ae2e1aad 277a1085

+81
+75
drivers/misc/lkdtm/bugs.c
··· 11 11 #include <linux/sched/signal.h> 12 12 #include <linux/sched/task_stack.h> 13 13 #include <linux/uaccess.h> 14 + #include <linux/slab.h> 14 15 15 16 #ifdef CONFIG_X86_32 16 17 #include <asm/desc.h> ··· 174 173 { 175 174 set_current_state(TASK_UNINTERRUPTIBLE); 176 175 schedule(); 176 + } 177 + 178 + volatile unsigned int huge = INT_MAX - 2; 179 + volatile unsigned int ignored; 180 + 181 + void lkdtm_OVERFLOW_SIGNED(void) 182 + { 183 + int value; 184 + 185 + value = huge; 186 + pr_info("Normal signed addition ...\n"); 187 + value += 1; 188 + ignored = value; 189 + 190 + pr_info("Overflowing signed addition ...\n"); 191 + value += 4; 192 + ignored = value; 193 + } 194 + 195 + 196 + void lkdtm_OVERFLOW_UNSIGNED(void) 197 + { 198 + unsigned int value; 199 + 200 + value = huge; 201 + pr_info("Normal unsigned addition ...\n"); 202 + value += 1; 203 + ignored = value; 204 + 205 + pr_info("Overflowing unsigned addition ...\n"); 206 + value += 4; 207 + ignored = value; 208 + } 209 + 210 + /* Intentially using old-style flex array definition of 1 byte. */ 211 + struct array_bounds_flex_array { 212 + int one; 213 + int two; 214 + char data[1]; 215 + }; 216 + 217 + struct array_bounds { 218 + int one; 219 + int two; 220 + char data[8]; 221 + int three; 222 + }; 223 + 224 + void lkdtm_ARRAY_BOUNDS(void) 225 + { 226 + struct array_bounds_flex_array *not_checked; 227 + struct array_bounds *checked; 228 + volatile int i; 229 + 230 + not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL); 231 + checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL); 232 + 233 + pr_info("Array access within bounds ...\n"); 234 + /* For both, touch all bytes in the actual member size. */ 235 + for (i = 0; i < sizeof(checked->data); i++) 236 + checked->data[i] = 'A'; 237 + /* 238 + * For the uninstrumented flex array member, also touch 1 byte 239 + * beyond to verify it is correctly uninstrumented. 240 + */ 241 + for (i = 0; i < sizeof(not_checked->data) + 1; i++) 242 + not_checked->data[i] = 'A'; 243 + 244 + pr_info("Array access beyond bounds ...\n"); 245 + for (i = 0; i < sizeof(checked->data) + 1; i++) 246 + checked->data[i] = 'B'; 247 + 248 + kfree(not_checked); 249 + kfree(checked); 177 250 } 178 251 179 252 void lkdtm_CORRUPT_LIST_ADD(void)
+3
drivers/misc/lkdtm/core.c
··· 130 130 CRASHTYPE(HARDLOCKUP), 131 131 CRASHTYPE(SPINLOCKUP), 132 132 CRASHTYPE(HUNG_TASK), 133 + CRASHTYPE(OVERFLOW_SIGNED), 134 + CRASHTYPE(OVERFLOW_UNSIGNED), 135 + CRASHTYPE(ARRAY_BOUNDS), 133 136 CRASHTYPE(EXEC_DATA), 134 137 CRASHTYPE(EXEC_STACK), 135 138 CRASHTYPE(EXEC_KMALLOC),
+3
drivers/misc/lkdtm/lkdtm.h
··· 22 22 void lkdtm_HARDLOCKUP(void); 23 23 void lkdtm_SPINLOCKUP(void); 24 24 void lkdtm_HUNG_TASK(void); 25 + void lkdtm_OVERFLOW_SIGNED(void); 26 + void lkdtm_OVERFLOW_UNSIGNED(void); 27 + void lkdtm_ARRAY_BOUNDS(void); 25 28 void lkdtm_CORRUPT_LIST_ADD(void); 26 29 void lkdtm_CORRUPT_LIST_DEL(void); 27 30 void lkdtm_CORRUPT_USER_DS(void);