mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-27 04:47:05 +00:00
62931f59ce
if the IPVS module is removed while the sync daemon is starting, there is a small gap where try_module_get() might fail getting the refcount inside ip_vs_use_count_inc(). Then, the refcounts of IPVS module are unbalanced, and the subsequent call to stop_sync_thread() causes the following splat: WARNING: CPU: 0 PID: 4013 at kernel/module.c:1146 module_put.part.44+0x15b/0x290 Modules linked in: ip_vs(-) nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 veth ip6table_filter ip6_tables iptable_filter binfmt_misc intel_rapl_msr intel_rapl_common crct10dif_pclmul crc32_pclmul ext4 mbcache jbd2 ghash_clmulni_intel snd_hda_codec_generic ledtrig_audio snd_hda_intel snd_intel_nhlt snd_hda_codec snd_hda_core snd_hwdep snd_seq snd_seq_device snd_pcm aesni_intel crypto_simd cryptd glue_helper joydev pcspkr snd_timer virtio_balloon snd soundcore i2c_piix4 nfsd auth_rpcgss nfs_acl lockd grace sunrpc ip_tables xfs libcrc32c ata_generic pata_acpi virtio_net net_failover virtio_blk failover virtio_console qxl drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ata_piix ttm crc32c_intel serio_raw drm virtio_pci libata virtio_ring virtio floppy dm_mirror dm_region_hash dm_log dm_mod [last unloaded: nf_defrag_ipv6] CPU: 0 PID: 4013 Comm: modprobe Tainted: G W 5.4.0-rc1.upstream+ #741 Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 RIP: 0010:module_put.part.44+0x15b/0x290 Code: 04 25 28 00 00 00 0f 85 18 01 00 00 48 83 c4 68 5b 5d 41 5c 41 5d 41 5e 41 5f c3 89 44 24 28 83 e8 01 89 c5 0f 89 57 ff ff ff <0f> 0b e9 78 ff ff ff 65 8b 1d 67 83 26 4a 89 db be 08 00 00 00 48 RSP: 0018:ffff888050607c78 EFLAGS: 00010297 RAX: 0000000000000003 RBX: ffffffffc1420590 RCX: ffffffffb5db0ef9 RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffffc1420590 RBP: 00000000ffffffff R08: fffffbfff82840b3 R09: fffffbfff82840b3 R10: 0000000000000001 R11: fffffbfff82840b2 R12: 1ffff1100a0c0f90 R13: ffffffffc1420200 R14: ffff88804f533300 R15: ffff88804f533ca0 FS: 00007f8ea9720740(0000) GS:ffff888053800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f3245abe000 CR3: 000000004c28a006 CR4: 00000000001606f0 Call Trace: stop_sync_thread+0x3a3/0x7c0 [ip_vs] ip_vs_sync_net_cleanup+0x13/0x50 [ip_vs] ops_exit_list.isra.5+0x94/0x140 unregister_pernet_operations+0x29d/0x460 unregister_pernet_device+0x26/0x60 ip_vs_cleanup+0x11/0x38 [ip_vs] __x64_sys_delete_module+0x2d5/0x400 do_syscall_64+0xa5/0x4e0 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x7f8ea8bf0db7 Code: 73 01 c3 48 8b 0d b9 80 2c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 89 80 2c 00 f7 d8 64 89 01 48 RSP: 002b:00007ffcd38d2fe8 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0 RAX: ffffffffffffffda RBX: 0000000002436240 RCX: 00007f8ea8bf0db7 RDX: 0000000000000000 RSI: 0000000000000800 RDI: 00000000024362a8 RBP: 0000000000000000 R08: 00007f8ea8eba060 R09: 00007f8ea8c658a0 R10: 00007ffcd38d2a60 R11: 0000000000000206 R12: 0000000000000000 R13: 0000000000000001 R14: 00000000024362a8 R15: 0000000000000000 irq event stamp: 4538 hardirqs last enabled at (4537): [<ffffffffb6193dde>] quarantine_put+0x9e/0x170 hardirqs last disabled at (4538): [<ffffffffb5a0556a>] trace_hardirqs_off_thunk+0x1a/0x20 softirqs last enabled at (4522): [<ffffffffb6f8ebe9>] sk_common_release+0x169/0x2d0 softirqs last disabled at (4520): [<ffffffffb6f8eb3e>] sk_common_release+0xbe/0x2d0 Check the return value of ip_vs_use_count_inc() and let its caller return proper error. Inside do_ip_vs_set_ctl() the module is already refcounted, we don't need refcount/derefcount there. Finally, in register_ip_vs_app() and start_sync_thread(), take the module refcount earlier and ensure it's released in the error path. Change since v1: - better return values in case of failure of ip_vs_use_count_inc(), thanks to Julian Anastasov - no need to increase/decrease the module refcount in ip_vs_set_ctl(), thanks to Julian Anastasov Signed-off-by: Davide Caratti <dcaratti@redhat.com> Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Simon Horman <horms@verge.net.au>
112 lines
2.4 KiB
C
112 lines
2.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
#define KMSG_COMPONENT "IPVS"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <asm/string.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/sysctl.h>
|
|
|
|
#include <net/ip_vs.h>
|
|
|
|
/* IPVS pe list */
|
|
static LIST_HEAD(ip_vs_pe);
|
|
|
|
/* semaphore for IPVS PEs. */
|
|
static DEFINE_MUTEX(ip_vs_pe_mutex);
|
|
|
|
/* Get pe in the pe list by name */
|
|
struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
|
|
{
|
|
struct ip_vs_pe *pe;
|
|
|
|
IP_VS_DBG(10, "%s(): pe_name \"%s\"\n", __func__,
|
|
pe_name);
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(pe, &ip_vs_pe, n_list) {
|
|
/* Test and get the modules atomically */
|
|
if (pe->module &&
|
|
!try_module_get(pe->module)) {
|
|
/* This pe is just deleted */
|
|
continue;
|
|
}
|
|
if (strcmp(pe_name, pe->name)==0) {
|
|
/* HIT */
|
|
rcu_read_unlock();
|
|
return pe;
|
|
}
|
|
module_put(pe->module);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Lookup pe and try to load it if it doesn't exist */
|
|
struct ip_vs_pe *ip_vs_pe_getbyname(const char *name)
|
|
{
|
|
struct ip_vs_pe *pe;
|
|
|
|
/* Search for the pe by name */
|
|
pe = __ip_vs_pe_getbyname(name);
|
|
|
|
/* If pe not found, load the module and search again */
|
|
if (!pe) {
|
|
request_module("ip_vs_pe_%s", name);
|
|
pe = __ip_vs_pe_getbyname(name);
|
|
}
|
|
|
|
return pe;
|
|
}
|
|
|
|
/* Register a pe in the pe list */
|
|
int register_ip_vs_pe(struct ip_vs_pe *pe)
|
|
{
|
|
struct ip_vs_pe *tmp;
|
|
|
|
/* increase the module use count */
|
|
if (!ip_vs_use_count_inc())
|
|
return -ENOENT;
|
|
|
|
mutex_lock(&ip_vs_pe_mutex);
|
|
/* Make sure that the pe with this name doesn't exist
|
|
* in the pe list.
|
|
*/
|
|
list_for_each_entry(tmp, &ip_vs_pe, n_list) {
|
|
if (strcmp(tmp->name, pe->name) == 0) {
|
|
mutex_unlock(&ip_vs_pe_mutex);
|
|
ip_vs_use_count_dec();
|
|
pr_err("%s(): [%s] pe already existed "
|
|
"in the system\n", __func__, pe->name);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
/* Add it into the d-linked pe list */
|
|
list_add_rcu(&pe->n_list, &ip_vs_pe);
|
|
mutex_unlock(&ip_vs_pe_mutex);
|
|
|
|
pr_info("[%s] pe registered.\n", pe->name);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(register_ip_vs_pe);
|
|
|
|
/* Unregister a pe from the pe list */
|
|
int unregister_ip_vs_pe(struct ip_vs_pe *pe)
|
|
{
|
|
mutex_lock(&ip_vs_pe_mutex);
|
|
/* Remove it from the d-linked pe list */
|
|
list_del_rcu(&pe->n_list);
|
|
mutex_unlock(&ip_vs_pe_mutex);
|
|
|
|
/* decrease the module use count */
|
|
ip_vs_use_count_dec();
|
|
|
|
pr_info("[%s] pe unregistered.\n", pe->name);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(unregister_ip_vs_pe);
|