Concurrency defenders in config parse and module destroy

This commit is parr of #213 fix.
In this issue kernel module crashes on high bandwidth usage has been
reported. The part of the problem is concurrency usage: when config gets
freed, callbacks keep to depend on it.
This commit is contained in:
Vadim Vetrov
2025-01-08 15:43:15 +03:00
parent cadec5a05c
commit 16ba8801c1
2 changed files with 65 additions and 4 deletions

View File

@@ -24,11 +24,23 @@
#include "args.h"
#include "logging.h"
/**
* Defined in kyoutubeUnblock.c
*/
extern struct spinlock hot_config_spinlock;
extern atomic_t hot_config_counter;
extern atomic_t hot_config_rep;
extern struct mutex config_free_mutex;
#define MAX_ARGC 1024
static char *argv[MAX_ARGC];
static int params_set(const char *cval, const struct kernel_param *kp) {
int ret = 0;
int ret;
ret = mutex_trylock(&config_free_mutex);
if (ret == 0)
return -EBUSY;
int cv_len = strlen(cval);
if (cv_len >= 1 && cval[cv_len - 1] == '\n') {
@@ -58,8 +70,25 @@ static int params_set(const char *cval, const struct kernel_param *kp) {
}
}
spin_lock(&hot_config_spinlock);
// lock netfilter youtubeUnblock
atomic_set(&hot_config_rep, 1);
spin_unlock(&hot_config_spinlock);
// lock config hot replacement process until all
// netfilter callbacks keep running
while (atomic_read(&hot_config_counter) > 0) {}
ret = yparse_args(argc, argv);
spin_lock(&hot_config_spinlock);
// relaunch youtubeUnblock
atomic_set(&hot_config_rep, 0);
spin_unlock(&hot_config_spinlock);
kfree(val);
mutex_unlock(&config_free_mutex);
return ret;
}

View File

@@ -61,9 +61,15 @@ MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock");
static struct socket *rawsocket;
static struct socket *raw6socket;
DEFINE_SPINLOCK(hot_config_spinlock);
DEFINE_MUTEX(config_free_mutex);
atomic_t hot_config_counter = ATOMIC_INIT(0);
// boolean flag for hot config replacement
// if 1, youtubeUnblock should stop processing
atomic_t hot_config_rep = ATOMIC_INIT(0);
static int open_raw_socket(void) {
int ret = 0;
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
@@ -385,6 +391,17 @@ static NF_CALLBACK(ykb_nf_hook, skb) {
int ret;
struct packet_data pd = {0};
spin_lock(&hot_config_spinlock);
// if set flag to disable processing,
// explicitly accept all packets
if (atomic_read(&hot_config_rep)) {
spin_unlock(&hot_config_spinlock);
return NF_ACCEPT;
} else {
atomic_inc(&hot_config_counter);
}
spin_unlock(&hot_config_spinlock);
if ((skb->mark & config.mark) == config.mark)
goto accept;
@@ -422,8 +439,10 @@ static NF_CALLBACK(ykb_nf_hook, skb) {
}
accept:
atomic_dec(&hot_config_counter);
return NF_ACCEPT;
drop:
atomic_dec(&hot_config_counter);
kfree_skb(skb);
return NF_STOLEN;
}
@@ -505,6 +524,17 @@ err:
}
static void __exit ykb_destroy(void) {
mutex_lock(&config_free_mutex);
// acquire all locks.
spin_lock(&hot_config_spinlock);
// lock netfilter youtubeUnblock
atomic_set(&hot_config_rep, 1);
spin_unlock(&hot_config_spinlock);
// wait until all
// netfilter callbacks keep running
while (atomic_read(&hot_config_counter) > 0) {}
if (config.use_ipv6) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
struct net *n;
@@ -513,7 +543,6 @@ static void __exit ykb_destroy(void) {
#else
nf_unregister_hook(&ykb6_nf_reg);
#endif
close_raw6_socket();
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
@@ -523,9 +552,12 @@ static void __exit ykb_destroy(void) {
#else
nf_unregister_hook(&ykb_nf_reg);
#endif
if (config.use_ipv6) {
close_raw6_socket();
}
close_raw_socket();
free_config(config);
lginfo("youtubeUnblock kernel module destroyed.\n");
}