Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jiri Bohac and committed by
David S. Miller
7aa8e63f 8be0328e

+4 -5
+4 -5
net/ipv6/addrconf.c
··· 1185 1185 u32 addr_flags; 1186 1186 unsigned long now = jiffies; 1187 1187 long max_desync_factor; 1188 + s32 cnf_temp_preferred_lft; 1188 1189 1189 1190 write_lock_bh(&idev->lock); 1190 1191 if (ift) { ··· 1229 1228 /* recalculate max_desync_factor each time and update 1230 1229 * idev->desync_factor if it's larger 1231 1230 */ 1231 + cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft); 1232 1232 max_desync_factor = min_t(__u32, 1233 1233 idev->cnf.max_desync_factor, 1234 - idev->cnf.temp_prefered_lft - regen_advance); 1234 + cnf_temp_preferred_lft - regen_advance); 1235 1235 1236 1236 if (unlikely(idev->desync_factor > max_desync_factor)) { 1237 1237 if (max_desync_factor > 0) { ··· 1247 1245 tmp_valid_lft = min_t(__u32, 1248 1246 ifp->valid_lft, 1249 1247 idev->cnf.temp_valid_lft + age); 1250 - tmp_prefered_lft = idev->cnf.temp_prefered_lft + age - 1248 + tmp_prefered_lft = cnf_temp_preferred_lft + age - 1251 1249 idev->desync_factor; 1252 - /* guard against underflow in case of concurrent updates to cnf */ 1253 - if (unlikely(tmp_prefered_lft < 0)) 1254 - tmp_prefered_lft = 0; 1255 1250 tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft); 1256 1251 tmp_plen = ifp->prefix_len; 1257 1252 tmp_tstamp = ifp->tstamp;