Sashiko reports for possible heap buffer overflow when generating sync message for the v0 and v1 message formats. We should read the cp->flags only once because another CPU can concurrently set the SEQ_MASK between the two reads. Note that IPVS does not set the SEQ_MASK anymore for the ip_vs_ftp.c helper starting from commit 7f1c40757951 ("IPVS: make FTP work with full NAT support") (2.6.36+), so the problem should not be reproducible. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260903004149.1037028-1-pablo%40netfilter.org Link: https://sashiko.dev/#/patchset/20260909111338.44357-1-ja%40ssi.bg Signed-off-by: Julian Anastasov --- net/netfilter/ipvs/ip_vs_sync.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 5383aeafb0ae..dfa8487ec0c2 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -543,13 +543,15 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, struct ip_vs_sync_conn_v0 *s; struct ip_vs_sync_buff *buff; struct ipvs_master_sync_state *ms; - int id; + u32 flags, seq_mask; unsigned int len; + int id; if (unlikely(cp->af != AF_INET)) return; + flags = READ_ONCE(cp->flags); /* Do not sync ONE PACKET */ - if (cp->flags & IP_VS_CONN_F_ONE_PACKET) + if (flags & IP_VS_CONN_F_ONE_PACKET) return; if (!ip_vs_sync_conn_needed(ipvs, cp, pkts)) @@ -564,8 +566,8 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, id = select_master_thread_id(ipvs, cp); ms = &ipvs->ms[id]; buff = ms->sync_buff; - len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE : - SIMPLE_CONN_SIZE; + seq_mask = flags & IP_VS_CONN_F_SEQ_MASK; + len = seq_mask ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE; if (buff) { m = (struct ip_vs_sync_mesg_v0 *) buff->mesg; /* Send buffer if it is for v1 */ @@ -597,9 +599,9 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, s->caddr = cp->caddr.ip; s->vaddr = cp->vaddr.ip; s->daddr = cp->daddr.ip; - s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); + s->flags = htons(flags & ~IP_VS_CONN_F_HASHED); s->state = htons(cp->state); - if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { + if (seq_mask) { struct ip_vs_sync_conn_options *opt = (struct ip_vs_sync_conn_options *)&s[1]; memcpy(opt, &cp->sync_conn_opt, sizeof(*opt)); @@ -635,6 +637,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts) int id; __u8 *p; unsigned int len, pe_name_len, pad; + u32 flags, seq_mask; /* Handle old version of the protocol */ if (sysctl_sync_ver(ipvs) == 0) { @@ -647,6 +650,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts) sloop: if (!ip_vs_sync_conn_needed(ipvs, cp, pkts)) goto control; + flags = READ_ONCE(cp->flags); /* Sanity checks */ pe_name_len = 0; @@ -674,7 +678,8 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts) #endif len = sizeof(struct ip_vs_sync_v4); - if (cp->flags & IP_VS_CONN_F_SEQ_MASK) + seq_mask = flags & IP_VS_CONN_F_SEQ_MASK; + if (seq_mask) len += sizeof(struct ip_vs_sync_conn_options) + 2; if (cp->pe_data_len) @@ -720,7 +725,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts) /* Set message type & copy members */ s->v4.type = (cp->af == AF_INET6 ? STYPE_F_INET6 : 0); s->v4.ver_size = htons(len & SVER_MASK); /* Version 0 */ - s->v4.flags = htonl(cp->flags & ~IP_VS_CONN_F_HASHED); + s->v4.flags = htonl(flags & ~IP_VS_CONN_F_HASHED); s->v4.state = htons(cp->state); s->v4.protocol = cp->protocol; s->v4.cport = cp->cport; @@ -744,7 +749,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts) s->v4.vaddr = cp->vaddr.ip; s->v4.daddr = cp->daddr.ip; } - if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { + if (seq_mask) { *(p++) = IPVS_OPT_SEQ_DATA; *(p++) = sizeof(struct ip_vs_sync_conn_options); hton_seq(&cp->in_seq, (struct ip_vs_seq *)p); -- 2.55.0