// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Andrey Ryabinin <a.ryabinin@samsung.com>
*/
#define pr_fmt(fmt) "kasan test: %s " fmt, __func__
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/kasan.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include <asm/page.h>
#include "../mm/kasan/kasan.h"
#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_SHADOW_SCALE_SIZE)
/*
* We assign some test results to these globals to make sure the tests
* are not eliminated as dead code.
*/
int kasan_int_result;
void *kasan_ptr_result;
/*
* Note: test functions are marked noinline so that their names appear in
* reports.
*/
static noinline void __init kmalloc_oob_right(void)
{
char *ptr;
size_t size = 123;
pr_info("out-of-bounds to right\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
ptr[size + OOB_TAG_OFF] = 'x';
kfree(ptr);
}
static noinline void __init kmalloc_oob_left(void)
{
char *ptr;
size_t size = 15;
pr_info("out-of-bounds to left\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
*ptr = *(ptr - 1);
kfree(ptr);
}
static noinline void __init kmalloc_node_oob_right(void)
{
char *ptr;
size_t size = 4096;
pr_info("kmalloc_node(): out-of-bounds to right\n");
ptr = kmalloc_node(size, GFP_KERNEL, 0);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
ptr[size] = 0;
kfree(ptr);
}
#ifdef CONFIG_SLUB
static noinline void __init kmalloc_pagealloc_oob_right(void)
{
char *ptr;
size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
/* Allocate a chunk that does not fit into a SLUB cache to trigger
* the page allocator fallback.
*/
pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
ptr[size + OOB_TAG_OFF] = 0;
kfree(ptr);
}
static noinline void __init kmalloc_pagealloc_uaf(void)
{
char *ptr;
size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
pr_info("kmalloc pagealloc allocation: use-after-free\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
kfree(ptr);
ptr[0] = 0;
}
static noinline void __init kmalloc_pagealloc_invalid_free(void)
{
char *ptr;
size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
pr_info("kmalloc pagealloc allocation: invalid-free\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
kfree(ptr + 1);
}
#endif
static noinline void __init kmalloc_large_oob_right(void)
{
char *ptr;
size_t size = KMALLOC_MAX_CACHE_SIZE - 256;
/* Allocate a chunk that is large enough, but still fits into a slab
* and does not trigger the page allocator fallback in SLUB.
*/
pr_info("kmalloc large allocation: out-of-bounds to right\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return;
}
ptr[size] = 0;
kfree(ptr);
}
static noinline void __init kmalloc_oob_krealloc_more(void)
{
char *ptr1, *ptr2;
size_t size1 = 17;
size_t size2 = 19;
pr_info("out-of-bounds after krealloc more\n");
ptr1 = kmalloc(size1, GFP_KERNEL);
ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
if (!ptr1 || !ptr2) {
pr_err("Allocation failed\n");
kfree(ptr1);
kfree(ptr2);
return;
}
ptr2[size2 + OOB_TAG_OFF] = 'x';
kfree(ptr2);
}
static noinline void __init kmalloc_oob_krealloc_less(void)
{
char *ptr1, *ptr2;
size_t size1 = 17;
size_t size2 = 15;
pr_info("out-of-bounds after krealloc less\n");
ptr1 = kmalloc(size1, GFP_KERNEL);
ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
if (!ptr1 || !ptr2) {
pr_err("Allocation failed\n");
kfree(ptr1);
return;
}
ptr2[size2 + OOB_TAG_OFF] = 'x';
kfree(ptr2);
}
static noinline void __init kmalloc_oob_16(void)
{
struct {
u64<