aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Bohac <jbohac@suse.cz>2016-10-20 06:29:26 -0400
committerDavid S. Miller <davem@davemloft.net>2016-10-20 14:29:11 -0400
commit7aa8e63f0d0f2e0ae353632bca1ce75a258696c6 (patch)
tree296d579460ec8de98617fd4c63945f4cf4db2aae
parent8be0328e52dc2c8c6f61563c9ccc95ae4d63e9ce (diff)
ipv6: properly prevent temp_prefered_lft sysctl race
The check for an underflow of tmp_prefered_lft is always false because tmp_prefered_lft is unsigned. The intention of the check was to guard against racing with an update of the temp_prefered_lft sysctl, potentially resulting in an underflow. As suggested by David Miller, the best way to prevent the race is by reading the sysctl variable using READ_ONCE. Signed-off-by: Jiri Bohac <jbohac@suse.cz> Reported-by: Julia Lawall <julia.lawall@lip6.fr> Fixes: 76506a986dc3 ("IPv6: fix DESYNC_FACTOR") Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/addrconf.c9
1 files changed, 4 insertions, 5 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index cc7c26d05f45..060dd9922018 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1185,6 +1185,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
1185 u32 addr_flags; 1185 u32 addr_flags;
1186 unsigned long now = jiffies; 1186 unsigned long now = jiffies;
1187 long max_desync_factor; 1187 long max_desync_factor;
1188 s32 cnf_temp_preferred_lft;
1188 1189
1189 write_lock_bh(&idev->lock); 1190 write_lock_bh(&idev->lock);
1190 if (ift) { 1191 if (ift) {
@@ -1228,9 +1229,10 @@ retry:
1228 /* recalculate max_desync_factor each time and update 1229 /* recalculate max_desync_factor each time and update
1229 * idev->desync_factor if it's larger 1230 * idev->desync_factor if it's larger
1230 */ 1231 */
1232 cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft);
1231 max_desync_factor = min_t(__u32, 1233 max_desync_factor = min_t(__u32,
1232 idev->cnf.max_desync_factor, 1234 idev->cnf.max_desync_factor,
1233 idev->cnf.temp_prefered_lft - regen_advance); 1235 cnf_temp_preferred_lft - regen_advance);
1234 1236
1235 if (unlikely(idev->desync_factor > max_desync_factor)) { 1237 if (unlikely(idev->desync_factor > max_desync_factor)) {
1236 if (max_desync_factor > 0) { 1238 if (max_desync_factor > 0) {
@@ -1245,11 +1247,8 @@ retry:
1245 tmp_valid_lft = min_t(__u32, 1247 tmp_valid_lft = min_t(__u32,
1246 ifp->valid_lft, 1248 ifp->valid_lft,
1247 idev->cnf.temp_valid_lft + age); 1249 idev->cnf.temp_valid_lft + age);
1248 tmp_prefered_lft = idev->cnf.temp_prefered_lft + age - 1250 tmp_prefered_lft = cnf_temp_preferred_lft + age -
1249 idev->desync_factor; 1251 idev->desync_factor;
1250 /* guard against underflow in case of concurrent updates to cnf */
1251 if (unlikely(tmp_prefered_lft < 0))
1252 tmp_prefered_lft = 0;
1253 tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft); 1252 tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft);
1254 tmp_plen = ifp->prefix_len; 1253 tmp_plen = ifp->prefix_len;
1255 tmp_tstamp = ifp->tstamp; 1254 tmp_tstamp = ifp->tstamp;