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 kcov.refcount is used as pure reference counter.
Convert it to refcount_t and fix up the operations.

Suggested-by: Kees Cook <[email protected]>
Reviewed-by: David Windsor <[email protected]>
Reviewed-by: Hans Liljestrand <[email protected]>
Signed-off-by: Elena Reshetova <[email protected]>
---
 kernel/kcov.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/kernel/kcov.c b/kernel/kcov.c
index 461c55e..03c174c 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -19,6 +19,7 @@
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 #include <linux/kcov.h>
+#include <linux/refcount.h>
 #include <asm/setup.h>
 
 /* Number of 64-bit words written per one comparison: */
@@ -43,7 +44,7 @@ struct kcov {
         *  - opened file descriptor
         *  - task with enabled coverage (we can't unwire it from another task)
         */
-       atomic_t                refcount;
+       refcount_t              refcount;
        /* The lock protects mode, size, area and t. */
        spinlock_t              lock;
        enum kcov_mode          mode;
@@ -227,12 +228,12 @@ EXPORT_SYMBOL(__sanitizer_cov_trace_switch);
 
 static void kcov_get(struct kcov *kcov)
 {
-       atomic_inc(&kcov->refcount);
+       refcount_inc(&kcov->refcount);
 }
 
 static void kcov_put(struct kcov *kcov)
 {
-       if (atomic_dec_and_test(&kcov->refcount)) {
+       if (refcount_dec_and_test(&kcov->refcount)) {
                vfree(kcov->area);
                kfree(kcov);
        }
@@ -310,7 +311,7 @@ static int kcov_open(struct inode *inode, struct file 
*filep)
        if (!kcov)
                return -ENOMEM;
        kcov->mode = KCOV_MODE_DISABLED;
-       atomic_set(&kcov->refcount, 1);
+       refcount_set(&kcov->refcount, 1);
        spin_lock_init(&kcov->lock);
        filep->private_data = kcov;
        return nonseekable_open(inode, filep);
-- 
2.7.4

Reply via email to