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

lkdtm: Add tests for struct list corruption

When building under CONFIG_DEBUG_LIST, list addition and removal will be
sanity-checked. This validates that the check is working as expected by
setting up classic corruption attacks against list manipulations, available
with the new lkdtm tests CORRUPT_LIST_ADD and CORRUPT_LIST_DEL.

Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Rik van Riel <riel@redhat.com>

authored by

Kees Cook and committed by
Paul E. McKenney
6819d101 de54ebbe

+72
+2
drivers/misc/lkdtm.h
··· 21 21 void lkdtm_HUNG_TASK(void); 22 22 void lkdtm_ATOMIC_UNDERFLOW(void); 23 23 void lkdtm_ATOMIC_OVERFLOW(void); 24 + void lkdtm_CORRUPT_LIST_ADD(void); 25 + void lkdtm_CORRUPT_LIST_DEL(void); 24 26 25 27 /* lkdtm_heap.c */ 26 28 void lkdtm_OVERWRITE_ALLOCATION(void);
+68
drivers/misc/lkdtm_bugs.c
··· 5 5 * test source files. 6 6 */ 7 7 #include "lkdtm.h" 8 + #include <linux/list.h> 8 9 #include <linux/sched.h> 10 + 11 + struct lkdtm_list { 12 + struct list_head node; 13 + }; 9 14 10 15 /* 11 16 * Make sure our attempts to over run the kernel stack doesn't trigger ··· 150 145 151 146 pr_info("attempting bad atomic overflow\n"); 152 147 atomic_inc(&over); 148 + } 149 + 150 + void lkdtm_CORRUPT_LIST_ADD(void) 151 + { 152 + /* 153 + * Initially, an empty list via LIST_HEAD: 154 + * test_head.next = &test_head 155 + * test_head.prev = &test_head 156 + */ 157 + LIST_HEAD(test_head); 158 + struct lkdtm_list good, bad; 159 + void *target[2] = { }; 160 + void *redirection = &target; 161 + 162 + pr_info("attempting good list addition\n"); 163 + 164 + /* 165 + * Adding to the list performs these actions: 166 + * test_head.next->prev = &good.node 167 + * good.node.next = test_head.next 168 + * good.node.prev = test_head 169 + * test_head.next = good.node 170 + */ 171 + list_add(&good.node, &test_head); 172 + 173 + pr_info("attempting corrupted list addition\n"); 174 + /* 175 + * In simulating this "write what where" primitive, the "what" is 176 + * the address of &bad.node, and the "where" is the address held 177 + * by "redirection". 178 + */ 179 + test_head.next = redirection; 180 + list_add(&bad.node, &test_head); 181 + 182 + if (target[0] == NULL && target[1] == NULL) 183 + pr_err("Overwrite did not happen, but no BUG?!\n"); 184 + else 185 + pr_err("list_add() corruption not detected!\n"); 186 + } 187 + 188 + void lkdtm_CORRUPT_LIST_DEL(void) 189 + { 190 + LIST_HEAD(test_head); 191 + struct lkdtm_list item; 192 + void *target[2] = { }; 193 + void *redirection = &target; 194 + 195 + list_add(&item.node, &test_head); 196 + 197 + pr_info("attempting good list removal\n"); 198 + list_del(&item.node); 199 + 200 + pr_info("attempting corrupted list removal\n"); 201 + list_add(&item.node, &test_head); 202 + 203 + /* As with the list_add() test above, this corrupts "next". */ 204 + item.node.next = redirection; 205 + list_del(&item.node); 206 + 207 + if (target[0] == NULL && target[1] == NULL) 208 + pr_err("Overwrite did not happen, but no BUG?!\n"); 209 + else 210 + pr_err("list_del() corruption not detected!\n"); 153 211 }
+2
drivers/misc/lkdtm_core.c
··· 197 197 CRASHTYPE(EXCEPTION), 198 198 CRASHTYPE(LOOP), 199 199 CRASHTYPE(OVERFLOW), 200 + CRASHTYPE(CORRUPT_LIST_ADD), 201 + CRASHTYPE(CORRUPT_LIST_DEL), 200 202 CRASHTYPE(CORRUPT_STACK), 201 203 CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), 202 204 CRASHTYPE(OVERWRITE_ALLOCATION),