blob: e62a7de51eac7a9f213fb993d5133a0bd9906544 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* This is for all the tests related to list integrity checking.
*/
#include "lkdtm.h"
#include <linux/list.h>
#define LIST_REDIR_BUF(type, name) \
union { \
type list; \
char buf[sizeof(type)]; \
} name = { }
static void __check_list_redir_buf(const char *str, char *buf, size_t sz)
{
for (; sz && !buf[sz - 1]; sz--);
if (sz)
pr_err("%s: corruption not detected!\n", str);
else
pr_err("%s: overwrite did not happen, but no BUG?!\n", str);
}
#define check_list_redir_buf(s, b) \
__check_list_redir_buf((s), (b).buf, sizeof(b.buf))
void lkdtm_LIST_ADD_NEXT_CORRUPTION(void)
{
LIST_HEAD(head);
LIST_HEAD(mid);
LIST_HEAD(tail);
LIST_REDIR_BUF(struct list_head, target);
list_add(&tail, &head);
head.next = &target.list;
list_add(&mid, &head);
check_list_redir_buf("list_add()", target);
}
void lkdtm_LIST_ADD_PREV_CORRUPTION(void)
{
LIST_HEAD(head);
LIST_HEAD(mid);
LIST_HEAD(tail);
LIST_REDIR_BUF(struct list_head, target);
list_add(&tail, &head);
tail.prev = &target.list;
list_add_tail(&mid, &tail);
check_list_redir_buf("list_add()", target);
}
void lkdtm_LIST_ADD_TWICE(void)
{
LIST_HEAD(head);
LIST_HEAD(mid);
LIST_HEAD(tail);
list_add(&tail, &head);
mid = tail;
list_add(&tail, &head);
if (mid.prev != tail.prev || mid.next != tail.next)
pr_err("list_add(): adding twice not detected!\n");
else
pr_err("list_add(): could not add twice, but no BUG?!\n");
}
void lkdtm_LIST_DEL_NEXT_CORRUPTION(void)
{
LIST_HEAD(head);
LIST_HEAD(mid);
LIST_HEAD(tail);
LIST_REDIR_BUF(struct list_head, target);
list_add(&tail, &head);
list_add(&mid, &head);
mid.next = &target.list;
list_del(&mid);
check_list_redir_buf("list_del()", target);
}
void lkdtm_LIST_DEL_PREV_CORRUPTION(void)
{
LIST_HEAD(head);
LIST_HEAD(mid);
LIST_HEAD(tail);
LIST_REDIR_BUF(struct list_head, target);
list_add(&tail, &head);
list_add(&mid, &head);
mid.prev = &target.list;
list_del(&mid);
check_list_redir_buf("list_del()", target);
}
void lkdtm_LIST_DEL_TWICE(void)
{
LIST_HEAD(head);
LIST_HEAD(tail);
list_add(&tail, &head);
list_del(&tail);
if (tail.prev != LIST_POISON2 || tail.next != LIST_POISON1) {
pr_err("list_del(): prev/next pointers not poisoned!\n");
} else {
list_del(&tail);
pr_err("list_del(): could not delete twice, but no BUG?!\n");
}
}
void lkdtm_HLIST_ADD_HEAD_CORRUPTION(void)
{
HLIST_HEAD(head);
LIST_REDIR_BUF(struct hlist_node, target);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
head.first = &target.list;
hlist_add_head(&mid, &head);
check_list_redir_buf("hlist_add_head()", target);
}
void lkdtm_HLIST_ADD_HEAD_TWICE(void)
{
HLIST_HEAD(head);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
mid = tail;
hlist_add_head(&tail, &head);
if (mid.next != tail.next || mid.pprev != tail.pprev)
pr_err("hlist_add_head(): adding twice not detected!\n");
else
pr_err("hlist_add_head(): could not add twice, but no BUG?!\n");
}
void lkdtm_HLIST_ADD_BEFORE_CORRUPTION(void)
{
HLIST_HEAD(head);
LIST_REDIR_BUF(struct hlist_node, target);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
tail.pprev = &target.list.next;
hlist_add_before(&mid, &tail);
check_list_redir_buf("hlist_add_before()", target);
}
void lkdtm_HLIST_ADD_BEFORE_TWICE(void)
{
HLIST_HEAD(head);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
mid = tail;
hlist_add_before(&tail, &tail);
if (mid.next != tail.next || mid.pprev != tail.pprev)
pr_err("hlist_add_before(): adding twice not detected!\n");
else
pr_err("hlist_add_before(): could not add twice, but no BUG?!\n");
}
void lkdtm_HLIST_ADD_BEHIND_CORRUPTION(void)
{
HLIST_HEAD(head);
LIST_REDIR_BUF(struct hlist_node, target);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&mid, &head);
mid.next = &target.list;
hlist_add_behind(&tail, &mid);
check_list_redir_buf("hlist_add_behind()", target);
}
void lkdtm_HLIST_ADD_BEHIND_TWICE(void)
{
HLIST_HEAD(head);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
mid = tail;
hlist_add_behind(&tail, &tail);
if (mid.next != tail.next || mid.pprev != tail.pprev)
pr_err("hlist_add_behind(): adding twice not detected!\n");
else
pr_err("hlist_add_behind(): could not add twice, but no BUG?!\n");
}
void lkdtm_HLIST_DEL_PREV_CORRUPTION(void)
{
HLIST_HEAD(head);
LIST_REDIR_BUF(struct hlist_node, target);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
hlist_add_head(&mid, &head);
mid.pprev = &target.list.next;
hlist_del(&mid);
check_list_redir_buf("hlist_del()", target);
}
void lkdtm_HLIST_DEL_NEXT_CORRUPTION(void)
{
HLIST_HEAD(head);
LIST_REDIR_BUF(struct hlist_node, target);
struct hlist_node mid, tail;
INIT_HLIST_NODE(&mid);
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
hlist_add_head(&mid, &head);
mid.next = &target.list;
hlist_del(&mid);
check_list_redir_buf("hlist_del()", target);
}
void lkdtm_HLIST_DEL_TWICE(void)
{
HLIST_HEAD(head);
struct hlist_node tail;
INIT_HLIST_NODE(&tail);
hlist_add_head(&tail, &head);
hlist_del(&tail);
if (tail.next != LIST_POISON1 || tail.pprev != LIST_POISON2) {
pr_err("hlist_del(): pprev/next pointers not poisoned!\n");
} else {
hlist_del(&tail);
pr_err("hlist_del(): could not delete twice, but no BUG?!\n");
}
}
#include <linux/list_nulls.h>
void lkdtm_HLIST_NULLS_ADD_HEAD_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_nulls_node, target);
struct hlist_nulls_head head;
struct hlist_nulls_node mid, tail;
INIT_HLIST_NULLS_HEAD(&head, 0);
hlist_nulls_add_head(&tail, &head);
head.first = &target.list;
hlist_nulls_add_head(&mid, &head);
check_list_redir_buf("hlist_nulls_add_head()", target);
}
void lkdtm_HLIST_NULLS_ADD_HEAD_TWICE(void)
{
struct hlist_nulls_head head;
struct hlist_nulls_node mid, tail;
INIT_HLIST_NULLS_HEAD(&head, 0);
hlist_nulls_add_head(&tail, &head);
mid = tail;
hlist_nulls_add_head(&tail, &head);
if (mid.next != tail.next || mid.pprev != tail.pprev)
pr_err("hlist_nulls_add_head(): adding twice not detected!\n");
else
pr_err("hlist_nulls_add_head(): could not add twice, but no BUG?!\n");
}
void lkdtm_HLIST_NULLS_DEL_PREV_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_nulls_node, target);
struct hlist_nulls_head head;
struct hlist_nulls_node mid, tail;
INIT_HLIST_NULLS_HEAD(&head, 0);
hlist_nulls_add_head(&tail, &head);
hlist_nulls_add_head(&mid, &head);
mid.pprev = &target.list.next;
hlist_nulls_del(&mid);
check_list_redir_buf("hlist_nulls_del()", target);
}
void lkdtm_HLIST_NULLS_DEL_NEXT_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_nulls_node, target);
struct hlist_nulls_head head;
struct hlist_nulls_node mid, tail;
INIT_HLIST_NULLS_HEAD(&head, 0);
hlist_nulls_add_head(&tail, &head);
hlist_nulls_add_head(&mid, &head);
mid.next = &target.list;
hlist_nulls_del(&mid);
check_list_redir_buf("hlist_nulls_del()", target);
}
void lkdtm_HLIST_NULLS_DEL_TWICE(void)
{
struct hlist_nulls_head head;
struct hlist_nulls_node tail;
INIT_HLIST_NULLS_HEAD(&head, 0);
hlist_nulls_add_head(&tail, &head);
hlist_nulls_del(&tail);
if (tail.next != LIST_POISON1 || tail.pprev != LIST_POISON2) {
pr_err("hlist_nulls_del(): pprev/next pointers not poisoned!\n");
} else {
hlist_nulls_del(&tail);
pr_err("hlist_nulls_del(): could not delete twice, but no BUG?!\n");
}
}
#include <linux/list_bl.h>
void lkdtm_HLIST_BL_ADD_HEAD_UNLOCKED(void)
{
LIST_REDIR_BUF(struct hlist_bl_node, target);
struct hlist_bl_head head;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_add_head(&target.list, &head);
check_list_redir_buf("hlist_bl_add()", target);
}
void lkdtm_HLIST_BL_ADD_HEAD_NODE_LOCKED(void)
{
LIST_REDIR_BUF(struct hlist_bl_node, target);
struct hlist_bl_head head;
unsigned long nval;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
nval = (unsigned long)&target.list | LIST_BL_LOCKMASK;
hlist_bl_add_head((struct hlist_bl_node *)nval, &head);
hlist_bl_unlock(&head);
check_list_redir_buf("hlist_bl_add()", target);
}
void lkdtm_HLIST_BL_ADD_HEAD_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_bl_node, target);
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
unsigned long nval;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
nval = (unsigned long)&target.list | LIST_BL_LOCKMASK;
head.first = (struct hlist_bl_node *)nval;
hlist_bl_add_head(&mid, &head);
hlist_bl_unlock(&head);
check_list_redir_buf("hlist_bl_add_head()", target);
}
void lkdtm_HLIST_BL_ADD_HEAD_TWICE(void)
{
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
mid = tail;
hlist_bl_add_head(&tail, &head);
hlist_bl_unlock(&head);
if (mid.next != tail.next || mid.pprev != tail.pprev)
pr_err("hlist_bl_add_head(): adding twice not detected!\n");
else
pr_err("hlist_bl_add_head(): could not add twice, but no BUG?!\n");
}
void lkdtm_HLIST_BL_DEL_NODE_LOCKED(void)
{
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
unsigned long nval;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
hlist_bl_add_head(&mid, &head);
hlist_bl_unlock(&head);
nval = (unsigned long)&mid | LIST_BL_LOCKMASK;
/* hlist_bl_del() poisons ->next, so don't use it with a locked node */
__hlist_bl_del((struct hlist_bl_node *)nval);
if (head.first != &mid || tail.pprev != &mid.next)
pr_err("hlist_bl_del(): deleting locked node not detected!\n");
else
pr_err("hlist_bl_del(): could not delete locked node, but no BUG?!\n");
}
void lkdtm_HLIST_BL_DEL_NEXT_LOCKED(void)
{
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
unsigned long nval;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
hlist_bl_add_head(&mid, &head);
hlist_bl_unlock(&head);
nval = (unsigned long)mid.next | LIST_BL_LOCKMASK;
mid.next = (struct hlist_bl_node *)nval;
hlist_bl_del(&mid);
if (head.first != &mid || tail.pprev != &mid.next)
pr_err("hlist_bl_del(): deleting node with locked ->next not detected!\n");
else
pr_err("hlist_bl_del(): could not delete node with locked ->next, but no BUG?!\n");
}
void lkdtm_HLIST_BL_DEL_PREV_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_bl_node, target);
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
hlist_bl_add_head(&mid, &head);
hlist_bl_unlock(&head);
mid.pprev = &target.list.next;
hlist_bl_del(&mid);
check_list_redir_buf("hlist_bl_del()", target);
}
void lkdtm_HLIST_BL_DEL_NEXT_CORRUPTION(void)
{
LIST_REDIR_BUF(struct hlist_bl_node, target);
struct hlist_bl_head head;
struct hlist_bl_node mid, tail;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
hlist_bl_add_head(&mid, &head);
hlist_bl_unlock(&head);
mid.next = &target.list;
hlist_bl_del(&mid);
check_list_redir_buf("hlist_bl_del()", target);
}
void lkdtm_HLIST_BL_DEL_TWICE(void)
{
struct hlist_bl_head head;
struct hlist_bl_node tail;
INIT_HLIST_BL_HEAD(&head);
hlist_bl_lock(&head);
hlist_bl_add_head(&tail, &head);
hlist_bl_unlock(&head);
hlist_bl_del(&tail);
if (tail.next != LIST_POISON1 || tail.pprev != LIST_POISON2) {
pr_err("hlist_bl_del(): pprev/next pointers not poisoned!\n");
} else {
hlist_bl_del(&tail);
pr_err("hlist_bl_del(): could not delete twice, but no BUG?!\n");
}
}