* [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
@ 2021-10-12 8:43 Xin Long
2021-10-13 16:31 ` Jakub Kicinski
0 siblings, 1 reply; 5+ messages in thread
From: Xin Long @ 2021-10-12 8:43 UTC (permalink / raw)
To: network dev, davem, kuba; +Cc: Dan Carpenter, Andreas Roeseler
In icmp_build_probe(), the icmp_ext_echo_iio parsing should be done
step by step and skb_header_pointer() return value should always be
checked, this patch fixes 3 places in there:
- On case ICMP_EXT_ECHO_CTYPE_NAME, it should only copy ident.name
from skb by skb_header_pointer(), its len is ident_len. Besides,
the return value of skb_header_pointer() should always be checked.
- On case ICMP_EXT_ECHO_CTYPE_INDEX, move ident_len check ahead of
skb_header_pointer(), and also do the return value check for
skb_header_pointer().
- On case ICMP_EXT_ECHO_CTYPE_ADDR, before accessing iio->ident.addr.
ctype3_hdr.addrlen, skb_header_pointer() should be called first,
then check its return value and ident_len.
On subcases ICMP_AFI_IP and ICMP_AFI_IP6, also do check for ident.
addr.ctype3_hdr.addrlen and skb_header_pointer()'s return value.
On subcase ICMP_AFI_IP, the len for skb_header_pointer() should be
"sizeof(iio->extobj_hdr) + sizeof(iio->ident.addr.ctype3_hdr) +
sizeof(struct in_addr)" or "ident_len".
Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages")
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/ipv4/icmp.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 8b30cadff708..818c79266c48 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -1061,38 +1061,48 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
dev = NULL;
switch (iio->extobj_hdr.class_type) {
case ICMP_EXT_ECHO_CTYPE_NAME:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
if (ident_len >= IFNAMSIZ)
goto send_mal_query;
+ iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
+ ident_len, &_iio);
+ if (!iio)
+ goto send_mal_query;
memset(buff, 0, sizeof(buff));
memcpy(buff, &iio->ident.name, ident_len);
dev = dev_get_by_name(net, buff);
break;
case ICMP_EXT_ECHO_CTYPE_INDEX:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
- sizeof(iio->ident.ifindex), &_iio);
if (ident_len != sizeof(iio->ident.ifindex))
goto send_mal_query;
+ iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
+ ident_len, &_iio);
+ if (!iio)
+ goto send_mal_query;
dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
break;
case ICMP_EXT_ECHO_CTYPE_ADDR:
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
- iio->ident.addr.ctype3_hdr.addrlen)
+ iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
+ sizeof(iio->ident.addr.ctype3_hdr), &_iio);
+ if (!iio || ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
+ iio->ident.addr.ctype3_hdr.addrlen)
goto send_mal_query;
switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
case ICMP_AFI_IP:
+ if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
+ goto send_mal_query;
iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
- sizeof(struct in_addr), &_iio);
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
- sizeof(struct in_addr))
+ ident_len, &_iio);
+ if (!iio)
goto send_mal_query;
dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
break;
#if IS_ENABLED(CONFIG_IPV6)
case ICMP_AFI_IP6:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
- sizeof(struct in6_addr))
+ if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
+ goto send_mal_query;
+ iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
+ ident_len, &_iio);
+ if (!iio)
goto send_mal_query;
dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
dev_hold(dev);
--
2.27.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
2021-10-12 8:43 [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe Xin Long
@ 2021-10-13 16:31 ` Jakub Kicinski
2021-10-14 3:39 ` Xin Long
0 siblings, 1 reply; 5+ messages in thread
From: Jakub Kicinski @ 2021-10-13 16:31 UTC (permalink / raw)
To: Xin Long; +Cc: network dev, davem, Dan Carpenter, Andreas Roeseler
On Tue, 12 Oct 2021 04:43:07 -0400 Xin Long wrote:
> In icmp_build_probe(), the icmp_ext_echo_iio parsing should be done
> step by step and skb_header_pointer() return value should always be
> checked, this patch fixes 3 places in there:
>
> - On case ICMP_EXT_ECHO_CTYPE_NAME, it should only copy ident.name
> from skb by skb_header_pointer(), its len is ident_len. Besides,
> the return value of skb_header_pointer() should always be checked.
>
> - On case ICMP_EXT_ECHO_CTYPE_INDEX, move ident_len check ahead of
> skb_header_pointer(), and also do the return value check for
> skb_header_pointer().
>
> - On case ICMP_EXT_ECHO_CTYPE_ADDR, before accessing iio->ident.addr.
> ctype3_hdr.addrlen, skb_header_pointer() should be called first,
> then check its return value and ident_len.
> On subcases ICMP_AFI_IP and ICMP_AFI_IP6, also do check for ident.
> addr.ctype3_hdr.addrlen and skb_header_pointer()'s return value.
> On subcase ICMP_AFI_IP, the len for skb_header_pointer() should be
> "sizeof(iio->extobj_hdr) + sizeof(iio->ident.addr.ctype3_hdr) +
> sizeof(struct in_addr)" or "ident_len".
>
> Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages")
> Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> index 8b30cadff708..818c79266c48 100644
> --- a/net/ipv4/icmp.c
> +++ b/net/ipv4/icmp.c
> @@ -1061,38 +1061,48 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> dev = NULL;
> switch (iio->extobj_hdr.class_type) {
> case ICMP_EXT_ECHO_CTYPE_NAME:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> if (ident_len >= IFNAMSIZ)
> goto send_mal_query;
> + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> + ident_len, &_iio);
> + if (!iio)
> + goto send_mal_query;
> memset(buff, 0, sizeof(buff));
> memcpy(buff, &iio->ident.name, ident_len);
> dev = dev_get_by_name(net, buff);
> break;
> case ICMP_EXT_ECHO_CTYPE_INDEX:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> - sizeof(iio->ident.ifindex), &_iio);
> if (ident_len != sizeof(iio->ident.ifindex))
> goto send_mal_query;
> + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> + ident_len, &_iio);
> + if (!iio)
> + goto send_mal_query;
> dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> break;
> case ICMP_EXT_ECHO_CTYPE_ADDR:
> - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> - iio->ident.addr.ctype3_hdr.addrlen)
> + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> + sizeof(iio->ident.addr.ctype3_hdr), &_iio);
> + if (!iio || ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> + iio->ident.addr.ctype3_hdr.addrlen)
> goto send_mal_query;
> switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> case ICMP_AFI_IP:
> + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> + goto send_mal_query;
> iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> - sizeof(struct in_addr), &_iio);
> - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> - sizeof(struct in_addr))
> + ident_len, &_iio);
> + if (!iio)
> goto send_mal_query;
> dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> break;
> #if IS_ENABLED(CONFIG_IPV6)
> case ICMP_AFI_IP6:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> - sizeof(struct in6_addr))
> + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> + goto send_mal_query;
> + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> + ident_len, &_iio);
> + if (!iio)
> goto send_mal_query;
> dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> dev_hold(dev);
If I'm reading this right we end up with the same skb_header_pointer
call 4 times, every path ends up calling:
skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) + ident_len, &_iio);
and looks like the skb does not get modified in between so these calls
are equivalent.
So why don't we instead consolidate the paths:
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 8b30cadff708..efa2ec1a85bf 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -1057,11 +1057,15 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
if (ntohs(iio->extobj_hdr.length) <= sizeof(iio->extobj_hdr))
goto send_mal_query;
ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
+ iio = skb_header_pointer(skb, sizeof(_ext_hdr),
+ sizeof(iio->extobj_hdr) + ident_len, &_iio);
+ if (!iio)
+ goto send_mal_query;
+
status = 0;
dev = NULL;
switch (iio->extobj_hdr.class_type) {
case ICMP_EXT_ECHO_CTYPE_NAME:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
if (ident_len >= IFNAMSIZ)
goto send_mal_query;
memset(buff, 0, sizeof(buff));
@@ -1069,8 +1073,6 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
dev = dev_get_by_name(net, buff);
break;
case ICMP_EXT_ECHO_CTYPE_INDEX:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
- sizeof(iio->ident.ifindex), &_iio);
if (ident_len != sizeof(iio->ident.ifindex))
goto send_mal_query;
dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
@@ -1081,18 +1083,13 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
goto send_mal_query;
switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
case ICMP_AFI_IP:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
- sizeof(struct in_addr), &_iio);
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
- sizeof(struct in_addr))
+ if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
goto send_mal_query;
dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
break;
#if IS_ENABLED(CONFIG_IPV6)
case ICMP_AFI_IP6:
- iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
- sizeof(struct in6_addr))
+ if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
goto send_mal_query;
dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
dev_hold(dev);
--
2.31.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
2021-10-13 16:31 ` Jakub Kicinski
@ 2021-10-14 3:39 ` Xin Long
2021-10-14 3:59 ` Xin Long
0 siblings, 1 reply; 5+ messages in thread
From: Xin Long @ 2021-10-14 3:39 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: network dev, davem, Dan Carpenter, Andreas Roeseler
On Thu, Oct 14, 2021 at 12:31 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 12 Oct 2021 04:43:07 -0400 Xin Long wrote:
> > In icmp_build_probe(), the icmp_ext_echo_iio parsing should be done
> > step by step and skb_header_pointer() return value should always be
> > checked, this patch fixes 3 places in there:
> >
> > - On case ICMP_EXT_ECHO_CTYPE_NAME, it should only copy ident.name
> > from skb by skb_header_pointer(), its len is ident_len. Besides,
> > the return value of skb_header_pointer() should always be checked.
> >
> > - On case ICMP_EXT_ECHO_CTYPE_INDEX, move ident_len check ahead of
> > skb_header_pointer(), and also do the return value check for
> > skb_header_pointer().
> >
> > - On case ICMP_EXT_ECHO_CTYPE_ADDR, before accessing iio->ident.addr.
> > ctype3_hdr.addrlen, skb_header_pointer() should be called first,
> > then check its return value and ident_len.
> > On subcases ICMP_AFI_IP and ICMP_AFI_IP6, also do check for ident.
> > addr.ctype3_hdr.addrlen and skb_header_pointer()'s return value.
> > On subcase ICMP_AFI_IP, the len for skb_header_pointer() should be
> > "sizeof(iio->extobj_hdr) + sizeof(iio->ident.addr.ctype3_hdr) +
> > sizeof(struct in_addr)" or "ident_len".
> >
> > Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages")
> > Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
> > Signed-off-by: Xin Long <lucien.xin@gmail.com>
>
> > diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> > index 8b30cadff708..818c79266c48 100644
> > --- a/net/ipv4/icmp.c
> > +++ b/net/ipv4/icmp.c
> > @@ -1061,38 +1061,48 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > dev = NULL;
> > switch (iio->extobj_hdr.class_type) {
> > case ICMP_EXT_ECHO_CTYPE_NAME:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > if (ident_len >= IFNAMSIZ)
> > goto send_mal_query;
> > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > + ident_len, &_iio);
> > + if (!iio)
> > + goto send_mal_query;
> > memset(buff, 0, sizeof(buff));
> > memcpy(buff, &iio->ident.name, ident_len);
> > dev = dev_get_by_name(net, buff);
> > break;
> > case ICMP_EXT_ECHO_CTYPE_INDEX:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > - sizeof(iio->ident.ifindex), &_iio);
> > if (ident_len != sizeof(iio->ident.ifindex))
> > goto send_mal_query;
> > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > + ident_len, &_iio);
> > + if (!iio)
> > + goto send_mal_query;
> > dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> > break;
> > case ICMP_EXT_ECHO_CTYPE_ADDR:
> > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > - iio->ident.addr.ctype3_hdr.addrlen)
> > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > + sizeof(iio->ident.addr.ctype3_hdr), &_iio);
> > + if (!iio || ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > + iio->ident.addr.ctype3_hdr.addrlen)
> > goto send_mal_query;
> > switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> > case ICMP_AFI_IP:
> > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> > + goto send_mal_query;
> > iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > - sizeof(struct in_addr), &_iio);
> > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > - sizeof(struct in_addr))
> > + ident_len, &_iio);
> > + if (!iio)
> > goto send_mal_query;
> > dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> > break;
> > #if IS_ENABLED(CONFIG_IPV6)
> > case ICMP_AFI_IP6:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > - sizeof(struct in6_addr))
> > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> > + goto send_mal_query;
> > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > + ident_len, &_iio);
> > + if (!iio)
> > goto send_mal_query;
> > dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> > dev_hold(dev);
>
> If I'm reading this right we end up with the same skb_header_pointer
> call 4 times, every path ends up calling:
>
> skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) + ident_len, &_iio);
>
> and looks like the skb does not get modified in between so these calls
> are equivalent.
This looks much clearer.
I was worrying that ident_len might be too big, and ident_len should be verified
before calling skb_header_pointer(ident_len).
But it should be okay as skb_header_pointer() will return NULL if ident_len is
too big.
Will post v2 with the suggestion, thanks!
>
> So why don't we instead consolidate the paths:
>
> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> index 8b30cadff708..efa2ec1a85bf 100644
> --- a/net/ipv4/icmp.c
> +++ b/net/ipv4/icmp.c
> @@ -1057,11 +1057,15 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> if (ntohs(iio->extobj_hdr.length) <= sizeof(iio->extobj_hdr))
> goto send_mal_query;
> ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
> + iio = skb_header_pointer(skb, sizeof(_ext_hdr),
> + sizeof(iio->extobj_hdr) + ident_len, &_iio);
> + if (!iio)
> + goto send_mal_query;
> +
> status = 0;
> dev = NULL;
> switch (iio->extobj_hdr.class_type) {
> case ICMP_EXT_ECHO_CTYPE_NAME:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> if (ident_len >= IFNAMSIZ)
> goto send_mal_query;
> memset(buff, 0, sizeof(buff));
> @@ -1069,8 +1073,6 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> dev = dev_get_by_name(net, buff);
> break;
> case ICMP_EXT_ECHO_CTYPE_INDEX:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> - sizeof(iio->ident.ifindex), &_iio);
> if (ident_len != sizeof(iio->ident.ifindex))
> goto send_mal_query;
> dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> @@ -1081,18 +1083,13 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> goto send_mal_query;
> switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> case ICMP_AFI_IP:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> - sizeof(struct in_addr), &_iio);
> - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> - sizeof(struct in_addr))
> + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> goto send_mal_query;
> dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> break;
> #if IS_ENABLED(CONFIG_IPV6)
> case ICMP_AFI_IP6:
> - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> - sizeof(struct in6_addr))
> + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> goto send_mal_query;
> dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> dev_hold(dev);
> --
> 2.31.1
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
2021-10-14 3:39 ` Xin Long
@ 2021-10-14 3:59 ` Xin Long
2021-10-14 4:07 ` Xin Long
0 siblings, 1 reply; 5+ messages in thread
From: Xin Long @ 2021-10-14 3:59 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: network dev, davem, Dan Carpenter, Andreas Roeseler
On Thu, Oct 14, 2021 at 11:39 AM Xin Long <lucien.xin@gmail.com> wrote:
>
> On Thu, Oct 14, 2021 at 12:31 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Tue, 12 Oct 2021 04:43:07 -0400 Xin Long wrote:
> > > In icmp_build_probe(), the icmp_ext_echo_iio parsing should be done
> > > step by step and skb_header_pointer() return value should always be
> > > checked, this patch fixes 3 places in there:
> > >
> > > - On case ICMP_EXT_ECHO_CTYPE_NAME, it should only copy ident.name
> > > from skb by skb_header_pointer(), its len is ident_len. Besides,
> > > the return value of skb_header_pointer() should always be checked.
> > >
> > > - On case ICMP_EXT_ECHO_CTYPE_INDEX, move ident_len check ahead of
> > > skb_header_pointer(), and also do the return value check for
> > > skb_header_pointer().
> > >
> > > - On case ICMP_EXT_ECHO_CTYPE_ADDR, before accessing iio->ident.addr.
> > > ctype3_hdr.addrlen, skb_header_pointer() should be called first,
> > > then check its return value and ident_len.
> > > On subcases ICMP_AFI_IP and ICMP_AFI_IP6, also do check for ident.
> > > addr.ctype3_hdr.addrlen and skb_header_pointer()'s return value.
> > > On subcase ICMP_AFI_IP, the len for skb_header_pointer() should be
> > > "sizeof(iio->extobj_hdr) + sizeof(iio->ident.addr.ctype3_hdr) +
> > > sizeof(struct in_addr)" or "ident_len".
> > >
> > > Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages")
> > > Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
> > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> >
> > > diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> > > index 8b30cadff708..818c79266c48 100644
> > > --- a/net/ipv4/icmp.c
> > > +++ b/net/ipv4/icmp.c
> > > @@ -1061,38 +1061,48 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > > dev = NULL;
> > > switch (iio->extobj_hdr.class_type) {
> > > case ICMP_EXT_ECHO_CTYPE_NAME:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > if (ident_len >= IFNAMSIZ)
> > > goto send_mal_query;
> > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > + ident_len, &_iio);
> > > + if (!iio)
> > > + goto send_mal_query;
> > > memset(buff, 0, sizeof(buff));
> > > memcpy(buff, &iio->ident.name, ident_len);
> > > dev = dev_get_by_name(net, buff);
> > > break;
> > > case ICMP_EXT_ECHO_CTYPE_INDEX:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > - sizeof(iio->ident.ifindex), &_iio);
> > > if (ident_len != sizeof(iio->ident.ifindex))
> > > goto send_mal_query;
> > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > + ident_len, &_iio);
> > > + if (!iio)
> > > + goto send_mal_query;
> > > dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> > > break;
> > > case ICMP_EXT_ECHO_CTYPE_ADDR:
> > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > - iio->ident.addr.ctype3_hdr.addrlen)
> > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > + sizeof(iio->ident.addr.ctype3_hdr), &_iio);
> > > + if (!iio || ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > + iio->ident.addr.ctype3_hdr.addrlen)
> > > goto send_mal_query;
> > > switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> > > case ICMP_AFI_IP:
> > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> > > + goto send_mal_query;
> > > iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > - sizeof(struct in_addr), &_iio);
> > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > - sizeof(struct in_addr))
> > > + ident_len, &_iio);
> > > + if (!iio)
> > > goto send_mal_query;
> > > dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> > > break;
> > > #if IS_ENABLED(CONFIG_IPV6)
> > > case ICMP_AFI_IP6:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > - sizeof(struct in6_addr))
> > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> > > + goto send_mal_query;
> > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > + ident_len, &_iio);
> > > + if (!iio)
> > > goto send_mal_query;
> > > dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> > > dev_hold(dev);
> >
> > If I'm reading this right we end up with the same skb_header_pointer
> > call 4 times, every path ends up calling:
> >
> > skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) + ident_len, &_iio);
> >
> > and looks like the skb does not get modified in between so these calls
> > are equivalent.
> This looks much clearer.
>
> I was worrying that ident_len might be too big, and ident_len should be verified
> before calling skb_header_pointer(ident_len).
> But it should be okay as skb_header_pointer() will return NULL if ident_len is
> too big.
>
> Will post v2 with the suggestion, thanks!
>
> >
> > So why don't we instead consolidate the paths:
> >
> > diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> > index 8b30cadff708..efa2ec1a85bf 100644
> > --- a/net/ipv4/icmp.c
> > +++ b/net/ipv4/icmp.c
> > @@ -1057,11 +1057,15 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > if (ntohs(iio->extobj_hdr.length) <= sizeof(iio->extobj_hdr))
> > goto send_mal_query;
> > ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
> > + iio = skb_header_pointer(skb, sizeof(_ext_hdr),
> > + sizeof(iio->extobj_hdr) + ident_len, &_iio);
> > + if (!iio)
> > + goto send_mal_query;
> > +
[a]
> > status = 0;
> > dev = NULL;
> > switch (iio->extobj_hdr.class_type) {
> > case ICMP_EXT_ECHO_CTYPE_NAME:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > if (ident_len >= IFNAMSIZ)
> > goto send_mal_query;
> > memset(buff, 0, sizeof(buff));
> > @@ -1069,8 +1073,6 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > dev = dev_get_by_name(net, buff);
> > break;
> > case ICMP_EXT_ECHO_CTYPE_INDEX:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > - sizeof(iio->ident.ifindex), &_iio);
> > if (ident_len != sizeof(iio->ident.ifindex))
> > goto send_mal_query;
> > dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> > @@ -1081,18 +1083,13 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > goto send_mal_query;
But, here is another check:
if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
iio->ident.addr.ctype3_hdr.addrlen)
goto send_mal_query;
Thinking if ident_len < sizeof(iio->ident.addr.ctype3_hdr),
iio->ident.addr.ctype3_hdr.addrlen
access could be overflow. So we may do: if (ident_len <
sizeof(iio->ident.addr.ctype3_hdr)) check
before calling skb_header_pointer() in the place [a] above, like:
ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
if (ident_len < 4) /* 4 is the minimal size for any class_type */
goto send_mal_query;
iio = skb_header_pointer(skb, sizeof(_ext_hdr),
sizeof(iio->extobj_hdr) + ident_len, &_iio);
if (!iio)
goto send_mal_query;
what do you think?
> > switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> > case ICMP_AFI_IP:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > - sizeof(struct in_addr), &_iio);
> > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > - sizeof(struct in_addr))
> > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> > goto send_mal_query;
> > dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> > break;
> > #if IS_ENABLED(CONFIG_IPV6)
> > case ICMP_AFI_IP6:
> > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > - sizeof(struct in6_addr))
> > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> > goto send_mal_query;
> > dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> > dev_hold(dev);
> > --
> > 2.31.1
> >
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
2021-10-14 3:59 ` Xin Long
@ 2021-10-14 4:07 ` Xin Long
0 siblings, 0 replies; 5+ messages in thread
From: Xin Long @ 2021-10-14 4:07 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: network dev, davem, Dan Carpenter, Andreas Roeseler
On Thu, Oct 14, 2021 at 11:59 AM Xin Long <lucien.xin@gmail.com> wrote:
>
> On Thu, Oct 14, 2021 at 11:39 AM Xin Long <lucien.xin@gmail.com> wrote:
> >
> > On Thu, Oct 14, 2021 at 12:31 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > >
> > > On Tue, 12 Oct 2021 04:43:07 -0400 Xin Long wrote:
> > > > In icmp_build_probe(), the icmp_ext_echo_iio parsing should be done
> > > > step by step and skb_header_pointer() return value should always be
> > > > checked, this patch fixes 3 places in there:
> > > >
> > > > - On case ICMP_EXT_ECHO_CTYPE_NAME, it should only copy ident.name
> > > > from skb by skb_header_pointer(), its len is ident_len. Besides,
> > > > the return value of skb_header_pointer() should always be checked.
> > > >
> > > > - On case ICMP_EXT_ECHO_CTYPE_INDEX, move ident_len check ahead of
> > > > skb_header_pointer(), and also do the return value check for
> > > > skb_header_pointer().
> > > >
> > > > - On case ICMP_EXT_ECHO_CTYPE_ADDR, before accessing iio->ident.addr.
> > > > ctype3_hdr.addrlen, skb_header_pointer() should be called first,
> > > > then check its return value and ident_len.
> > > > On subcases ICMP_AFI_IP and ICMP_AFI_IP6, also do check for ident.
> > > > addr.ctype3_hdr.addrlen and skb_header_pointer()'s return value.
> > > > On subcase ICMP_AFI_IP, the len for skb_header_pointer() should be
> > > > "sizeof(iio->extobj_hdr) + sizeof(iio->ident.addr.ctype3_hdr) +
> > > > sizeof(struct in_addr)" or "ident_len".
> > > >
> > > > Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages")
> > > > Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
> > > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > >
> > > > diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> > > > index 8b30cadff708..818c79266c48 100644
> > > > --- a/net/ipv4/icmp.c
> > > > +++ b/net/ipv4/icmp.c
> > > > @@ -1061,38 +1061,48 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > > > dev = NULL;
> > > > switch (iio->extobj_hdr.class_type) {
> > > > case ICMP_EXT_ECHO_CTYPE_NAME:
> > > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > > if (ident_len >= IFNAMSIZ)
> > > > goto send_mal_query;
> > > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > + ident_len, &_iio);
> > > > + if (!iio)
> > > > + goto send_mal_query;
> > > > memset(buff, 0, sizeof(buff));
> > > > memcpy(buff, &iio->ident.name, ident_len);
> > > > dev = dev_get_by_name(net, buff);
> > > > break;
> > > > case ICMP_EXT_ECHO_CTYPE_INDEX:
> > > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > - sizeof(iio->ident.ifindex), &_iio);
> > > > if (ident_len != sizeof(iio->ident.ifindex))
> > > > goto send_mal_query;
> > > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > + ident_len, &_iio);
> > > > + if (!iio)
> > > > + goto send_mal_query;
> > > > dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> > > > break;
> > > > case ICMP_EXT_ECHO_CTYPE_ADDR:
> > > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > > - iio->ident.addr.ctype3_hdr.addrlen)
> > > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > + sizeof(iio->ident.addr.ctype3_hdr), &_iio);
> > > > + if (!iio || ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > > + iio->ident.addr.ctype3_hdr.addrlen)
> > > > goto send_mal_query;
> > > > switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> > > > case ICMP_AFI_IP:
> > > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> > > > + goto send_mal_query;
> > > > iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > - sizeof(struct in_addr), &_iio);
> > > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > > - sizeof(struct in_addr))
> > > > + ident_len, &_iio);
> > > > + if (!iio)
> > > > goto send_mal_query;
> > > > dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> > > > break;
> > > > #if IS_ENABLED(CONFIG_IPV6)
> > > > case ICMP_AFI_IP6:
> > > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > > - sizeof(struct in6_addr))
> > > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> > > > + goto send_mal_query;
> > > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > > + ident_len, &_iio);
> > > > + if (!iio)
> > > > goto send_mal_query;
> > > > dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> > > > dev_hold(dev);
> > >
> > > If I'm reading this right we end up with the same skb_header_pointer
> > > call 4 times, every path ends up calling:
> > >
> > > skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) + ident_len, &_iio);
> > >
> > > and looks like the skb does not get modified in between so these calls
> > > are equivalent.
> > This looks much clearer.
> >
> > I was worrying that ident_len might be too big, and ident_len should be verified
> > before calling skb_header_pointer(ident_len).
> > But it should be okay as skb_header_pointer() will return NULL if ident_len is
> > too big.
> >
> > Will post v2 with the suggestion, thanks!
> >
> > >
> > > So why don't we instead consolidate the paths:
> > >
> > > diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> > > index 8b30cadff708..efa2ec1a85bf 100644
> > > --- a/net/ipv4/icmp.c
> > > +++ b/net/ipv4/icmp.c
> > > @@ -1057,11 +1057,15 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > > if (ntohs(iio->extobj_hdr.length) <= sizeof(iio->extobj_hdr))
> > > goto send_mal_query;
> > > ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
> > > + iio = skb_header_pointer(skb, sizeof(_ext_hdr),
> > > + sizeof(iio->extobj_hdr) + ident_len, &_iio);
> > > + if (!iio)
> > > + goto send_mal_query;
> > > +
> [a]
>
> > > status = 0;
> > > dev = NULL;
> > > switch (iio->extobj_hdr.class_type) {
> > > case ICMP_EXT_ECHO_CTYPE_NAME:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > if (ident_len >= IFNAMSIZ)
> > > goto send_mal_query;
> > > memset(buff, 0, sizeof(buff));
> > > @@ -1069,8 +1073,6 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > > dev = dev_get_by_name(net, buff);
> > > break;
> > > case ICMP_EXT_ECHO_CTYPE_INDEX:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > - sizeof(iio->ident.ifindex), &_iio);
> > > if (ident_len != sizeof(iio->ident.ifindex))
> > > goto send_mal_query;
> > > dev = dev_get_by_index(net, ntohl(iio->ident.ifindex));
> > > @@ -1081,18 +1083,13 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
> > > goto send_mal_query;
> But, here is another check:
> if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> iio->ident.addr.ctype3_hdr.addrlen)
> goto send_mal_query;
>
> Thinking if ident_len < sizeof(iio->ident.addr.ctype3_hdr),
> iio->ident.addr.ctype3_hdr.addrlen
> access could be overflow. So we may do: if (ident_len <
> sizeof(iio->ident.addr.ctype3_hdr)) check
> before calling skb_header_pointer() in the place [a] above, like:
>
> ident_len = ntohs(iio->extobj_hdr.length) - sizeof(iio->extobj_hdr);
> if (ident_len < 4) /* 4 is the minimal size for any class_type */
> goto send_mal_query;
> iio = skb_header_pointer(skb, sizeof(_ext_hdr),
> sizeof(iio->extobj_hdr) + ident_len, &_iio);
> if (!iio)
> goto send_mal_query;
>
> what do you think?
Sorry, seems doing the check,
case ICMP_EXT_ECHO_CTYPE_ADDR:
- if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
+ if (ident_len < sizeof(iio->ident.addr.ctype3_hdr) ||
+ ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
iio->ident.addr.ctype3_hdr.addrlen)
goto send_mal_query;
right here makes more sense.
>
> > > switch (ntohs(iio->ident.addr.ctype3_hdr.afi)) {
> > > case ICMP_AFI_IP:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(iio->extobj_hdr) +
> > > - sizeof(struct in_addr), &_iio);
> > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > - sizeof(struct in_addr))
> > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in_addr))
> > > goto send_mal_query;
> > > dev = ip_dev_find(net, iio->ident.addr.ip_addr.ipv4_addr);
> > > break;
> > > #if IS_ENABLED(CONFIG_IPV6)
> > > case ICMP_AFI_IP6:
> > > - iio = skb_header_pointer(skb, sizeof(_ext_hdr), sizeof(_iio), &_iio);
> > > - if (ident_len != sizeof(iio->ident.addr.ctype3_hdr) +
> > > - sizeof(struct in6_addr))
> > > + if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr))
> > > goto send_mal_query;
> > > dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev);
> > > dev_hold(dev);
> > > --
> > > 2.31.1
> > >
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-10-14 4:08 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-12 8:43 [PATCH net] icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe Xin Long
2021-10-13 16:31 ` Jakub Kicinski
2021-10-14 3:39 ` Xin Long
2021-10-14 3:59 ` Xin Long
2021-10-14 4:07 ` Xin Long
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.