blob: 6dbcf0d69eae5dbf67a2ade45301aeea02206627 [file] [log] [blame]
/*
*sec_debeg_kasan.c
*
* Copyright (c) 2018 Samsung Electronics Co., Ltd
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sec_debug.h>
#include <linux/sec_debug_ksan.h>
#include <linux/sec_ext.h>
#include <linux/sec_class.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
static LIST_HEAD(kasan_list);
void sec_debug_kasan_handler(struct kasandebug *ka)
{
struct kasandebug *this;
char *ap, *str=ka->ap;
ap = strsep(&str, "+");
list_for_each_entry(this, &kasan_list, entry) {
if((this->rType == ALLTYPE) && !strncmp(this->ap, "all", 3))
panic("kasan for all rType\n");
if(this->rType == ALLTYPE)
if(!strcmp(this->ap, ap))
panic("kasan for all type @%s",ap);
if(this->rType == ka->rType)
{
if(!strncmp(this->ap, "all", 3))
panic("kasan for type :%d",ka->rType);
if(!strcmp(this->ap, ap))
panic("kasan for type %d with %s",ka->rType, ka->ap);
}
}
}
static ssize_t sec_kasan_test_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
pr_info("-----------------------------------\n");
pr_info(" Report Type #rType \n");
pr_info("-----------------------------------\n");
pr_info(" out-of-bounds 1 \n");
pr_info(" slab-of-bounds 2 \n");
pr_info(" global-of-bounds 3 \n");
pr_info(" stack-of-bounds 4 \n");
pr_info(" use-after-free 5 \n");
pr_info(" use-after-scope 6 \n");
pr_info("-----------------------------------\n");
pr_info("\n");
pr_info("-----------------------------------\n");
pr_info("ex)\n");
pr_info(" echo add@#rType@Symbolname \n");
pr_info(" #rType '100' is for all type\n");
pr_info(" symbolname 'all' is for all symbol\n");
pr_info("-----------------------------------\n");
pr_info(" In case of bugreport below \n");
pr_info(" => BUG: KASAN: global-out-of-bounds in find_lowest_rq_fluid+0x1e64/0x2558 \n");
pr_info(" Set the setting as below \n");
pr_info(" => echo add@3@find_lowest_rq_fluid > /sys/class/sec/sec_debug_kasan/kasan_info\n");
pr_info("-----------------------------------\n");
return size;
}
static ssize_t sec_kasan_test_info_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
char * kasanslub;
kasanslub = kmalloc(4096,GFP_KERNEL);
kfree(kasanslub);
*(kasanslub)='C';
return size;
}
static void sec_kasan_show(void)
{
struct kasandebug *this;
int cnt=1;
if(list_empty(&kasan_list)){
pr_info("%s : sec upsan list empty \n", __func__);
return ;
}
pr_info("----------------------------------------\n");
pr_info(" idx rType acessposition \n");
pr_info("----------------------------------------\n");
list_for_each_entry(this, &kasan_list, entry) {
pr_info("[%3d] %4d %10s\n",cnt++, this->rType, this->ap);
}
pr_info("----------------------------------------\n");
}
static ssize_t sec_kasan_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
sec_kasan_show();
return size;
}
static int sec_kasan_delete_item(int rType, char *accesspoint)
{
struct kasandebug *this, *q;
list_for_each_entry_safe(this,q ,&kasan_list ,entry) {
if(this->rType == rType)
{
if(!strcmp(this->ap, accesspoint)){
list_del(&this->entry);
kfree(this);
return 1;
}
}
}
return 0;
}
static int sec_kasan_list_is_not_duplicated(struct kasandebug *ub)
{
struct kasandebug *this, *q;
list_for_each_entry_safe(this,q, &kasan_list, entry) {
if(this->rType == ub->rType)
{
if(!strncmp(this->ap, ub->ap, FILESIZE)){
return 0;
}
}
}
return 1;
}
static void sec_kasan_list_add(struct kasandebug *ka)
{
struct kasandebug *this, *q;
list_for_each_entry_safe(this,q, &kasan_list, entry) {
if(this->rType == ka->rType)
{
/*
* In case of set the all for the rType,
* remove 'all' set and set the new specific filename for the rType.
*/
if(!strncmp(this->ap,"all",3)){
list_del(&this->entry);
kfree(this);
}
}
}
list_add(&ka->entry, &kasan_list);
}
/*
* remove all rType with ap and set rType with all
*/
static void sec_kasan_set_all_for_rType(struct kasandebug* ka)
{
struct kasandebug *this,*q;
list_for_each_entry_safe(this,q, &kasan_list, entry) {
if(this->rType == ka->rType){
list_del(&this->entry);
kfree(this);
}
}
list_add(&ka->entry, &kasan_list);
}
static void sec_kasan_delete_list_all(void)
{
struct kasandebug *this,*q;
list_for_each_entry_safe(this,q, &kasan_list, entry) {
list_del(&this->entry);
kfree(this);
}
}
static void sec_kasan_set_all(struct kasandebug* ka)
{
sec_kasan_delete_list_all();
list_add(&ka->entry, &kasan_list);
}
static ssize_t sec_kasan_info_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
struct kasandebug *new_kasan;
char *str = (char *)buf;
char *action, *rTypestr, *ap, *aptmp;
int rType;
action = strsep(&str,"@");
rTypestr = strsep(&str,"@");
rType = simple_strtol(rTypestr,NULL,10);
aptmp = strsep(&str,"@");
ap = strsep(&aptmp,"\n");
if(!strncmp(action,"add",3)){
new_kasan = kmalloc(sizeof(struct kasandebug), GFP_KERNEL);
strncpy(new_kasan->ap, ap, FILESIZE);
new_kasan->rType = rType;
if(!strncmp(new_kasan->ap, "all",3) && (rType == ALLTYPE)){
sec_kasan_set_all(new_kasan);
} else if(!strncmp(new_kasan->ap, "all",3)){
sec_kasan_set_all_for_rType(new_kasan);
} else {
if(sec_kasan_list_is_not_duplicated(new_kasan)){
sec_kasan_list_add(new_kasan);
} else {
pr_info("duplicated!\n");
kfree(new_kasan);
}
}
} else if (!strncmp(action,"del",3)) {
if( (rType == ALLTYPE ) && (!strncmp(ap,"all",3)) ){
sec_kasan_delete_list_all();
} else {
sec_kasan_delete_item(rType, ap);
}
} else {
pr_info("%s : check your cmd again\n", __func__);
pr_info("%s : usage : echo add@#rType@accesspoint > kasan_info\n", __func__);
}
sec_kasan_show();
return size;
}
static struct kobj_attribute sec_kasan_info_attr =
__ATTR(kasan_info, 0644, sec_kasan_info_show, sec_kasan_info_store);
static struct kobj_attribute sec_kasan_test_info_attr =
__ATTR(kasan_test_info, 0644, sec_kasan_test_info_show, sec_kasan_test_info_store);
static struct attribute *sec_kasan_attributes[] = {
&sec_kasan_info_attr.attr,
&sec_kasan_test_info_attr.attr,
NULL,
};
static struct attribute_group sec_kasan_attr_group = {
.attrs = sec_kasan_attributes,
};
static int __init sec_debug_kasan_init(void)
{
int ret = 0;
struct device *dev;
dev = sec_device_create(NULL, "sec_debug_kasan");
if(!dev)
pr_err("%s : sec device create failed!\n", __func__);
ret = sysfs_create_group(&dev->kobj, &sec_kasan_attr_group);
if (ret)
pr_err("%s : could not create sysfs node\n", __func__);
return 0;
}
device_initcall(sec_debug_kasan_init);