2017-03-03 09:07:18

by Elena Reshetova

[permalink] [raw]
Subject: [PATCH 0/3] fs, fuse subsystem refcount conversions

Now when new refcount_t type and API are finally merged
(see include/linux/refcount.h), the following
patches convert various refcounters in the fuse filesystem from atomic_t
to refcount_t. By doing this we prevent intentional or accidental
underflows or overflows that can led to use-after-free vulnerabilities.

The below patches are fully independent and can be cherry-picked separately.
Since we convert all kernel subsystems in the same fashion, resulting
in about 300 patches, we have to group them for sending at least in some
fashion to be manageable. Please excuse the long cc list.

These patches have been tested using tests supplied with libfuse.
Not sure if this is the right way to test it. No output or failures
with result to refcount conversions. refcount WARNs were on.


Elena Reshetova (3):
fs, fuse: convert fuse_file.count from atomic_t to refcount_t
fs, fuse: convert fuse_req.count from atomic_t to refcount_t
fs, fuse: convert fuse_conn.count from atomic_t to refcount_t

fs/fuse/dev.c | 10 +++++-----
fs/fuse/file.c | 8 ++++----
fs/fuse/fuse_i.h | 7 ++++---
fs/fuse/inode.c | 6 +++---
4 files changed, 16 insertions(+), 15 deletions(-)

--
2.7.4


2017-03-03 09:07:26

by Elena Reshetova

