Commit c85bb41e93184bf5494dde6d8fe5a81b564c84c8

Authored by Flavio Leitner
Committed by David S. Miller
1 parent 8b64056dac

igmp: fix ip_mc_sf_allow race [v5]

Almost all igmp functions accessing inet->mc_list are protected by
rtnl_lock(), but there is one exception which is ip_mc_sf_allow(),
so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group
remove an entry while ip_mc_sf_allow is running causing a crash.

Signed-off-by: Flavio Leitner <fleitner@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 2 changed files with 64 additions and 21 deletions Side-by-side Diff

include/linux/igmp.h
... ... @@ -153,6 +153,7 @@
153 153 struct ip_sf_socklist {
154 154 unsigned int sl_max;
155 155 unsigned int sl_count;
  156 + struct rcu_head rcu;
156 157 __be32 sl_addr[0];
157 158 };
158 159  
... ... @@ -170,6 +171,7 @@
170 171 struct ip_mreqn multi;
171 172 unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */
172 173 struct ip_sf_socklist *sflist;
  174 + struct rcu_head rcu;
173 175 };
174 176  
175 177 struct ip_sf_list {
... ... @@ -1799,7 +1799,7 @@
1799 1799 iml->next = inet->mc_list;
1800 1800 iml->sflist = NULL;
1801 1801 iml->sfmode = MCAST_EXCLUDE;
1802   - inet->mc_list = iml;
  1802 + rcu_assign_pointer(inet->mc_list, iml);
1803 1803 ip_mc_inc_group(in_dev, addr);
1804 1804 err = 0;
1805 1805 done:
1806 1806  
1807 1807  
1808 1808  
1809 1809  
... ... @@ -1807,24 +1807,46 @@
1807 1807 return err;
1808 1808 }
1809 1809  
  1810 +static void ip_sf_socklist_reclaim(struct rcu_head *rp)
  1811 +{
  1812 + struct ip_sf_socklist *psf;
  1813 +
  1814 + psf = container_of(rp, struct ip_sf_socklist, rcu);
  1815 + /* sk_omem_alloc should have been decreased by the caller*/
  1816 + kfree(psf);
  1817 +}
  1818 +
1810 1819 static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
1811 1820 struct in_device *in_dev)
1812 1821 {
  1822 + struct ip_sf_socklist *psf = iml->sflist;
1813 1823 int err;
1814 1824  
1815   - if (iml->sflist == NULL) {
  1825 + if (psf == NULL) {
1816 1826 /* any-source empty exclude case */
1817 1827 return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
1818 1828 iml->sfmode, 0, NULL, 0);
1819 1829 }
1820 1830 err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
1821   - iml->sfmode, iml->sflist->sl_count,
1822   - iml->sflist->sl_addr, 0);
1823   - sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max));
1824   - iml->sflist = NULL;
  1831 + iml->sfmode, psf->sl_count, psf->sl_addr, 0);
  1832 + rcu_assign_pointer(iml->sflist, NULL);
  1833 + /* decrease mem now to avoid the memleak warning */
  1834 + atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc);
  1835 + call_rcu(&psf->rcu, ip_sf_socklist_reclaim);
1825 1836 return err;
1826 1837 }
1827 1838  
  1839 +
  1840 +static void ip_mc_socklist_reclaim(struct rcu_head *rp)
  1841 +{
  1842 + struct ip_mc_socklist *iml;
  1843 +
  1844 + iml = container_of(rp, struct ip_mc_socklist, rcu);
  1845 + /* sk_omem_alloc should have been decreased by the caller*/
  1846 + kfree(iml);
  1847 +}
  1848 +
  1849 +
1828 1850 /*
1829 1851 * Ask a socket to leave a group.
1830 1852 */
1831 1853  
... ... @@ -1854,12 +1876,14 @@
1854 1876  
1855 1877 (void) ip_mc_leave_src(sk, iml, in_dev);
1856 1878  
1857   - *imlp = iml->next;
  1879 + rcu_assign_pointer(*imlp, iml->next);
1858 1880  
1859 1881 if (in_dev)
1860 1882 ip_mc_dec_group(in_dev, group);
1861 1883 rtnl_unlock();
1862   - sock_kfree_s(sk, iml, sizeof(*iml));
  1884 + /* decrease mem now to avoid the memleak warning */
  1885 + atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
  1886 + call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
