ipvs: move old_secure_tcp into struct netns_ipvs

syzbot reported the following issue :

BUG: KCSAN: data-race in update_defense_level / update_defense_level

read to 0xffffffff861a6260 of 4 bytes by task 3006 on cpu 1:
 update_defense_level+0x621/0xb30 net/netfilter/ipvs/ip_vs_ctl.c:177
 defense_work_handler+0x3d/0xd0 net/netfilter/ipvs/ip_vs_ctl.c:225
 process_one_work+0x3d4/0x890 kernel/workqueue.c:2269
 worker_thread+0xa0/0x800 kernel/workqueue.c:2415
 kthread+0x1d4/0x200 drivers/block/aoe/aoecmd.c:1253
 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:352

write to 0xffffffff861a6260 of 4 bytes by task 7333 on cpu 0:
 update_defense_level+0xa62/0xb30 net/netfilter/ipvs/ip_vs_ctl.c:205
 defense_work_handler+0x3d/0xd0 net/netfilter/ipvs/ip_vs_ctl.c:225
 process_one_work+0x3d4/0x890 kernel/workqueue.c:2269
 worker_thread+0xa0/0x800 kernel/workqueue.c:2415
 kthread+0x1d4/0x200 drivers/block/aoe/aoecmd.c:1253
 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:352

Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 7333 Comm: kworker/0:5 Not tainted 5.4.0-rc3+ #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Workqueue: events defense_work_handler

Indeed, old_secure_tcp is currently a static variable, while it
needs to be a per netns variable.

Fixes: a0840e2e16 ("IPVS: netns, ip_vs_ctl local vars moved to ipvs struct.")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
This commit is contained in:
Eric Dumazet 2019-10-23 09:53:03 -07:00 committed by Simon Horman
parent 62931f59ce
commit c24b75e0f9
2 changed files with 8 additions and 8 deletions

View File

@ -889,6 +889,7 @@ struct netns_ipvs {
struct delayed_work defense_work; /* Work handler */ struct delayed_work defense_work; /* Work handler */
int drop_rate; int drop_rate;
int drop_counter; int drop_counter;
int old_secure_tcp;
atomic_t dropentry; atomic_t dropentry;
/* locks in ctl.c */ /* locks in ctl.c */
spinlock_t dropentry_lock; /* drop entry handling */ spinlock_t dropentry_lock; /* drop entry handling */

View File

@ -93,7 +93,6 @@ static bool __ip_vs_addr_is_local_v6(struct net *net,
static void update_defense_level(struct netns_ipvs *ipvs) static void update_defense_level(struct netns_ipvs *ipvs)
{ {
struct sysinfo i; struct sysinfo i;
static int old_secure_tcp = 0;
int availmem; int availmem;
int nomem; int nomem;
int to_change = -1; int to_change = -1;
@ -174,35 +173,35 @@ static void update_defense_level(struct netns_ipvs *ipvs)
spin_lock(&ipvs->securetcp_lock); spin_lock(&ipvs->securetcp_lock);
switch (ipvs->sysctl_secure_tcp) { switch (ipvs->sysctl_secure_tcp) {
case 0: case 0:
if (old_secure_tcp >= 2) if (ipvs->old_secure_tcp >= 2)
to_change = 0; to_change = 0;
break; break;
case 1: case 1:
if (nomem) { if (nomem) {
if (old_secure_tcp < 2) if (ipvs->old_secure_tcp < 2)
to_change = 1; to_change = 1;
ipvs->sysctl_secure_tcp = 2; ipvs->sysctl_secure_tcp = 2;
} else { } else {
if (old_secure_tcp >= 2) if (ipvs->old_secure_tcp >= 2)
to_change = 0; to_change = 0;
} }
break; break;
case 2: case 2:
if (nomem) { if (nomem) {
if (old_secure_tcp < 2) if (ipvs->old_secure_tcp < 2)
to_change = 1; to_change = 1;
} else { } else {
if (old_secure_tcp >= 2) if (ipvs->old_secure_tcp >= 2)
to_change = 0; to_change = 0;
ipvs->sysctl_secure_tcp = 1; ipvs->sysctl_secure_tcp = 1;
} }
break; break;
case 3: case 3:
if (old_secure_tcp < 2) if (ipvs->old_secure_tcp < 2)
to_change = 1; to_change = 1;
break; break;
} }
old_secure_tcp = ipvs->sysctl_secure_tcp; ipvs->old_secure_tcp = ipvs->sysctl_secure_tcp;
if (to_change >= 0) if (to_change >= 0)
ip_vs_protocol_timeout_change(ipvs, ip_vs_protocol_timeout_change(ipvs,
ipvs->sysctl_secure_tcp > 1); ipvs->sysctl_secure_tcp > 1);