[permalink] [raw]
Subject: [PATCH 2/3] fs, fuse: convert fuse_req.count from atomic_t to refcount_t

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Signed-off-by: Elena Reshetova <[email protected]>
Signed-off-by: Hans Liljestrand <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Signed-off-by: David Windsor <[email protected]>
---
fs/fuse/dev.c | 10 +++++-----
fs/fuse/fuse_i.h | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index f117926..3e67205 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -44,7 +44,7 @@ static void fuse_request_init(struct fuse_req *req, struct page **pages,
INIT_LIST_HEAD(&req->list);
INIT_LIST_HEAD(&req->intr_entry);
init_waitqueue_head(&req->waitq);
- atomic_set(&req->count, 1);
+ refcount_set(&req->count, 1);
req->pages = pages;
req->page_descs = page_descs;
req->max_pages = npages;
@@ -101,14 +101,14 @@ void fuse_request_free(struct fuse_req *req)

void __fuse_get_request(struct fuse_req *req)
{
- atomic_inc(&req->count);
+ refcount_inc(&req->count);
}

/* Must be called with > 1 refcount */
static void __fuse_put_request(struct fuse_req *req)
{
- BUG_ON(atomic_read(&req->count) < 2);
- atomic_dec(&req->count);
+ BUG_ON(refcount_read(&req->count) < 2);
+ refcount_dec(&req->count);
}

static void fuse_req_init_context(struct fuse_req *req)
@@ -263,7 +263,7 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,

void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
- if (atomic_dec_and_test(&req->count)) {
+ if (refcount_dec_and_test(&req->count)) {
if (test_bit(FR_BACKGROUND, &req->flags)) {
/*
* We get here in the unlikely case that a background
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1d6d67e..9d43740 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -307,7 +307,7 @@ struct fuse_req {
struct list_head intr_entry;

/** refcount */
- atomic_t count;
+ refcount_t count;

/** Unique ID for the interrupt request */
u64 intr_unique;
--
2.7.4

2017-03-03 09:09:18

by Elena Reshetova

[permalink] [raw]
Subject: [PATCH 1/3] fs, fuse: convert fuse_file.count from atomic_t to refcount_t

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Signed-off-by: Elena Reshetova <[email protected]>
Signed-off-by: Hans Liljestrand <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Signed-off-by: David Windsor <[email protected]>
---
fs/fuse/file.c | 8 ++++----
fs/fuse/fuse_i.h | 3 ++-
2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ec238fb..70770a0 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -58,7 +58,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
}

INIT_LIST_HEAD(&ff->write_entry);
- atomic_set(&ff->count, 1);
+ refcount_set(&ff->count, 1);
RB_CLEAR_NODE(&ff->polled_node);
init_waitqueue_head(&ff->poll_wait);

@@ -77,7 +77,7 @@ void fuse_file_free(struct fuse_file *ff)

static struct fuse_file *fuse_file_get(struct fuse_file *ff)
{
- atomic_inc(&ff->count);
+ refcount_inc(&ff->count);
return ff;
}

@@ -88,7 +88,7 @@ static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)

static void fuse_file_put(struct fuse_file *ff, bool sync)
{
- if (atomic_dec_and_test(&ff->count)) {
+ if (refcount_dec_and_test(&ff->count)) {
struct fuse_req *req = ff->reserved_req;

if (ff->fc->no_open) {
@@ -293,7 +293,7 @@ static int fuse_release(struct inode *inode, struct file *file)

void fuse_sync_release(struct fuse_file *ff, int flags)
{
- WARN_ON(atomic_read(&ff->count) != 1);
+ WARN_ON(refcount_read(&ff->count) > 1);
fuse_prepare_release(ff, flags, FUSE_RELEASE);
/*
* iput(NULL) is a no-op and since the refcount is 1 and everything's
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 32ac2c9..1d6d67e 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -24,6 +24,7 @@
#include <linux/workqueue.h>
#include <linux/kref.h>
#include <linux/xattr.h>
+#include <linux/refcount.h>

/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
@@ -137,7 +138,7 @@ struct fuse_file {
u64 nodeid;

/** Refcount */
- atomic_t count;
+ refcount_t count;

/** FOPEN_* flags returned by open */
u32 open_flags;
--
2.7.4

2017-03-03 12:07:50

by Elena Reshetova

[permalink] [raw]
Subject: [PATCH 3/3] fs, fuse: convert fuse_conn.count from atomic_t to refcount_t

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Signed-off-by: Elena Reshetova <[email protected]>
Signed-off-by: Hans Liljestrand <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Signed-off-by: David Windsor <[email protected]>
---
fs/fuse/fuse_i.h | 2 +-
fs/fuse/inode.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 9d43740..6c649f0 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -449,7 +449,7 @@ struct fuse_conn {
spinlock_t lock;

/** Refcount */
- atomic_t count;
+ refcount_t count;

/** Number of fuse_dev's */
atomic_t dev_count;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 6fe6a88..3961c5f 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -608,7 +608,7 @@ void fuse_conn_init(struct fuse_conn *fc)
memset(fc, 0, sizeof(*fc));
spin_lock_init(&fc->lock);
init_rwsem(&fc->killsb);
- atomic_set(&fc->count, 1);
+ refcount_set(&fc->count, 1);
atomic_set(&fc->dev_count, 1);
init_waitqueue_head(&fc->blocked_waitq);
init_waitqueue_head(&fc->reserved_req_waitq);
@@ -631,7 +631,7 @@ EXPORT_SYMBOL_GPL(fuse_conn_init);

void fuse_conn_put(struct fuse_conn *fc)
{
- if (atomic_dec_and_test(&fc->count)) {
+ if (refcount_dec_and_test(&fc->count)) {
if (fc->destroy_req)
fuse_request_free(fc->destroy_req);
fc->release(fc);
@@ -641,7 +641,7 @@ EXPORT_SYMBOL_GPL(fuse_conn_put);

struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
{
- atomic_inc(&fc->count);
+ refcount_inc(&fc->count);
return fc;
}
EXPORT_SYMBOL_GPL(fuse_conn_get);
--
2.7.4

2017-03-03 16:38:04

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH 0/3] fs, fuse subsystem refcount conversions

On Fri, Mar 3, 2017 at 10:04 AM, Elena Reshetova
<[email protected]> wrote:
> Now when new refcount_t type and API are finally merged
> (see include/linux/refcount.h), the following
> patches convert various refcounters in the fuse filesystem from atomic_t
> to refcount_t. By doing this we prevent intentional or accidental
> underflows or overflows that can led to use-after-free vulnerabilities.
>
> The below patches are fully independent and can be cherry-picked separately.
> Since we convert all kernel subsystems in the same fashion, resulting
> in about 300 patches, we have to group them for sending at least in some
> fashion to be manageable. Please excuse the long cc list.
>
> These patches have been tested using tests supplied with libfuse.
> Not sure if this is the right way to test it. No output or failures
> with result to refcount conversions. refcount WARNs were on.

Thanks, queued.

Miklos