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

sparc64: convert mdesc_handle.refcnt 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 mdesc_handle.refcnt is used as pure reference counter.
Convert it to refcount_t and fix up the operations.

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>
Acked-by: Shannon Nelson <shannon.nelson@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Elena Reshetova and committed by
David S. Miller
cdf5976f 68fa10dc

+9 -8
+9 -8
arch/sparc/kernel/mdesc.c
··· 12 12 #include <linux/miscdevice.h> 13 13 #include <linux/bootmem.h> 14 14 #include <linux/export.h> 15 + #include <linux/refcount.h> 15 16 16 17 #include <asm/cpudata.h> 17 18 #include <asm/hypervisor.h> ··· 71 70 struct list_head list; 72 71 struct mdesc_mem_ops *mops; 73 72 void *self_base; 74 - atomic_t refcnt; 73 + refcount_t refcnt; 75 74 unsigned int handle_size; 76 75 struct mdesc_hdr mdesc; 77 76 }; ··· 153 152 memset(hp, 0, handle_size); 154 153 INIT_LIST_HEAD(&hp->list); 155 154 hp->self_base = base; 156 - atomic_set(&hp->refcnt, 1); 155 + refcount_set(&hp->refcnt, 1); 157 156 hp->handle_size = handle_size; 158 157 } 159 158 ··· 183 182 unsigned int alloc_size; 184 183 unsigned long start; 185 184 186 - BUG_ON(atomic_read(&hp->refcnt) != 0); 185 + BUG_ON(refcount_read(&hp->refcnt) != 0); 187 186 BUG_ON(!list_empty(&hp->list)); 188 187 189 188 alloc_size = PAGE_ALIGN(hp->handle_size); ··· 221 220 222 221 static void mdesc_kfree(struct mdesc_handle *hp) 223 222 { 224 - BUG_ON(atomic_read(&hp->refcnt) != 0); 223 + BUG_ON(refcount_read(&hp->refcnt) != 0); 225 224 BUG_ON(!list_empty(&hp->list)); 226 225 227 226 kfree(hp->self_base); ··· 260 259 spin_lock_irqsave(&mdesc_lock, flags); 261 260 hp = cur_mdesc; 262 261 if (hp) 263 - atomic_inc(&hp->refcnt); 262 + refcount_inc(&hp->refcnt); 264 263 spin_unlock_irqrestore(&mdesc_lock, flags); 265 264 266 265 return hp; ··· 272 271 unsigned long flags; 273 272 274 273 spin_lock_irqsave(&mdesc_lock, flags); 275 - if (atomic_dec_and_test(&hp->refcnt)) { 274 + if (refcount_dec_and_test(&hp->refcnt)) { 276 275 list_del_init(&hp->list); 277 276 hp->mops->free(hp); 278 277 } ··· 514 513 if (status != HV_EOK || real_len > len) { 515 514 printk(KERN_ERR "MD: mdesc reread fails with %lu\n", 516 515 status); 517 - atomic_dec(&hp->refcnt); 516 + refcount_dec(&hp->refcnt); 518 517 mdesc_free(hp); 519 518 goto out; 520 519 } ··· 527 526 mdesc_notify_clients(orig_hp, hp); 528 527 529 528 spin_lock_irqsave(&mdesc_lock, flags); 530 - if (atomic_dec_and_test(&orig_hp->refcnt)) 529 + if (refcount_dec_and_test(&orig_hp->refcnt)) 531 530 mdesc_free(orig_hp); 532 531 else 533 532 list_add(&orig_hp->list, &mdesc_zombie_list);