net: ipv4: Fix parsing of IPv4 header options

If IPv4 header options has wrong options length
(e.g. options length is more that actual data),
then parser decrements opts_len without checking
actual data length. Which crashes the network stack.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@linux.intel.com>
This commit is contained in:
Ravi kumar Veeramally 2020-02-28 14:30:59 +02:00 committed by Johan Hedberg
commit 22f93b35ab

View file

@ -116,7 +116,7 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
{ {
struct net_pkt_cursor cur; struct net_pkt_cursor cur;
u8_t opt_data[NET_IPV4_HDR_OPTNS_MAX_LEN]; u8_t opt_data[NET_IPV4_HDR_OPTNS_MAX_LEN];
u8_t opts_len; u8_t total_opts_len;
if (!cb) { if (!cb) {
return -EINVAL; return -EINVAL;
@ -129,9 +129,9 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
return -EINVAL; return -EINVAL;
} }
opts_len = net_pkt_ipv4_opts_len(pkt); total_opts_len = net_pkt_ipv4_opts_len(pkt);
while (opts_len) { while (total_opts_len) {
u8_t opt_len = 0U; u8_t opt_len = 0U;
u8_t opt_type; u8_t opt_type;
@ -139,7 +139,7 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
return -EINVAL; return -EINVAL;
} }
opts_len--; total_opts_len--;
if (!(opt_type == NET_IPV4_OPTS_EO || if (!(opt_type == NET_IPV4_OPTS_EO ||
opt_type == NET_IPV4_OPTS_NOP)) { opt_type == NET_IPV4_OPTS_NOP)) {
@ -147,11 +147,15 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
return -EINVAL; return -EINVAL;
} }
if (opt_len < 2U || total_opts_len < 1U) {
return -EINVAL;
}
opt_len -= 2U; opt_len -= 2U;
opts_len--; total_opts_len--;
} }
if (opt_len > opts_len) { if (opt_len > total_opts_len) {
return -EINVAL; return -EINVAL;
} }
@ -187,7 +191,7 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
break; break;
} }
opts_len -= opt_len; total_opts_len -= opt_len;
} }
net_pkt_cursor_restore(pkt, &cur); net_pkt_cursor_restore(pkt, &cur);
@ -239,6 +243,10 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt)
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr)); net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr));
opts_len = hdr_len - sizeof(struct net_ipv4_hdr); opts_len = hdr_len - sizeof(struct net_ipv4_hdr);
if (opts_len > NET_IPV4_HDR_OPTNS_MAX_LEN) {
return -EINVAL;
}
net_pkt_set_ipv4_opts_len(pkt, opts_len); net_pkt_set_ipv4_opts_len(pkt, opts_len);
pkt_len = ntohs(hdr->len); pkt_len = ntohs(hdr->len);