Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp

process_sdp() declares union nf_inet_addr rtp_addr on the stack and
passes it to the nf_nat_sip sdp_session hook after walking the SDP
media descriptions. However rtp_addr is only initialized inside the
media loop when a recognized media type with a non-zero port is found.

If the SDP body contains no m= lines, only inactive media sections
(m=audio 0 ...) or only unrecognized media types, rtp_addr is never
assigned. Despite that, the function still calls hooks->sdp_session()
with &rtp_addr, causing nf_nat_sdp_session() to format the stale stack
value as an IP address and rewrite the SDP session owner and connection
lines with it.

With CONFIG_INIT_STACK_ALL_ZERO (default on most distributions) this
results in the session-level o= and c= addresses being rewritten to
0.0.0.0 for inactive SDP sessions. Without stack auto-init the
rewritten address is whatever happened to be on the stack.

Fix this by pre-initializing rtp_addr from the session-level connection
address (caddr) when available, and tracking via a have_rtp_addr flag
whether any valid address was established. Skip the sdp_session hook
entirely when no valid address exists.

Fixes: 4ab9e64e5e3c ("[NETFILTER]: nf_nat_sip: split up SDP mangling")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Weiming Shi and committed by
Pablo Neira Ayuso
6a2b7244 3db56479

+10 -4
+10 -4
net/netfilter/nf_conntrack_sip.c
··· 1040 1040 unsigned int port; 1041 1041 const struct sdp_media_type *t; 1042 1042 int ret = NF_ACCEPT; 1043 + bool have_rtp_addr = false; 1043 1044 1044 1045 hooks = rcu_dereference(nf_nat_sip_hooks); 1045 1046 ··· 1057 1056 caddr_len = 0; 1058 1057 if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen, 1059 1058 SDP_HDR_CONNECTION, SDP_HDR_MEDIA, 1060 - &matchoff, &matchlen, &caddr) > 0) 1059 + &matchoff, &matchlen, &caddr) > 0) { 1061 1060 caddr_len = matchlen; 1061 + memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); 1062 + have_rtp_addr = true; 1063 + } 1062 1064 1063 1065 mediaoff = sdpoff; 1064 1066 for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { ··· 1095 1091 &matchoff, &matchlen, &maddr) > 0) { 1096 1092 maddr_len = matchlen; 1097 1093 memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); 1098 - } else if (caddr_len) 1094 + have_rtp_addr = true; 1095 + } else if (caddr_len) { 1099 1096 memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); 1100 - else { 1097 + have_rtp_addr = true; 1098 + } else { 1101 1099 nf_ct_helper_log(skb, ct, "cannot parse SDP message"); 1102 1100 return NF_DROP; 1103 1101 } ··· 1131 1125 1132 1126 /* Update session connection and owner addresses */ 1133 1127 hooks = rcu_dereference(nf_nat_sip_hooks); 1134 - if (hooks && ct->status & IPS_NAT_MASK) 1128 + if (hooks && ct->status & IPS_NAT_MASK && have_rtp_addr) 1135 1129 ret = hooks->sdp_session(skb, protoff, dataoff, 1136 1130 dptr, datalen, sdpoff, 1137 1131 &rtp_addr);