Commit ab58298c authored by Vegard Nossum's avatar Vegard Nossum Committed by David S. Miller

net: fix decnet rtnexthop parsing

dn_fib_count_nhs() could enter an infinite loop if nhp->rtnh_len == 0
(i.e. if userspace passes a malformed netlink message).

Let's use the helpers from net/nexthop.h which take care of all this
stuff. We can do exactly the same as e.g. fib_count_nexthops() and
fib_get_nhs() from net/ipv4/fib_semantics.c.

This fixes the softlockup for me.

Cc: Thomas Graf <tgraf@suug.ch>
Signed-off-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f5d65161
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <net/dn_fib.h> #include <net/dn_fib.h>
#include <net/dn_neigh.h> #include <net/dn_neigh.h>
#include <net/dn_dev.h> #include <net/dn_dev.h>
#include <net/nexthop.h>
#define RT_MIN_TABLE 1 #define RT_MIN_TABLE 1
...@@ -150,14 +151,13 @@ static int dn_fib_count_nhs(const struct nlattr *attr) ...@@ -150,14 +151,13 @@ static int dn_fib_count_nhs(const struct nlattr *attr)
struct rtnexthop *nhp = nla_data(attr); struct rtnexthop *nhp = nla_data(attr);
int nhs = 0, nhlen = nla_len(attr); int nhs = 0, nhlen = nla_len(attr);
while(nhlen >= (int)sizeof(struct rtnexthop)) { while (rtnh_ok(nhp, nhlen)) {
if ((nhlen -= nhp->rtnh_len) < 0)
return 0;
nhs++; nhs++;
nhp = RTNH_NEXT(nhp); nhp = rtnh_next(nhp, &nhlen);
} }
return nhs; /* leftover implies invalid nexthop configuration, discard it */
return nhlen > 0 ? 0 : nhs;
} }
static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr, static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
...@@ -167,21 +167,24 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr, ...@@ -167,21 +167,24 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
int nhlen = nla_len(attr); int nhlen = nla_len(attr);
change_nexthops(fi) { change_nexthops(fi) {
int attrlen = nhlen - sizeof(struct rtnexthop); int attrlen;
if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
if (!rtnh_ok(nhp, nhlen))
return -EINVAL; return -EINVAL;
nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags; nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
nh->nh_oif = nhp->rtnh_ifindex; nh->nh_oif = nhp->rtnh_ifindex;
nh->nh_weight = nhp->rtnh_hops + 1; nh->nh_weight = nhp->rtnh_hops + 1;
if (attrlen) { attrlen = rtnh_attrlen(nhp);
if (attrlen > 0) {
struct nlattr *gw_attr; struct nlattr *gw_attr;
gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY); gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0; nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;
} }
nhp = RTNH_NEXT(nhp);
nhp = rtnh_next(nhp, &nhlen);
} endfor_nexthops(fi); } endfor_nexthops(fi);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment