182 lines
4.1 KiB
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);
|