[IPVS]: Close race conditions on ip_vs_conn_tab list modification

In an smp system, it is possible for an connection timer to expire, calling
ip_vs_conn_expire while the connection table is being flushed, before
ct_write_lock_bh is acquired.

Since the list iterator loop in ip_vs_con_flush releases and re-acquires the
spinlock (even though it doesn't re-enable softirqs), it is possible for the
expiration function to modify the connection list, while it is being traversed
in ip_vs_conn_flush.

The result is that the next pointer gets set to NULL, and subsequently
dereferenced, resulting in an oops.

Signed-off-by: Neil Horman <nhorman@redhat.com>
Acked-by: JulianAnastasov
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Neil Horman and committed by David S. Miller fb3d8949 689be439

+4 -21
+4 -21
net/ipv4/ipvs/ip_vs_conn.c
··· 548 { 549 if (del_timer(&cp->timer)) 550 mod_timer(&cp->timer, jiffies); 551 - __ip_vs_conn_put(cp); 552 } 553 554 ··· 763 { 764 int idx; 765 struct ip_vs_conn *cp; 766 - struct ip_vs_conn *ct; 767 768 /* 769 * Randomly scan 1/32 of the whole table every second ··· 799 continue; 800 } 801 802 - /* 803 - * Drop the entry, and drop its ct if not referenced 804 - */ 805 - atomic_inc(&cp->refcnt); 806 - ct_write_unlock(hash); 807 - 808 - if ((ct = cp->control)) 809 - atomic_inc(&ct->refcnt); 810 IP_VS_DBG(4, "del connection\n"); 811 ip_vs_conn_expire_now(cp); 812 - if (ct) { 813 IP_VS_DBG(4, "del conn template\n"); 814 - ip_vs_conn_expire_now(ct); 815 } 816 - ct_write_lock(hash); 817 } 818 ct_write_unlock(hash); 819 } ··· 818 { 819 int idx; 820 struct ip_vs_conn *cp; 821 - struct ip_vs_conn *ct; 822 823 flush_again: 824 for (idx=0; idx<IP_VS_CONN_TAB_SIZE; idx++) { ··· 827 ct_write_lock_bh(idx); 828 829 list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { 830 - atomic_inc(&cp->refcnt); 831 - ct_write_unlock(idx); 832 833 - if ((ct = cp->control)) 834 - atomic_inc(&ct->refcnt); 835 IP_VS_DBG(4, "del connection\n"); 836 ip_vs_conn_expire_now(cp); 837 - if (ct) { 838 IP_VS_DBG(4, "del conn template\n"); 839 - ip_vs_conn_expire_now(ct); 840 } 841 - ct_write_lock(idx); 842 } 843 ct_write_unlock_bh(idx); 844 }
··· 548 { 549 if (del_timer(&cp->timer)) 550 mod_timer(&cp->timer, jiffies); 551 } 552 553 ··· 764 { 765 int idx; 766 struct ip_vs_conn *cp; 767 768 /* 769 * Randomly scan 1/32 of the whole table every second ··· 801 continue; 802 } 803 804 IP_VS_DBG(4, "del connection\n"); 805 ip_vs_conn_expire_now(cp); 806 + if (cp->control) { 807 IP_VS_DBG(4, "del conn template\n"); 808 + ip_vs_conn_expire_now(cp->control); 809 } 810 } 811 ct_write_unlock(hash); 812 } ··· 829 { 830 int idx; 831 struct ip_vs_conn *cp; 832 833 flush_again: 834 for (idx=0; idx<IP_VS_CONN_TAB_SIZE; idx++) { ··· 839 ct_write_lock_bh(idx); 840 841 list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { 842 843 IP_VS_DBG(4, "del connection\n"); 844 ip_vs_conn_expire_now(cp); 845 + if (cp->control) { 846 IP_VS_DBG(4, "del conn template\n"); 847 + ip_vs_conn_expire_now(cp->control); 848 } 849 } 850 ct_write_unlock_bh(idx); 851 }