diff options
| author | Vegard Nossum <vegard.nossum@oracle.com> | 2016-07-05 21:12:53 +0200 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-07-05 14:08:47 -0700 | 
| commit | ab58298cf459fcd4f588a401d36abf0bd2215b51 (patch) | |
| tree | 58c77416b58a2b50ff681d1e3fde2444d17fdc98 /net | |
| parent | f5d6516120ee5c777fb7b1ba9d39031881ad511b (diff) | |
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: Vegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
| -rw-r--r-- | net/decnet/dn_fib.c | 21 | 
1 files changed, 12 insertions, 9 deletions
| diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index df4803437888..a796fc7cbc35 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -41,6 +41,7 @@  #include <net/dn_fib.h>  #include <net/dn_neigh.h>  #include <net/dn_dev.h> +#include <net/nexthop.h>  #define RT_MIN_TABLE 1 @@ -150,14 +151,13 @@ static int dn_fib_count_nhs(const struct nlattr *attr)  	struct rtnexthop *nhp = nla_data(attr);  	int nhs = 0, nhlen = nla_len(attr); -	while(nhlen >= (int)sizeof(struct rtnexthop)) { -		if ((nhlen -= nhp->rtnh_len) < 0) -			return 0; +	while (rtnh_ok(nhp, nhlen)) {  		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, @@ -167,21 +167,24 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,  	int nhlen = nla_len(attr);  	change_nexthops(fi) { -		int attrlen = nhlen - sizeof(struct rtnexthop); -		if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) +		int attrlen; + +		if (!rtnh_ok(nhp, nhlen))  			return -EINVAL;  		nh->nh_flags  = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;  		nh->nh_oif    = nhp->rtnh_ifindex;  		nh->nh_weight = nhp->rtnh_hops + 1; -		if (attrlen) { +		attrlen = rtnh_attrlen(nhp); +		if (attrlen > 0) {  			struct nlattr *gw_attr;  			gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);  			nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;  		} -		nhp = RTNH_NEXT(nhp); + +		nhp = rtnh_next(nhp, &nhlen);  	} endfor_nexthops(fi);  	return 0; | 
