After converting to use rcu for conntrack hash, one CPU may update the ct->status via ctnetlink, while another CPU may process the packets and update the ct->status. So the non-atomic operation "ct->status |= status;" via ctnetlink becomes unsafe, and this may clear the IPS_DYING_BIT bit set by another CPU unexpectedly. For example: CPU0 CPU1 ctnetlink_change_status __nf_conntrack_find_get old = ct->status nf_ct_gc_expired - nf_ct_kill - test_and_set_bit(IPS_DYING_BIT new = old | status; - ct->status = new; <-- oops, _DYING_ is cleared! Now using a series of atomic bit operation to solve the above issue. Also note, user shouldn't set IPS_TEMPLATE, IPS_SEQ_ADJUST directly, so make these two bits be unchangable too. If we set the IPS_TEMPLATE_BIT, ct will be freed by nf_ct_tmpl_free, but actually it is alloced by nf_conntrack_alloc. If we set the IPS_SEQ_ADJUST_BIT, this may cause the NULL pointer deference, as the nfct_seqadj(ct) maybe NULL. Last, add some comments to describe the logic change due to the commit a963d710f367 ("netfilter: ctnetlink: Fix regression in CTA_STATUS processing"), which makes me feel a little confusing.
Showing the changes to include/uapi/linux (1 of 2 files). Show all
Unified · Split
| @@ -82,10 +82,6 @@ enum ip_conntrack_status { · 81 unmodified lines | |||
| 82 | IPS_DYING_BIT = 9, | 82 | IPS_DYING_BIT = 9, |
| 83 | IPS_DYING = (1 << IPS_DYING_BIT), | 83 | IPS_DYING = (1 << IPS_DYING_BIT), |
| 84 | 84 | ||
| 85 | /* Bits that cannot be altered from userland. */ | ||
| 86 | IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | | ||
| 87 | IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING), | ||
| 88 | |||
| 89 | /* Connection has fixed timeout. */ | 85 | /* Connection has fixed timeout. */ |
| 90 | IPS_FIXED_TIMEOUT_BIT = 10, | 86 | IPS_FIXED_TIMEOUT_BIT = 10, |
| 91 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), | 87 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), |
| @@ -101,6 +97,15 @@ enum ip_conntrack_status { · 9 unmodified lines | |||
| 101 | /* Conntrack got a helper explicitly attached via CT target. */ | 97 | /* Conntrack got a helper explicitly attached via CT target. */ |
| 102 | IPS_HELPER_BIT = 13, | 98 | IPS_HELPER_BIT = 13, |
| 103 | IPS_HELPER = (1 << IPS_HELPER_BIT), | 99 | IPS_HELPER = (1 << IPS_HELPER_BIT), |
| 100 | |||
| 101 | /* Be careful here, modifying these bits can make things messy, | ||
| 102 | * so don't let users modify them directly. | ||
| 103 | */ | ||
| 104 | IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | | ||
| 105 | IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | | ||
| 106 | IPS_SEQ_ADJUST | IPS_TEMPLATE), | ||
| 107 | |||
| 108 | __IPS_MAX_BIT = 14, | ||
| 104 | }; | 109 | }; |
| 105 | 110 | ||
| 106 | /* Connection tracking event types */ | 111 | /* Connection tracking event types */ |