1863 1887 return 0;
1864 1888 }
1865 1889 if (!in_dev)
1866 1890  
... ... @@ -1974,9 +1998,12 @@
1974 1998 if (psl) {
1975 1999 for (i=0; i<psl->sl_count; i++)
1976 2000 newpsl->sl_addr[i] = psl->sl_addr[i];
1977   - sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
  2001 + /* decrease mem now to avoid the memleak warning */
  2002 + atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
  2003 + call_rcu(&psl->rcu, ip_sf_socklist_reclaim);
1978 2004 }
1979   - pmc->sflist = psl = newpsl;
  2005 + rcu_assign_pointer(pmc->sflist, newpsl);
  2006 + psl = newpsl;
1980 2007 }
1981 2008 rv = 1; /* > 0 for insert logic below if sl_count is 0 */
1982 2009 for (i=0; i<psl->sl_count; i++) {
1983 2010  
... ... @@ -2072,11 +2099,13 @@
2072 2099 if (psl) {
2073 2100 (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
2074 2101 psl->sl_count, psl->sl_addr, 0);
2075   - sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
  2102 + /* decrease mem now to avoid the memleak warning */
  2103 + atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
  2104 + call_rcu(&psl->rcu, ip_sf_socklist_reclaim);
2076 2105 } else
2077 2106 (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
2078 2107 0, NULL, 0);
2079   - pmc->sflist = newpsl;
  2108 + rcu_assign_pointer(pmc->sflist, newpsl);
2080 2109 pmc->sfmode = msf->imsf_fmode;
2081 2110 err = 0;
2082 2111 done:
2083 2112  
2084 2113  
2085 2114  
2086 2115  
2087 2116  
2088 2117  
2089 2118  
2090 2119  
2091 2120  
2092 2121  
... ... @@ -2209,30 +2238,40 @@
2209 2238 struct ip_mc_socklist *pmc;
2210 2239 struct ip_sf_socklist *psl;
2211 2240 int i;
  2241 + int ret;
2212 2242  
  2243 + ret = 1;
2213 2244 if (!ipv4_is_multicast(loc_addr))
2214   - return 1;
  2245 + goto out;
2215 2246  
2216   - for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
  2247 + rcu_read_lock();
  2248 + for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) {
2217 2249 if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
2218 2250 pmc->multi.imr_ifindex == dif)
2219 2251 break;
2220 2252 }
  2253 + ret = inet->mc_all;
2221 2254 if (!pmc)
2222   - return inet->mc_all;
  2255 + goto unlock;
2223 2256 psl = pmc->sflist;
  2257 + ret = (pmc->sfmode == MCAST_EXCLUDE);
2224 2258 if (!psl)
2225   - return pmc->sfmode == MCAST_EXCLUDE;
  2259 + goto unlock;
2226 2260  
2227 2261 for (i=0; i<psl->sl_count; i++) {
2228 2262 if (psl->sl_addr[i] == rmt_addr)
2229 2263 break;
2230 2264 }
  2265 + ret = 0;
2231 2266 if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
2232   - return 0;
  2267 + goto unlock;
2233 2268 if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
2234   - return 0;
2235   - return 1;
  2269 + goto unlock;
  2270 + ret = 1;
  2271 +unlock:
  2272 + rcu_read_unlock();
  2273 +out:
  2274 + return ret;
2236 2275 }
2237 2276  
2238 2277 /*
... ... @@ -2251,7 +2290,7 @@
2251 2290 rtnl_lock();
2252 2291 while ((iml = inet->mc_list) != NULL) {
2253 2292 struct in_device *in_dev;
2254   - inet->mc_list = iml->next;
  2293 + rcu_assign_pointer(inet->mc_list, iml->next);
2255 2294  
2256 2295 in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
2257 2296 (void) ip_mc_leave_src(sk, iml, in_dev);
... ... @@ -2259,7 +2298,9 @@
2259 2298 ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
2260 2299 in_dev_put(in_dev);
2261 2300 }
2262   - sock_kfree_s(sk, iml, sizeof(*iml));
  2301 + /* decrease mem now to avoid the memleak warning */
  2302 + atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
  2303 + call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
2263 2304 }
2264 2305 rtnl_unlock();
2265 2306 }