OpenCloudOS-Kernel/kernel/kill_block.c

182 lines
4.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* linux/kernel/kill_block.c
*
* Copyright (C) 2023 Hongbo Li <herberthbli@tencent.com>
*/
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/kill_block.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
int sysctl_sig_kill_block;
static LIST_HEAD(kill_block_list);
static DEFINE_RWLOCK(kill_block_lock);
static ssize_t kill_block_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
char cmd[KILL_BLOCK_CMD_LEN], _str[KILL_BLOCK_CMD_LEN];
char *token[3], *str;
int cnt, i = 0;
struct kill_block_rule *rule, *tmp;
memset(cmd, 0, KILL_BLOCK_CMD_LEN);
cnt = min_t(size_t, count, KILL_BLOCK_CMD_LEN - 1);
if (strncpy_from_user(cmd, ubuf, cnt) < 0)
return -EINVAL;
if (strlen(cmd) < 1)
return -EINVAL;
strcpy(_str, cmd);
str = _str;
str[cnt - 1] = '\0';
i = 0;
while ((token[i] = strsep(&str, " ")) != NULL) {
if (!strlen(token[i]))
break;
i++;
if (i == 3)
break;
}
if (i == 1) {
if (!strcmp(token[0], "flush")) {
write_lock_bh(&kill_block_lock);
list_for_each_entry_safe(rule, tmp, &kill_block_list,
node) {
list_del(&rule->node);
kfree(rule);
}
write_unlock_bh(&kill_block_lock);
return cnt;
}
return -EINVAL;
} else if (i != 3) {
return -EINVAL;
}
if (!strcmp(token[0], "add")) {
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
if (!strcmp(token[1], "*"))
return -EINVAL;
cnt = min_t(size_t, TASK_COMM_LEN - 1, strlen(token[1]));
strncpy(rule->src_comm, token[1], cnt);
rule->src_comm[cnt] = '\0';
cnt = min_t(size_t, TASK_COMM_LEN - 1, strlen(token[2]));
strncpy(rule->dst_comm, token[2], cnt);
rule->dst_comm[cnt] = '\0';
write_lock_bh(&kill_block_lock);
list_add(&rule->node, &kill_block_list);
write_unlock_bh(&kill_block_lock);
} else if (!strcmp(token[0], "del")) {
list_for_each_entry_safe(rule, tmp, &kill_block_list, node) {
if (!strcasecmp(rule->src_comm, token[1]) &&
!strcasecmp(rule->dst_comm, token[2])) {
write_lock_bh(&kill_block_lock);
list_del(&rule->node);
write_unlock_bh(&kill_block_lock);
kfree(rule);
break;
}
}
} else {
return -EINVAL;
}
return count;
}
static void *kill_block_seq_start(struct seq_file *m, loff_t *pos)
{
seq_puts(m, "src_comm\tdst_comm\n");
read_lock_bh(&kill_block_lock);
return seq_list_start(&kill_block_list, *pos);
}
static void *kill_block_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
return seq_list_next(v, &kill_block_list, pos);
}
static void kill_block_seq_stop(struct seq_file *m, void *v)
{
read_unlock_bh(&kill_block_lock);
}
static int kill_block_seq_show(struct seq_file *m, void *v)
{
struct kill_block_rule *rule;
rule = list_entry(v, struct kill_block_rule, node);
seq_printf(m, "%s\t%s\n", rule->src_comm, rule->dst_comm);
return 0;
}
static const struct seq_operations kill_block_seq_ops = {
.start = kill_block_seq_start,
.next = kill_block_seq_next,
.stop = kill_block_seq_stop,
.show = kill_block_seq_show,
};
static int kill_block_seq_open(struct inode *inode, struct file *filp)
{
return seq_open(filp, &kill_block_seq_ops);
}
static const struct file_operations kill_block_fops = {
.open = kill_block_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.write = kill_block_write,
.release = seq_release,
};
int kill_block_match(struct task_struct *p)
{
struct list_head *item;
struct kill_block_rule *rule;
int match = 1;
if (list_empty(&kill_block_list))
goto out;
read_lock_bh(&kill_block_lock);
list_for_each(item, &kill_block_list) {
rule = list_entry(item, struct kill_block_rule, node);
if (!strcasecmp(current->comm, rule->src_comm)) {
if (!strcmp(rule->dst_comm, "*") ||
strstr(p->comm, rule->dst_comm)) {
match = 0;
break;
}
}
}
read_unlock_bh(&kill_block_lock);
out:
if (match)
pr_info_ratelimited("block the kill signal from %s to %s\n",
current->comm, p->comm);
return match;
}
static int __init kill_block_init(void)
{
proc_create("kill_block", 0, NULL, &kill_block_fops);
return 0;
}
late_initcall(kill_block_init);