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

lockd: convert nsm_handle.sm_count from atomic_t to refcount_t

atomic_t variables are currently used to implement reference
counters with the following properties:
- counter is initialized to 1 using atomic_set()
- a resource is freed upon counter reaching zero
- once counter reaches zero, its further
increments aren't allowed
- counter schema uses basic atomic operations
(set, inc, inc_not_zero, dec_and_test, etc.)

Such atomic variables should be converted to a newly provided
refcount_t type and API that prevents accidental counter overflows
and underflows. This is important since overflows and underflows
can lead to use-after-free situation and be exploitable.

The variable nsm_handle.sm_count is used as pure reference counter.
Convert it to refcount_t and fix up the operations.

**Important note for maintainers:

Some functions from refcount_t API defined in lib/refcount.c
have different memory ordering guarantees than their atomic
counterparts.
The full comparison can be seen in
https://lkml.org/lkml/2017/11/15/57 and it is hopefully soon
in state to be merged to the documentation tree.
Normally the differences should not matter since refcount_t provides
enough guarantees to satisfy the refcounting use cases, but in
some rare cases it might matter.
Please double check that you don't have some undocumented
memory guarantees for this variable usage.

For the nsm_handle.sm_count it might make a difference
in following places:
- nsm_release(): decrement in refcount_dec_and_lock() only
provides RELEASE ordering, control dependency on success
and holds a spin lock on success vs. fully ordered atomic
counterpart. No change for the spin lock guarantees.

Suggested-by: Kees Cook <keescook@chromium.org>
Reviewed-by: David Windsor <dwindsor@gmail.com>
Reviewed-by: Hans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>

authored by

Elena Reshetova and committed by
Trond Myklebust
c751082c fee21fb5

+9 -9
+1 -1
fs/lockd/host.c
··· 114 114 unsigned long now = jiffies; 115 115 116 116 if (nsm != NULL) 117 - atomic_inc(&nsm->sm_count); 117 + refcount_inc(&nsm->sm_count); 118 118 else { 119 119 host = NULL; 120 120 nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
+7 -7
fs/lockd/mon.c
··· 191 191 struct nsm_res res; 192 192 int status; 193 193 194 - if (atomic_read(&nsm->sm_count) == 1 194 + if (refcount_read(&nsm->sm_count) == 1 195 195 && nsm->sm_monitored && !nsm->sm_sticky) { 196 196 dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); 197 197 ··· 279 279 if (unlikely(new == NULL)) 280 280 return NULL; 281 281 282 - atomic_set(&new->sm_count, 1); 282 + refcount_set(&new->sm_count, 1); 283 283 new->sm_name = (char *)(new + 1); 284 284 memcpy(nsm_addr(new), sap, salen); 285 285 new->sm_addrlen = salen; ··· 337 337 cached = nsm_lookup_addr(&ln->nsm_handles, sap); 338 338 339 339 if (cached != NULL) { 340 - atomic_inc(&cached->sm_count); 340 + refcount_inc(&cached->sm_count); 341 341 spin_unlock(&nsm_lock); 342 342 kfree(new); 343 343 dprintk("lockd: found nsm_handle for %s (%s), " 344 344 "cnt %d\n", cached->sm_name, 345 345 cached->sm_addrbuf, 346 - atomic_read(&cached->sm_count)); 346 + refcount_read(&cached->sm_count)); 347 347 return cached; 348 348 } 349 349 ··· 388 388 return cached; 389 389 } 390 390 391 - atomic_inc(&cached->sm_count); 391 + refcount_inc(&cached->sm_count); 392 392 spin_unlock(&nsm_lock); 393 393 394 394 dprintk("lockd: host %s (%s) rebooted, cnt %d\n", 395 395 cached->sm_name, cached->sm_addrbuf, 396 - atomic_read(&cached->sm_count)); 396 + refcount_read(&cached->sm_count)); 397 397 return cached; 398 398 } 399 399 ··· 404 404 */ 405 405 void nsm_release(struct nsm_handle *nsm) 406 406 { 407 - if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) { 407 + if (refcount_dec_and_lock(&nsm->sm_count, &nsm_lock)) { 408 408 list_del(&nsm->sm_link); 409 409 spin_unlock(&nsm_lock); 410 410 dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
+1 -1
include/linux/lockd/lockd.h
··· 84 84 85 85 struct nsm_handle { 86 86 struct list_head sm_link; 87 - atomic_t sm_count; 87 + refcount_t sm_count; 88 88 char *sm_mon_name; 89 89 char *sm_name; 90 90 struct sockaddr_storage sm_addr;