diff options
author | Julian Anastasov <ja@ssi.bg> | 2005-09-08 16:34:47 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-09-08 16:34:47 -0400 |
commit | ce723d8e048ef98ea64d12379e3921c933f5b3e0 (patch) | |
tree | 368e85ac0014d203bba15bb8777ffbd94dc2d2e6 | |
parent | c921e4c4dbb043f9435414c4e661e7f0a783053d (diff) |
[IPV4]: Fix refcount damaging in net/ipv4/route.c
One such place that can damage the dst refcnts is route.c with
CONFIG_IP_ROUTE_MULTIPATH_CACHED enabled, i don't see the user's
.config. In this new code i see that rt_intern_hash is called before
dst->refcnt is set to 1, dst is the 2nd arg to rt_intern_hash.
Arg 2 of rt_intern_hash must come with refcnt 1 as it is added to
table or dropped depending on error/add/update. One such example is
ip_mkroute_input where __mkroute_input return rth with refcnt 0 which
is provided to rt_intern_hash. ip_mkroute_output looks like a 2nd such
place. Appending untested patch for comments and review. The idea is
to put previous reference as we are going to return next result/error.
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/route.c | 29 |
1 files changed, 14 insertions, 15 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 8c0b14e3beec..8549f26e2495 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1760,6 +1760,7 @@ static inline int __mkroute_input(struct sk_buff *skb, | |||
1760 | goto cleanup; | 1760 | goto cleanup; |
1761 | } | 1761 | } |
1762 | 1762 | ||
1763 | atomic_set(&rth->u.dst.__refcnt, 1); | ||
1763 | rth->u.dst.flags= DST_HOST; | 1764 | rth->u.dst.flags= DST_HOST; |
1764 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | 1765 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED |
1765 | if (res->fi->fib_nhs > 1) | 1766 | if (res->fi->fib_nhs > 1) |
@@ -1820,7 +1821,6 @@ static inline int ip_mkroute_input_def(struct sk_buff *skb, | |||
1820 | err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); | 1821 | err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); |
1821 | if (err) | 1822 | if (err) |
1822 | return err; | 1823 | return err; |
1823 | atomic_set(&rth->u.dst.__refcnt, 1); | ||
1824 | 1824 | ||
1825 | /* put it into the cache */ | 1825 | /* put it into the cache */ |
1826 | hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); | 1826 | hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); |
@@ -1834,8 +1834,8 @@ static inline int ip_mkroute_input(struct sk_buff *skb, | |||
1834 | u32 daddr, u32 saddr, u32 tos) | 1834 | u32 daddr, u32 saddr, u32 tos) |
1835 | { | 1835 | { |
1836 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | 1836 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED |
1837 | struct rtable* rth = NULL; | 1837 | struct rtable* rth = NULL, *rtres; |
1838 | unsigned char hop, hopcount, lasthop; | 1838 | unsigned char hop, hopcount; |
1839 | int err = -EINVAL; | 1839 | int err = -EINVAL; |
1840 | unsigned int hash; | 1840 | unsigned int hash; |
1841 | 1841 | ||
@@ -1844,8 +1844,6 @@ static inline int ip_mkroute_input(struct sk_buff *skb, | |||
1844 | else | 1844 | else |
1845 | hopcount = 1; | 1845 | hopcount = 1; |
1846 | 1846 | ||
1847 | lasthop = hopcount - 1; | ||
1848 | |||
1849 | /* distinguish between multipath and singlepath */ | 1847 | /* distinguish between multipath and singlepath */ |
1850 | if (hopcount < 2) | 1848 | if (hopcount < 2) |
1851 | return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, | 1849 | return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, |
@@ -1855,6 +1853,10 @@ static inline int ip_mkroute_input(struct sk_buff *skb, | |||
1855 | for (hop = 0; hop < hopcount; hop++) { | 1853 | for (hop = 0; hop < hopcount; hop++) { |
1856 | res->nh_sel = hop; | 1854 | res->nh_sel = hop; |
1857 | 1855 | ||
1856 | /* put reference to previous result */ | ||
1857 | if (hop) | ||
1858 | ip_rt_put(rtres); | ||
1859 | |||
1858 | /* create a routing cache entry */ | 1860 | /* create a routing cache entry */ |
1859 | err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, | 1861 | err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, |
1860 | &rth); | 1862 | &rth); |
@@ -1863,7 +1865,7 @@ static inline int ip_mkroute_input(struct sk_buff *skb, | |||
1863 | 1865 | ||
1864 | /* put it into the cache */ | 1866 | /* put it into the cache */ |
1865 | hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); | 1867 | hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); |
1866 | err = rt_intern_hash(hash, rth, (struct rtable**)&skb->dst); | 1868 | err = rt_intern_hash(hash, rth, &rtres); |
1867 | if (err) | 1869 | if (err) |
1868 | return err; | 1870 | return err; |
1869 | 1871 | ||
@@ -1873,13 +1875,8 @@ static inline int ip_mkroute_input(struct sk_buff *skb, | |||
1873 | FIB_RES_NETMASK(*res), | 1875 | FIB_RES_NETMASK(*res), |
1874 | res->prefixlen, | 1876 | res->prefixlen, |
1875 | &FIB_RES_NH(*res)); | 1877 | &FIB_RES_NH(*res)); |
1876 | |||
1877 | /* only for the last hop the reference count is handled | ||
1878 | * outside | ||
1879 | */ | ||
1880 | if (hop == lasthop) | ||
1881 | atomic_set(&(skb->dst->__refcnt), 1); | ||
1882 | } | 1878 | } |
1879 | skb->dst = &rtres->u.dst; | ||
1883 | return err; | 1880 | return err; |
1884 | #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ | 1881 | #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ |
1885 | return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos); | 1882 | return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos); |
@@ -2208,6 +2205,7 @@ static inline int __mkroute_output(struct rtable **result, | |||
2208 | goto cleanup; | 2205 | goto cleanup; |
2209 | } | 2206 | } |
2210 | 2207 | ||
2208 | atomic_set(&rth->u.dst.__refcnt, 1); | ||
2211 | rth->u.dst.flags= DST_HOST; | 2209 | rth->u.dst.flags= DST_HOST; |
2212 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | 2210 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED |
2213 | if (res->fi) { | 2211 | if (res->fi) { |
@@ -2290,8 +2288,6 @@ static inline int ip_mkroute_output_def(struct rtable **rp, | |||
2290 | if (err == 0) { | 2288 | if (err == 0) { |
2291 | u32 tos = RT_FL_TOS(oldflp); | 2289 | u32 tos = RT_FL_TOS(oldflp); |
2292 | 2290 | ||
2293 | atomic_set(&rth->u.dst.__refcnt, 1); | ||
2294 | |||
2295 | hash = rt_hash_code(oldflp->fl4_dst, | 2291 | hash = rt_hash_code(oldflp->fl4_dst, |
2296 | oldflp->fl4_src ^ (oldflp->oif << 5), tos); | 2292 | oldflp->fl4_src ^ (oldflp->oif << 5), tos); |
2297 | err = rt_intern_hash(hash, rth, rp); | 2293 | err = rt_intern_hash(hash, rth, rp); |
@@ -2326,6 +2322,10 @@ static inline int ip_mkroute_output(struct rtable** rp, | |||
2326 | dev2nexthop = FIB_RES_DEV(*res); | 2322 | dev2nexthop = FIB_RES_DEV(*res); |
2327 | dev_hold(dev2nexthop); | 2323 | dev_hold(dev2nexthop); |
2328 | 2324 | ||
2325 | /* put reference to previous result */ | ||
2326 | if (hop) | ||
2327 | ip_rt_put(*rp); | ||
2328 | |||
2329 | err = __mkroute_output(&rth, res, fl, oldflp, | 2329 | err = __mkroute_output(&rth, res, fl, oldflp, |
2330 | dev2nexthop, flags); | 2330 | dev2nexthop, flags); |
2331 | 2331 | ||
@@ -2350,7 +2350,6 @@ static inline int ip_mkroute_output(struct rtable** rp, | |||
2350 | if (err != 0) | 2350 | if (err != 0) |
2351 | return err; | 2351 | return err; |
2352 | } | 2352 | } |
2353 | atomic_set(&(*rp)->u.dst.__refcnt, 1); | ||
2354 | return err; | 2353 | return err; |
2355 | } else { | 2354 | } else { |
2356 | return ip_mkroute_output_def(rp, res, fl, oldflp, dev_out, | 2355 | return ip_mkroute_output_def(rp, res, fl, oldflp, dev_out, |