2017-11-03 11:28:30 +01:00
|
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2010-05-05 12:53:14 +02:00
|
|
|
|
/*
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* f_fs.c -- user mode file system API for USB composite function controllers
|
2010-05-05 12:53:14 +02:00
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2010 Samsung Electronics
|
2012-01-13 15:05:16 +01:00
|
|
|
|
* Author: Michal Nazarewicz <mina86@mina86.com>
|
2010-05-05 12:53:14 +02:00
|
|
|
|
*
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* Based on inode.c (GadgetFS) which was:
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* Copyright (C) 2003-2004 David Brownell
|
|
|
|
|
* Copyright (C) 2003 Agilent Technologies
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* #define DEBUG */
|
|
|
|
|
/* #define VERBOSE_DEBUG */
|
|
|
|
|
|
|
|
|
|
#include <linux/blkdev.h>
|
2024-01-30 13:23:39 +01:00
|
|
|
|
#include <linux/dma-buf.h>
|
|
|
|
|
#include <linux/dma-fence.h>
|
|
|
|
|
#include <linux/dma-resv.h>
|
2010-05-10 10:51:36 -07:00
|
|
|
|
#include <linux/pagemap.h>
|
2011-05-27 09:56:31 -04:00
|
|
|
|
#include <linux/export.h>
|
2019-03-20 23:31:05 +00:00
|
|
|
|
#include <linux/fs_parser.h>
|
2012-05-30 20:43:37 +02:00
|
|
|
|
#include <linux/hid.h>
|
2018-11-14 10:47:48 +01:00
|
|
|
|
#include <linux/mm.h>
|
2013-12-03 15:15:33 +01:00
|
|
|
|
#include <linux/module.h>
|
2018-11-14 10:47:48 +01:00
|
|
|
|
#include <linux/scatterlist.h>
|
2017-02-02 19:15:33 +01:00
|
|
|
|
#include <linux/sched/signal.h>
|
2015-02-22 08:58:50 -08:00
|
|
|
|
#include <linux/uio.h>
|
2018-11-14 10:47:48 +01:00
|
|
|
|
#include <linux/vmalloc.h>
|
2024-10-01 15:35:57 -04:00
|
|
|
|
#include <linux/unaligned.h>
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2018-10-09 14:43:18 +00:00
|
|
|
|
#include <linux/usb/ccid.h>
|
2010-05-05 12:53:14 +02:00
|
|
|
|
#include <linux/usb/composite.h>
|
|
|
|
|
#include <linux/usb/functionfs.h>
|
2024-09-01 21:11:16 +02:00
|
|
|
|
#include <linux/usb/func_utils.h>
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
#include <linux/aio.h>
|
2020-06-10 18:41:59 -07:00
|
|
|
|
#include <linux/kthread.h>
|
2014-02-10 10:42:43 +01:00
|
|
|
|
#include <linux/poll.h>
|
2015-01-23 13:41:01 +01:00
|
|
|
|
#include <linux/eventfd.h>
|
2014-02-10 10:42:43 +01:00
|
|
|
|
|
2013-12-03 15:15:31 +01:00
|
|
|
|
#include "u_fs.h"
|
2014-07-09 12:20:08 +02:00
|
|
|
|
#include "u_os_desc.h"
|
2013-12-03 15:15:36 +01:00
|
|
|
|
#include "configfs.h"
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
|
2024-03-01 13:47:08 +01:00
|
|
|
|
#define MAX_ALT_SETTINGS 2 /* Allow up to 2 alt settings to be set. */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2024-04-02 13:09:51 +02:00
|
|
|
|
#define DMABUF_ENQUEUE_TIMEOUT_MS 5000
|
|
|
|
|
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
|
MODULE_IMPORT_NS("DMA_BUF");
|
2024-01-30 13:23:39 +01:00
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
/* Reference counter handling */
|
|
|
|
|
static void ffs_data_get(struct ffs_data *ffs);
|
|
|
|
|
static void ffs_data_put(struct ffs_data *ffs);
|
|
|
|
|
/* Creates new ffs_data object. */
|
2017-09-12 10:24:40 +01:00
|
|
|
|
static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
|
|
|
|
|
__attribute__((malloc));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
/* Opened counter handling. */
|
|
|
|
|
static void ffs_data_opened(struct ffs_data *ffs);
|
|
|
|
|
static void ffs_data_closed(struct ffs_data *ffs);
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Called with ffs->mutex held; take over ownership of data. */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int __must_check
|
|
|
|
|
__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
|
|
|
|
|
static int __must_check
|
|
|
|
|
__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The function structure ***************************************************/
|
|
|
|
|
|
|
|
|
|
struct ffs_ep;
|
|
|
|
|
|
|
|
|
|
struct ffs_function {
|
|
|
|
|
struct usb_configuration *conf;
|
|
|
|
|
struct usb_gadget *gadget;
|
|
|
|
|
struct ffs_data *ffs;
|
|
|
|
|
|
|
|
|
|
struct ffs_ep *eps;
|
|
|
|
|
u8 eps_revmap[16];
|
|
|
|
|
short *interfaces_nums;
|
|
|
|
|
|
|
|
|
|
struct usb_function function;
|
2024-03-01 13:47:08 +01:00
|
|
|
|
int cur_alt[MAX_CONFIG_INTERFACES];
|
2010-05-05 12:53:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
return container_of(f, struct ffs_function, function);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-02-10 10:42:41 +01:00
|
|
|
|
static inline enum ffs_setup_state
|
|
|
|
|
ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
return (enum ffs_setup_state)
|
|
|
|
|
cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static void ffs_func_eps_disable(struct ffs_function *func);
|
|
|
|
|
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
|
|
|
|
|
|
|
|
|
|
static int ffs_func_bind(struct usb_configuration *,
|
|
|
|
|
struct usb_function *);
|
|
|
|
|
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
|
2024-03-01 13:47:08 +01:00
|
|
|
|
static int ffs_func_get_alt(struct usb_function *f, unsigned int intf);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static void ffs_func_disable(struct usb_function *);
|
|
|
|
|
static int ffs_func_setup(struct usb_function *,
|
|
|
|
|
const struct usb_ctrlrequest *);
|
2016-06-22 01:12:07 +02:00
|
|
|
|
static bool ffs_func_req_match(struct usb_function *,
|
2016-06-22 01:12:08 +02:00
|
|
|
|
const struct usb_ctrlrequest *,
|
|
|
|
|
bool config0);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static void ffs_func_suspend(struct usb_function *);
|
|
|
|
|
static void ffs_func_resume(struct usb_function *);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
|
|
|
|
|
static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The endpoints structures *************************************************/
|
|
|
|
|
|
|
|
|
|
struct ffs_ep {
|
|
|
|
|
struct usb_ep *ep; /* P: ffs->eps_lock */
|
|
|
|
|
struct usb_request *req; /* P: epfile->mutex */
|
|
|
|
|
|
2014-02-28 16:50:22 +05:30
|
|
|
|
/* [0]: full speed, [1]: high speed, [2]: super speed */
|
|
|
|
|
struct usb_endpoint_descriptor *descs[3];
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
u8 num;
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-30 13:23:39 +01:00
|
|
|
|
struct ffs_dmabuf_priv {
|
|
|
|
|
struct list_head entry;
|
|
|
|
|
struct kref ref;
|
|
|
|
|
struct ffs_data *ffs;
|
|
|
|
|
struct dma_buf_attachment *attach;
|
|
|
|
|
struct sg_table *sgt;
|
|
|
|
|
enum dma_data_direction dir;
|
|
|
|
|
spinlock_t lock;
|
|
|
|
|
u64 context;
|
|
|
|
|
struct usb_request *req; /* P: ffs->eps_lock */
|
|
|
|
|
struct usb_ep *ep; /* P: ffs->eps_lock */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ffs_dma_fence {
|
|
|
|
|
struct dma_fence base;
|
|
|
|
|
struct ffs_dmabuf_priv *priv;
|
|
|
|
|
struct work_struct work;
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
struct ffs_epfile {
|
|
|
|
|
/* Protects ep->ep and ep->req. */
|
|
|
|
|
struct mutex mutex;
|
|
|
|
|
|
|
|
|
|
struct ffs_data *ffs;
|
|
|
|
|
struct ffs_ep *ep; /* P: ffs->eps_lock */
|
|
|
|
|
|
|
|
|
|
struct dentry *dentry;
|
|
|
|
|
|
2016-05-21 20:47:35 +02:00
|
|
|
|
/*
|
|
|
|
|
* Buffer for holding data from partial reads which may happen since
|
|
|
|
|
* we’re rounding user read requests to a multiple of a max packet size.
|
2016-10-04 02:07:34 +02:00
|
|
|
|
*
|
|
|
|
|
* The pointer is initialised with NULL value and may be set by
|
|
|
|
|
* __ffs_epfile_read_data function to point to a temporary buffer.
|
|
|
|
|
*
|
|
|
|
|
* In normal operation, calls to __ffs_epfile_read_buffered will consume
|
|
|
|
|
* data from said buffer and eventually free it. Importantly, while the
|
|
|
|
|
* function is using the buffer, it sets the pointer to NULL. This is
|
|
|
|
|
* all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
|
|
|
|
|
* can never run concurrently (they are synchronised by epfile->mutex)
|
|
|
|
|
* so the latter will not assign a new value to the pointer.
|
|
|
|
|
*
|
|
|
|
|
* Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
|
|
|
|
|
* valid) and sets the pointer to READ_BUFFER_DROP value. This special
|
|
|
|
|
* value is crux of the synchronisation between ffs_func_eps_disable and
|
|
|
|
|
* __ffs_epfile_read_data.
|
|
|
|
|
*
|
|
|
|
|
* Once __ffs_epfile_read_data is about to finish it will try to set the
|
|
|
|
|
* pointer back to its old value (as described above), but seeing as the
|
|
|
|
|
* pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
|
|
|
|
|
* the buffer.
|
|
|
|
|
*
|
|
|
|
|
* == State transitions ==
|
|
|
|
|
*
|
|
|
|
|
* • ptr == NULL: (initial state)
|
|
|
|
|
* ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP
|
|
|
|
|
* ◦ __ffs_epfile_read_buffered: nop
|
|
|
|
|
* ◦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
|
|
|
|
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
|
|
|
|
* • ptr == DROP:
|
|
|
|
|
* ◦ __ffs_epfile_read_buffer_free: nop
|
|
|
|
|
* ◦ __ffs_epfile_read_buffered: go to ptr == NULL
|
|
|
|
|
* ◦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
|
|
|
|
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
|
|
|
|
* • ptr == buf:
|
|
|
|
|
* ◦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
|
|
|
|
|
* ◦ __ffs_epfile_read_buffered: go to ptr == NULL and reading
|
|
|
|
|
* ◦ __ffs_epfile_read_data: n/a, __ffs_epfile_read_buffered
|
|
|
|
|
* is always called first
|
|
|
|
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
|
|
|
|
* • ptr == NULL and reading:
|
|
|
|
|
* ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
|
|
|
|
|
* ◦ __ffs_epfile_read_buffered: n/a, mutex is held
|
|
|
|
|
* ◦ __ffs_epfile_read_data: n/a, mutex is held
|
|
|
|
|
* ◦ reading finishes and …
|
|
|
|
|
* … all data read: free buf, go to ptr == NULL
|
|
|
|
|
* … otherwise: go to ptr == buf and reading
|
|
|
|
|
* • ptr == DROP and reading:
|
|
|
|
|
* ◦ __ffs_epfile_read_buffer_free: nop
|
|
|
|
|
* ◦ __ffs_epfile_read_buffered: n/a, mutex is held
|
|
|
|
|
* ◦ __ffs_epfile_read_data: n/a, mutex is held
|
|
|
|
|
* ◦ reading finishes: free buf, go to ptr == DROP
|
2016-05-21 20:47:35 +02:00
|
|
|
|
*/
|
2016-10-04 02:07:34 +02:00
|
|
|
|
struct ffs_buffer *read_buffer;
|
|
|
|
|
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
|
2016-05-21 20:47:35 +02:00
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
char name[5];
|
|
|
|
|
|
|
|
|
|
unsigned char in; /* P: ffs->eps_lock */
|
|
|
|
|
unsigned char isoc; /* P: ffs->eps_lock */
|
|
|
|
|
|
|
|
|
|
unsigned char _pad;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
|
|
|
|
|
/* Protects dmabufs */
|
|
|
|
|
struct mutex dmabufs_mutex;
|
|
|
|
|
struct list_head dmabufs; /* P: dmabufs_mutex */
|
|
|
|
|
atomic_t seqno;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
2016-05-21 20:47:35 +02:00
|
|
|
|
struct ffs_buffer {
|
|
|
|
|
size_t length;
|
|
|
|
|
char *data;
|
2023-09-15 12:58:49 -07:00
|
|
|
|
char storage[] __counted_by(length);
|
2016-05-21 20:47:35 +02:00
|
|
|
|
};
|
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
/* ffs_io_data structure ***************************************************/
|
|
|
|
|
|
|
|
|
|
struct ffs_io_data {
|
|
|
|
|
bool aio;
|
|
|
|
|
bool read;
|
|
|
|
|
|
|
|
|
|
struct kiocb *kiocb;
|
2015-01-31 23:23:35 -05:00
|
|
|
|
struct iov_iter data;
|
|
|
|
|
const void *to_free;
|
|
|
|
|
char *buf;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
|
|
|
|
struct mm_struct *mm;
|
|
|
|
|
struct work_struct work;
|
|
|
|
|
|
|
|
|
|
struct usb_ep *ep;
|
|
|
|
|
struct usb_request *req;
|
2018-11-14 10:47:48 +01:00
|
|
|
|
struct sg_table sgt;
|
|
|
|
|
bool use_sg;
|
2015-01-23 13:41:01 +01:00
|
|
|
|
|
|
|
|
|
struct ffs_data *ffs;
|
2022-06-10 20:17:57 +08:00
|
|
|
|
|
|
|
|
|
int status;
|
|
|
|
|
struct completion done;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
};
|
|
|
|
|
|
2014-08-25 11:16:27 +02:00
|
|
|
|
struct ffs_desc_helper {
|
|
|
|
|
struct ffs_data *ffs;
|
|
|
|
|
unsigned interfaces_count;
|
|
|
|
|
unsigned eps_count;
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
|
|
|
|
|
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
|
|
|
|
|
|
2014-09-03 13:32:19 -04:00
|
|
|
|
static struct dentry *
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
|
2014-09-03 13:32:19 -04:00
|
|
|
|
const struct file_operations *fops);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
/* Devices management *******************************************************/
|
|
|
|
|
|
|
|
|
|
DEFINE_MUTEX(ffs_lock);
|
2014-04-01 13:19:32 -05:00
|
|
|
|
EXPORT_SYMBOL_GPL(ffs_lock);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static struct ffs_dev *_ffs_find_dev(const char *name);
|
|
|
|
|
static struct ffs_dev *_ffs_alloc_dev(void);
|
|
|
|
|
static void _ffs_free_dev(struct ffs_dev *dev);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
|
|
|
|
|
static void ffs_release_dev(struct ffs_dev *ffs_dev);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
static int ffs_ready(struct ffs_data *ffs);
|
|
|
|
|
static void ffs_closed(struct ffs_data *ffs);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
/* Misc helper functions ****************************************************/
|
|
|
|
|
|
|
|
|
|
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
|
|
|
|
|
__attribute__((warn_unused_result, nonnull));
|
2012-09-26 21:43:45 -04:00
|
|
|
|
static char *ffs_prepare_buffer(const char __user *buf, size_t len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
__attribute__((warn_unused_result, nonnull));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Control file aka ep0 *****************************************************/
|
|
|
|
|
|
|
|
|
|
static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = req->context;
|
|
|
|
|
|
2016-09-22 15:51:53 +02:00
|
|
|
|
complete(&ffs->ep0req_completion);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
|
2017-11-28 15:20:53 +00:00
|
|
|
|
__releases(&ffs->ev.waitq.lock)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct usb_request *req = ffs->ep0req;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2023-01-24 14:41:49 +05:30
|
|
|
|
if (!req) {
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
2022-12-15 10:59:05 +05:30
|
|
|
|
return -EINVAL;
|
2023-01-24 14:41:49 +05:30
|
|
|
|
}
|
2022-12-15 10:59:05 +05:30
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
|
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
|
|
|
|
req->buf = data;
|
|
|
|
|
req->length = len;
|
|
|
|
|
|
2011-01-28 13:55:36 +01:00
|
|
|
|
/*
|
|
|
|
|
* UDC layer requires to provide a buffer even for ZLP, but should
|
|
|
|
|
* not use it at all. Let's provide some poisoned pointer to catch
|
|
|
|
|
* possible bug in the driver.
|
|
|
|
|
*/
|
|
|
|
|
if (req->buf == NULL)
|
|
|
|
|
req->buf = (void *)0xDEADBABE;
|
|
|
|
|
|
2013-11-14 14:32:02 -08:00
|
|
|
|
reinit_completion(&ffs->ep0req_completion);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
usb_ep_dequeue(ffs->gadget->ep0, req);
|
|
|
|
|
return -EINTR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs->setup_state = FFS_NO_SETUP;
|
2014-02-10 10:42:42 +01:00
|
|
|
|
return req->status ? req->status : req->actual;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __ffs_ep0_stall(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
if (ffs->ev.can_stall) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("ep0 stall\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
usb_ep_set_halt(ffs->gadget->ep0);
|
|
|
|
|
ffs->setup_state = FFS_NO_SETUP;
|
|
|
|
|
return -EL2HLT;
|
|
|
|
|
} else {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_debug("bogus ep0 stall!\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ESRCH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|
|
|
|
size_t len, loff_t *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = file->private_data;
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
char *data;
|
|
|
|
|
|
|
|
|
|
/* Fast check if setup was canceled */
|
2014-02-10 10:42:41 +01:00
|
|
|
|
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EIDRM;
|
|
|
|
|
|
|
|
|
|
/* Acquire mutex */
|
|
|
|
|
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Check state */
|
|
|
|
|
switch (ffs->state) {
|
|
|
|
|
case FFS_READ_DESCRIPTORS:
|
|
|
|
|
case FFS_READ_STRINGS:
|
|
|
|
|
/* Copy data */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (len < 16) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = ffs_prepare_buffer(buf, len);
|
2010-12-09 15:52:39 +01:00
|
|
|
|
if (IS_ERR(data)) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = PTR_ERR(data);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle data */
|
|
|
|
|
if (ffs->state == FFS_READ_DESCRIPTORS) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_info("read descriptors\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = __ffs_data_got_descs(ffs, data, len);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ffs->state = FFS_READ_STRINGS;
|
|
|
|
|
ret = len;
|
|
|
|
|
} else {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_info("read strings\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = __ffs_data_got_strings(ffs, data, len);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ret = ffs_epfiles_create(ffs);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs->state = FFS_ACTIVE;
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
ret = ffs_ready(ffs);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_ACTIVE:
|
|
|
|
|
data = NULL;
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* We're called from user space, we can use _irq
|
|
|
|
|
* rather then _irqsave
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
spin_lock_irq(&ffs->ev.waitq.lock);
|
2014-02-10 10:42:41 +01:00
|
|
|
|
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
2014-02-10 10:42:40 +01:00
|
|
|
|
case FFS_SETUP_CANCELLED:
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EIDRM;
|
|
|
|
|
goto done_spin;
|
|
|
|
|
|
|
|
|
|
case FFS_NO_SETUP:
|
|
|
|
|
ret = -ESRCH;
|
|
|
|
|
goto done_spin;
|
|
|
|
|
|
|
|
|
|
case FFS_SETUP_PENDING:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FFS_SETUP_PENDING */
|
|
|
|
|
if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
ret = __ffs_ep0_stall(ffs);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FFS_SETUP_PENDING and not stall */
|
2024-11-12 20:58:10 +05:00
|
|
|
|
len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
|
|
|
|
data = ffs_prepare_buffer(buf, len);
|
2010-12-09 15:52:39 +01:00
|
|
|
|
if (IS_ERR(data)) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = PTR_ERR(data);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* We are guaranteed to be still in FFS_ACTIVE state
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* but the state of setup could have changed from
|
2014-02-10 10:42:40 +01:00
|
|
|
|
* FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* to check for that. If that happened we copied data
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* from user space in vain but it's unlikely.
|
|
|
|
|
*
|
|
|
|
|
* For sure we are not in FFS_NO_SETUP since this is
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
|
|
|
|
|
* transition can be performed and it's protected by
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* mutex.
|
|
|
|
|
*/
|
2014-02-10 10:42:41 +01:00
|
|
|
|
if (ffs_setup_state_clear_cancelled(ffs) ==
|
|
|
|
|
FFS_SETUP_CANCELLED) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EIDRM;
|
|
|
|
|
done_spin:
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
} else {
|
|
|
|
|
/* unlocks spinlock */
|
|
|
|
|
ret = __ffs_ep0_queue_wait(ffs, data, len);
|
|
|
|
|
}
|
|
|
|
|
kfree(data);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ret = -EBADFD;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-10 17:50:24 +02:00
|
|
|
|
/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
|
|
|
|
|
size_t n)
|
2017-11-28 15:20:53 +00:00
|
|
|
|
__releases(&ffs->ev.waitq.lock)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
2014-09-10 17:50:24 +02:00
|
|
|
|
* n cannot be bigger than ffs->ev.count, which cannot be bigger than
|
|
|
|
|
* size of ffs->ev.types array (which is four) so that's how much space
|
|
|
|
|
* we reserve.
|
2010-11-12 14:29:28 +01:00
|
|
|
|
*/
|
2014-09-10 17:50:24 +02:00
|
|
|
|
struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
|
|
|
|
|
const size_t size = n * sizeof *events;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
unsigned i = 0;
|
|
|
|
|
|
2014-09-10 17:50:24 +02:00
|
|
|
|
memset(events, 0, size);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
events[i].type = ffs->ev.types[i];
|
|
|
|
|
if (events[i].type == FUNCTIONFS_SETUP) {
|
|
|
|
|
events[i].u.setup = ffs->ev.setup;
|
|
|
|
|
ffs->setup_state = FFS_SETUP_PENDING;
|
|
|
|
|
}
|
|
|
|
|
} while (++i < n);
|
|
|
|
|
|
2014-09-10 17:50:24 +02:00
|
|
|
|
ffs->ev.count -= n;
|
|
|
|
|
if (ffs->ev.count)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
memmove(ffs->ev.types, ffs->ev.types + n,
|
|
|
|
|
ffs->ev.count * sizeof *ffs->ev.types);
|
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
return copy_to_user(buf, events, size) ? -EFAULT : size;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|
|
|
|
size_t len, loff_t *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = file->private_data;
|
|
|
|
|
char *data = NULL;
|
|
|
|
|
size_t n;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* Fast check if setup was canceled */
|
2014-02-10 10:42:41 +01:00
|
|
|
|
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EIDRM;
|
|
|
|
|
|
|
|
|
|
/* Acquire mutex */
|
|
|
|
|
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Check state */
|
|
|
|
|
if (ffs->state != FFS_ACTIVE) {
|
|
|
|
|
ret = -EBADFD;
|
|
|
|
|
goto done_mutex;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* We're called from user space, we can use _irq rather then
|
|
|
|
|
* _irqsave
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
spin_lock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
2014-02-10 10:42:41 +01:00
|
|
|
|
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
2014-02-10 10:42:40 +01:00
|
|
|
|
case FFS_SETUP_CANCELLED:
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EIDRM;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_NO_SETUP:
|
|
|
|
|
n = len / sizeof(struct usb_functionfs_event);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!n) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
|
|
|
|
|
ret = -EAGAIN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
|
|
|
|
|
ffs->ev.count)) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EINTR;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-28 15:20:53 +00:00
|
|
|
|
/* unlocks spinlock */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return __ffs_ep0_read_events(ffs, buf,
|
2024-11-12 20:58:10 +05:00
|
|
|
|
min_t(size_t, n, ffs->ev.count));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
case FFS_SETUP_PENDING:
|
|
|
|
|
if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
ret = __ffs_ep0_stall(ffs);
|
|
|
|
|
goto done_mutex;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 20:58:10 +05:00
|
|
|
|
len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (len) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
data = kmalloc(len, GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!data) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto done_mutex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
|
|
|
|
|
/* See ffs_ep0_write() */
|
2014-02-10 10:42:41 +01:00
|
|
|
|
if (ffs_setup_state_clear_cancelled(ffs) ==
|
|
|
|
|
FFS_SETUP_CANCELLED) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EIDRM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* unlocks spinlock */
|
|
|
|
|
ret = __ffs_ep0_queue_wait(ffs, data, len);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if ((ret > 0) && (copy_to_user(buf, data, len)))
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
goto done_mutex;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ret = -EBADFD;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&ffs->ev.waitq.lock);
|
|
|
|
|
done_mutex:
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
|
|
|
kfree(data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_ep0_open(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = inode->i_private;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ffs->state == FFS_CLOSING)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
|
|
file->private_data = ffs;
|
|
|
|
|
ffs_data_opened(ffs);
|
|
|
|
|
|
2021-11-12 15:54:40 +05:30
|
|
|
|
return stream_open(inode, file);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_ep0_release(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = file->private_data;
|
|
|
|
|
|
|
|
|
|
ffs_data_closed(ffs);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = file->private_data;
|
|
|
|
|
struct usb_gadget *gadget = ffs->gadget;
|
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
|
|
if (code == FUNCTIONFS_INTERFACE_REVMAP) {
|
|
|
|
|
struct ffs_function *func = ffs->func;
|
|
|
|
|
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
|
2012-03-28 09:30:50 +02:00
|
|
|
|
} else if (gadget && gadget->ops->ioctl) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = gadget->ops->ioctl(gadget, code, value);
|
|
|
|
|
} else {
|
|
|
|
|
ret = -ENOTTY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 06:39:46 -04:00
|
|
|
|
static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait)
|
2014-02-10 10:42:43 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = file->private_data;
|
2018-02-11 14:34:03 -08:00
|
|
|
|
__poll_t mask = EPOLLWRNORM;
|
2014-02-10 10:42:43 +01:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
poll_wait(file, &ffs->ev.waitq, wait);
|
|
|
|
|
|
|
|
|
|
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2014-02-10 10:42:43 +01:00
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
|
|
switch (ffs->state) {
|
|
|
|
|
case FFS_READ_DESCRIPTORS:
|
|
|
|
|
case FFS_READ_STRINGS:
|
2018-02-11 14:34:03 -08:00
|
|
|
|
mask |= EPOLLOUT;
|
2014-02-10 10:42:43 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_ACTIVE:
|
|
|
|
|
switch (ffs->setup_state) {
|
|
|
|
|
case FFS_NO_SETUP:
|
|
|
|
|
if (ffs->ev.count)
|
2018-02-11 14:34:03 -08:00
|
|
|
|
mask |= EPOLLIN;
|
2014-02-10 10:42:43 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_SETUP_PENDING:
|
|
|
|
|
case FFS_SETUP_CANCELLED:
|
2018-02-11 14:34:03 -08:00
|
|
|
|
mask |= (EPOLLIN | EPOLLOUT);
|
2014-02-10 10:42:43 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-11-20 12:40:27 -06:00
|
|
|
|
break;
|
|
|
|
|
|
2014-02-10 10:42:43 +01:00
|
|
|
|
case FFS_CLOSING:
|
|
|
|
|
break;
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
case FFS_DEACTIVATED:
|
|
|
|
|
break;
|
2014-02-10 10:42:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static const struct file_operations ffs_ep0_operations = {
|
|
|
|
|
|
|
|
|
|
.open = ffs_ep0_open,
|
|
|
|
|
.write = ffs_ep0_write,
|
|
|
|
|
.read = ffs_ep0_read,
|
|
|
|
|
.release = ffs_ep0_release,
|
|
|
|
|
.unlocked_ioctl = ffs_ep0_ioctl,
|
2014-02-10 10:42:43 +01:00
|
|
|
|
.poll = ffs_ep0_poll,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* "Normal" endpoints operations ********************************************/
|
|
|
|
|
|
|
|
|
|
static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
|
|
|
|
|
{
|
2022-06-10 20:17:57 +08:00
|
|
|
|
struct ffs_io_data *io_data = req->context;
|
|
|
|
|
|
|
|
|
|
if (req->status)
|
|
|
|
|
io_data->status = req->status;
|
|
|
|
|
else
|
|
|
|
|
io_data->status = req->actual;
|
|
|
|
|
|
|
|
|
|
complete(&io_data->done);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-21 20:47:34 +02:00
|
|
|
|
static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
|
|
|
|
|
{
|
|
|
|
|
ssize_t ret = copy_to_iter(data, data_len, iter);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret == data_len)
|
2016-05-21 20:47:34 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (iov_iter_count(iter))
|
2016-05-21 20:47:34 +02:00
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Dear user space developer!
|
|
|
|
|
*
|
|
|
|
|
* TL;DR: To stop getting below error message in your kernel log, change
|
|
|
|
|
* user space code using functionfs to align read buffers to a max
|
|
|
|
|
* packet size.
|
|
|
|
|
*
|
|
|
|
|
* Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max
|
|
|
|
|
* packet size. When unaligned buffer is passed to functionfs, it
|
|
|
|
|
* internally uses a larger, aligned buffer so that such UDCs are happy.
|
|
|
|
|
*
|
|
|
|
|
* Unfortunately, this means that host may send more data than was
|
|
|
|
|
* requested in read(2) system call. f_fs doesn’t know what to do with
|
|
|
|
|
* that excess data so it simply drops it.
|
|
|
|
|
*
|
|
|
|
|
* Was the buffer aligned in the first place, no such problem would
|
|
|
|
|
* happen.
|
|
|
|
|
*
|
2016-05-21 20:47:35 +02:00
|
|
|
|
* Data may be dropped only in AIO reads. Synchronous reads are handled
|
|
|
|
|
* by splitting a request into multiple parts. This splitting may still
|
|
|
|
|
* be a problem though so it’s likely best to align the buffer
|
|
|
|
|
* regardless of it being AIO or not..
|
|
|
|
|
*
|
2016-05-21 20:47:34 +02:00
|
|
|
|
* This only affects OUT endpoints, i.e. reading data with a read(2),
|
|
|
|
|
* aio_read(2) etc. system calls. Writing data to an IN endpoint is not
|
|
|
|
|
* affected.
|
|
|
|
|
*/
|
|
|
|
|
pr_err("functionfs read size %d > requested size %zd, dropping excess data. "
|
|
|
|
|
"Align read buffer size to max packet size to avoid the problem.\n",
|
|
|
|
|
data_len, ret);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 10:47:48 +01:00
|
|
|
|
/*
|
|
|
|
|
* allocate a virtually contiguous buffer and create a scatterlist describing it
|
|
|
|
|
* @sg_table - pointer to a place to be filled with sg_table contents
|
|
|
|
|
* @size - required buffer size
|
|
|
|
|
*/
|
|
|
|
|
static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
|
|
|
|
|
{
|
|
|
|
|
struct page **pages;
|
|
|
|
|
void *vaddr, *ptr;
|
|
|
|
|
unsigned int n_pages;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
vaddr = vmalloc(sz);
|
|
|
|
|
if (!vaddr)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
|
|
|
|
|
pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
|
|
|
|
|
if (!pages) {
|
|
|
|
|
vfree(vaddr);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE)
|
|
|
|
|
pages[i] = vmalloc_to_page(ptr);
|
|
|
|
|
|
|
|
|
|
if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
|
|
|
|
|
kvfree(pages);
|
|
|
|
|
vfree(vaddr);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
kvfree(pages);
|
|
|
|
|
|
|
|
|
|
return vaddr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
|
|
|
|
|
size_t data_len)
|
|
|
|
|
{
|
|
|
|
|
if (io_data->use_sg)
|
|
|
|
|
return ffs_build_sg_list(&io_data->sgt, data_len);
|
|
|
|
|
|
|
|
|
|
return kmalloc(data_len, GFP_KERNEL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void ffs_free_buffer(struct ffs_io_data *io_data)
|
|
|
|
|
{
|
|
|
|
|
if (!io_data->buf)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (io_data->use_sg) {
|
|
|
|
|
sg_free_table(&io_data->sgt);
|
|
|
|
|
vfree(io_data->buf);
|
|
|
|
|
} else {
|
|
|
|
|
kfree(io_data->buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
static void ffs_user_copy_worker(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
|
|
|
|
work);
|
2022-11-24 17:04:28 +00:00
|
|
|
|
int ret = io_data->status;
|
2016-04-14 17:01:17 +02:00
|
|
|
|
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
|
|
|
|
if (io_data->read && ret > 0) {
|
2020-06-10 18:42:06 -07:00
|
|
|
|
kthread_use_mm(io_data->mm);
|
2016-05-21 20:47:34 +02:00
|
|
|
|
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
|
2020-06-10 18:42:06 -07:00
|
|
|
|
kthread_unuse_mm(io_data->mm);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-21 09:22:35 -06:00
|
|
|
|
io_data->kiocb->ki_complete(io_data->kiocb, ret);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-04-14 17:01:17 +02:00
|
|
|
|
if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
|
2023-11-22 13:48:23 +01:00
|
|
|
|
eventfd_signal(io_data->ffs->ffs_eventfd);
|
2015-01-23 13:41:01 +01:00
|
|
|
|
|
usb: gadget: f_fs: Fix race between aio_cancel() and AIO request complete
FFS based applications can utilize the aio_cancel() callback to dequeue
pending USB requests submitted to the UDC. There is a scenario where the
FFS application issues an AIO cancel call, while the UDC is handling a
soft disconnect. For a DWC3 based implementation, the callstack looks
like the following:
DWC3 Gadget FFS Application
dwc3_gadget_soft_disconnect() ...
--> dwc3_stop_active_transfers()
--> dwc3_gadget_giveback(-ESHUTDOWN)
--> ffs_epfile_async_io_complete() ffs_aio_cancel()
--> usb_ep_free_request() --> usb_ep_dequeue()
There is currently no locking implemented between the AIO completion
handler and AIO cancel, so the issue occurs if the completion routine is
running in parallel to an AIO cancel call coming from the FFS application.
As the completion call frees the USB request (io_data->req) the FFS
application is also referencing it for the usb_ep_dequeue() call. This can
lead to accessing a stale/hanging pointer.
commit b566d38857fc ("usb: gadget: f_fs: use io_data->status consistently")
relocated the usb_ep_free_request() into ffs_epfile_async_io_complete().
However, in order to properly implement locking to mitigate this issue, the
spinlock can't be added to ffs_epfile_async_io_complete(), as
usb_ep_dequeue() (if successfully dequeuing a USB request) will call the
function driver's completion handler in the same context. Hence, leading
into a deadlock.
Fix this issue by moving the usb_ep_free_request() back to
ffs_user_copy_worker(), and ensuring that it explicitly sets io_data->req
to NULL after freeing it within the ffs->eps_lock. This resolves the race
condition above, as the ffs_aio_cancel() routine will not continue
attempting to dequeue a request that has already been freed, or the
ffs_user_copy_work() not freeing the USB request until the AIO cancel is
done referencing it.
This fix depends on
commit b566d38857fc ("usb: gadget: f_fs: use io_data->status
consistently")
Fixes: 2e4c7553cd6f ("usb: gadget: f_fs: add aio support")
Cc: stable <stable@kernel.org> # b566d38857fc ("usb: gadget: f_fs: use io_data->status consistently")
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
Link: https://lore.kernel.org/r/20240409014059.6740-1-quic_wcheng@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-08 18:40:59 -07:00
|
|
|
|
usb_ep_free_request(io_data->ep, io_data->req);
|
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
if (io_data->read)
|
2015-01-31 23:23:35 -05:00
|
|
|
|
kfree(io_data->to_free);
|
2018-11-14 10:47:48 +01:00
|
|
|
|
ffs_free_buffer(io_data);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
kfree(io_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
|
|
|
|
struct usb_request *req)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_io_data *io_data = req->context;
|
2017-09-12 10:24:40 +01:00
|
|
|
|
struct ffs_data *ffs = io_data->ffs;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2022-11-24 17:04:28 +00:00
|
|
|
|
io_data->status = req->status ? req->status : req->actual;
|
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
INIT_WORK(&io_data->work, ffs_user_copy_worker);
|
2017-09-12 10:24:40 +01:00
|
|
|
|
queue_work(ffs->io_completion_wq, &io_data->work);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-04 02:07:34 +02:00
|
|
|
|
static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* See comment in struct ffs_epfile for full read_buffer pointer
|
|
|
|
|
* synchronisation story.
|
|
|
|
|
*/
|
|
|
|
|
struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
|
|
|
|
|
if (buf && buf != READ_BUFFER_DROP)
|
|
|
|
|
kfree(buf);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-21 20:47:35 +02:00
|
|
|
|
/* Assumes epfile->mutex is held. */
|
|
|
|
|
static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
|
|
|
|
|
struct iov_iter *iter)
|
|
|
|
|
{
|
2016-10-04 02:07:34 +02:00
|
|
|
|
/*
|
|
|
|
|
* Null out epfile->read_buffer so ffs_func_eps_disable does not free
|
|
|
|
|
* the buffer while we are using it. See comment in struct ffs_epfile
|
|
|
|
|
* for full read_buffer pointer synchronisation story.
|
|
|
|
|
*/
|
|
|
|
|
struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
|
2016-05-21 20:47:35 +02:00
|
|
|
|
ssize_t ret;
|
2016-10-04 02:07:34 +02:00
|
|
|
|
if (!buf || buf == READ_BUFFER_DROP)
|
2016-05-21 20:47:35 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
ret = copy_to_iter(buf->data, buf->length, iter);
|
|
|
|
|
if (buf->length == ret) {
|
|
|
|
|
kfree(buf);
|
2016-10-04 02:07:34 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (iov_iter_count(iter)) {
|
2016-05-21 20:47:35 +02:00
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
} else {
|
|
|
|
|
buf->length -= ret;
|
|
|
|
|
buf->data += ret;
|
|
|
|
|
}
|
2016-10-04 02:07:34 +02:00
|
|
|
|
|
|
|
|
|
if (cmpxchg(&epfile->read_buffer, NULL, buf))
|
|
|
|
|
kfree(buf);
|
|
|
|
|
|
2016-05-21 20:47:35 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Assumes epfile->mutex is held. */
|
|
|
|
|
static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
|
|
|
|
|
void *data, int data_len,
|
|
|
|
|
struct iov_iter *iter)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_buffer *buf;
|
|
|
|
|
|
|
|
|
|
ssize_t ret = copy_to_iter(data, data_len, iter);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (data_len == ret)
|
2016-05-21 20:47:35 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (iov_iter_count(iter))
|
2016-05-21 20:47:35 +02:00
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
|
|
/* See ffs_copy_to_iter for more context. */
|
|
|
|
|
pr_warn("functionfs read size %d > requested size %zd, splitting request into multiple reads.",
|
|
|
|
|
data_len, ret);
|
|
|
|
|
|
|
|
|
|
data_len -= ret;
|
2022-01-20 16:29:33 -06:00
|
|
|
|
buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
|
2016-06-24 15:23:16 +03:00
|
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
2016-05-21 20:47:35 +02:00
|
|
|
|
buf->length = data_len;
|
|
|
|
|
buf->data = buf->storage;
|
2022-01-20 16:29:33 -06:00
|
|
|
|
memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
|
2016-10-04 02:07:34 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* At this point read_buffer is NULL or READ_BUFFER_DROP (if
|
|
|
|
|
* ffs_func_eps_disable has been called in the meanwhile). See comment
|
|
|
|
|
* in struct ffs_epfile for full read_buffer pointer synchronisation
|
|
|
|
|
* story.
|
|
|
|
|
*/
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (cmpxchg(&epfile->read_buffer, NULL, buf))
|
2016-10-04 02:07:34 +02:00
|
|
|
|
kfree(buf);
|
2016-05-21 20:47:35 +02:00
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 13:23:38 +01:00
|
|
|
|
static struct ffs_ep *ffs_epfile_wait_ep(struct file *file)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
|
|
|
|
struct ffs_ep *ep;
|
2024-01-30 13:23:38 +01:00
|
|
|
|
int ret;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-09 15:55:36 -08:00
|
|
|
|
/* Wait for endpoint to be enabled */
|
|
|
|
|
ep = epfile->ep;
|
|
|
|
|
if (!ep) {
|
2016-01-04 20:58:12 +01:00
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
2024-01-30 13:23:38 +01:00
|
|
|
|
return ERR_PTR(-EAGAIN);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2017-04-18 16:11:48 -07:00
|
|
|
|
ret = wait_event_interruptible(
|
|
|
|
|
epfile->ffs->wait, (ep = epfile->ep));
|
2016-01-04 20:58:12 +01:00
|
|
|
|
if (ret)
|
2024-01-30 13:23:38 +01:00
|
|
|
|
return ERR_PTR(-EINTR);
|
2013-12-09 15:55:36 -08:00
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2024-01-30 13:23:38 +01:00
|
|
|
|
return ep;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
|
|
|
|
struct usb_request *req;
|
|
|
|
|
struct ffs_ep *ep;
|
|
|
|
|
char *data = NULL;
|
|
|
|
|
ssize_t ret, data_len = -EINVAL;
|
|
|
|
|
int halt;
|
|
|
|
|
|
|
|
|
|
/* Are we still active? */
|
|
|
|
|
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
ep = ffs_epfile_wait_ep(file);
|
|
|
|
|
if (IS_ERR(ep))
|
|
|
|
|
return PTR_ERR(ep);
|
|
|
|
|
|
2013-12-09 15:55:36 -08:00
|
|
|
|
/* Do we halt? */
|
2014-02-10 10:42:44 +01:00
|
|
|
|
halt = (!io_data->read == !epfile->in);
|
2016-01-04 20:58:12 +01:00
|
|
|
|
if (halt && epfile->isoc)
|
|
|
|
|
return -EINVAL;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2016-05-21 20:47:35 +02:00
|
|
|
|
/* We will be using request and read_buffer */
|
|
|
|
|
ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret)
|
2016-05-21 20:47:35 +02:00
|
|
|
|
goto error;
|
|
|
|
|
|
2013-12-09 15:55:36 -08:00
|
|
|
|
/* Allocate & copy */
|
|
|
|
|
if (!halt) {
|
2016-05-21 20:47:35 +02:00
|
|
|
|
struct usb_gadget *gadget;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do we have buffered data from previous partial read? Check
|
|
|
|
|
* that for synchronous case only because we do not have
|
|
|
|
|
* facility to ‘wake up’ a pending asynchronous read and push
|
|
|
|
|
* buffered data to it which we would need to make things behave
|
|
|
|
|
* consistently.
|
|
|
|
|
*/
|
|
|
|
|
if (!io_data->aio && io_data->read) {
|
|
|
|
|
ret = __ffs_epfile_read_buffered(epfile, &io_data->data);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto error_mutex;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-20 08:33:50 +01:00
|
|
|
|
/*
|
|
|
|
|
* if we _do_ wait above, the epfile->ffs->gadget might be NULL
|
2016-01-04 21:05:59 +01:00
|
|
|
|
* before the waiting completes, so do not assign to 'gadget'
|
|
|
|
|
* earlier
|
2014-01-20 08:33:50 +01:00
|
|
|
|
*/
|
2016-05-21 20:47:35 +02:00
|
|
|
|
gadget = epfile->ffs->gadget;
|
2014-01-20 08:33:50 +01:00
|
|
|
|
|
2014-04-14 11:19:53 +08:00
|
|
|
|
spin_lock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
/* In the meantime, endpoint got disabled or changed. */
|
|
|
|
|
if (epfile->ep != ep) {
|
2016-05-21 20:47:35 +02:00
|
|
|
|
ret = -ESHUTDOWN;
|
|
|
|
|
goto error_lock;
|
2014-04-14 11:19:53 +08:00
|
|
|
|
}
|
2015-01-31 23:23:35 -05:00
|
|
|
|
data_len = iov_iter_count(&io_data->data);
|
2013-12-09 15:55:37 -08:00
|
|
|
|
/*
|
|
|
|
|
* Controller may require buffer size to be aligned to
|
|
|
|
|
* maxpacketsize of an out endpoint.
|
|
|
|
|
*/
|
2015-01-31 23:23:35 -05:00
|
|
|
|
if (io_data->read)
|
|
|
|
|
data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
|
2019-06-12 15:13:26 -07:00
|
|
|
|
|
|
|
|
|
io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
|
2014-04-14 11:19:53 +08:00
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
2013-12-09 15:55:37 -08:00
|
|
|
|
|
2018-11-14 10:47:48 +01:00
|
|
|
|
data = ffs_alloc_buffer(io_data, data_len);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!data) {
|
2016-05-21 20:47:35 +02:00
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto error_mutex;
|
|
|
|
|
}
|
|
|
|
|
if (!io_data->read &&
|
2016-11-01 22:09:04 -04:00
|
|
|
|
!copy_from_iter_full(data, data_len, &io_data->data)) {
|
2016-05-21 20:47:35 +02:00
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
goto error_mutex;
|
2013-12-09 15:55:36 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-09 15:55:36 -08:00
|
|
|
|
spin_lock_irq(&epfile->ffs->eps_lock);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-09 15:55:36 -08:00
|
|
|
|
if (epfile->ep != ep) {
|
|
|
|
|
/* In the meantime, endpoint got disabled or changed. */
|
|
|
|
|
ret = -ESHUTDOWN;
|
|
|
|
|
} else if (halt) {
|
2017-08-11 15:46:08 -07:00
|
|
|
|
ret = usb_ep_set_halt(ep->ep);
|
|
|
|
|
if (!ret)
|
|
|
|
|
ret = -EBADMSG;
|
2020-11-27 15:05:59 +01:00
|
|
|
|
} else if (data_len == -EINVAL) {
|
2014-10-13 11:15:54 -07:00
|
|
|
|
/*
|
|
|
|
|
* Sanity Check: even though data_len can't be used
|
|
|
|
|
* uninitialized at the time I write this comment, some
|
|
|
|
|
* compilers complain about this situation.
|
|
|
|
|
* In order to keep the code clean from warnings, data_len is
|
|
|
|
|
* being initialized to -EINVAL during its declaration, which
|
|
|
|
|
* means we can't rely on compiler anymore to warn no future
|
|
|
|
|
* changes won't result in data_len being used uninitialized.
|
|
|
|
|
* For such reason, we're adding this redundant sanity check
|
|
|
|
|
* here.
|
|
|
|
|
*/
|
2016-01-04 21:05:59 +01:00
|
|
|
|
WARN(1, "%s: data_len == -EINVAL\n", __func__);
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
} else if (!io_data->aio) {
|
usb: f_fs: avoid race condition with ffs_epfile_io_complete
ffs_epfile_io and ffs_epfile_io_complete runs in different context, but
there is no synchronization between them.
consider the following scenario:
1) ffs_epfile_io interrupted by sigal while
wait_for_completion_interruptible
2) then ffs_epfile_io set ret to -EINTR
3) just before or during usb_ep_dequeue, the request completed
4) ffs_epfile_io return with -EINTR
In this case, ffs_epfile_io tell caller no transfer success but actually
it may has been done. This break the caller's pipe.
Below script can help test it (adbd is the process which lies on f_fs).
while true
do
pkill -19 adbd #SIGSTOP
pkill -18 adbd #SIGCONT
sleep 0.1
done
To avoid this, just dequeue the request first. After usb_ep_dequeue, the
request must be done or canceled.
With this change, we can ensure no race condition in f_fs driver. But
actually I found some of the udc driver has analogical issue in its
dequeue implementation. For example,
1) the dequeue function hold the controller's lock.
2) before driver request controller to stop transfer, a request
completed.
3) the controller trigger a interrupt, but its irq handler need wait
dequeue function to release the lock.
4) dequeue function give back the request with negative status, and
release lock.
5) irq handler get lock but the request has already been given back.
So, the dequeue implementation should take care of this case. IMO, it
can be done as below steps to dequeue a already started request,
1) request controller to stop transfer on the given ep. HW know the
actual transfer status.
2) after hw stop transfer, driver scan if there are any completed one.
3) if found, process it with real status. if no, the request can
canceled.
Signed-off-by: "Du, Changbin" <changbin.du@intel.com>
[mina86@mina86.com: rebased on top of refactoring commits]
Signed-off-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
2015-12-29 14:36:58 +08:00
|
|
|
|
bool interrupted = false;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
req = ep->req;
|
2018-11-14 10:47:48 +01:00
|
|
|
|
if (io_data->use_sg) {
|
|
|
|
|
req->buf = NULL;
|
|
|
|
|
req->sg = io_data->sgt.sgl;
|
|
|
|
|
req->num_sgs = io_data->sgt.nents;
|
|
|
|
|
} else {
|
|
|
|
|
req->buf = data;
|
2019-12-12 16:35:03 +08:00
|
|
|
|
req->num_sgs = 0;
|
2018-11-14 10:47:48 +01:00
|
|
|
|
}
|
|
|
|
|
req->length = data_len;
|
|
|
|
|
|
|
|
|
|
io_data->buf = data;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2022-06-10 20:17:57 +08:00
|
|
|
|
init_completion(&io_data->done);
|
|
|
|
|
req->context = io_data;
|
2016-01-04 21:05:59 +01:00
|
|
|
|
req->complete = ffs_epfile_io_complete;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2016-01-04 21:05:59 +01:00
|
|
|
|
goto error_lock;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2022-06-10 20:17:57 +08:00
|
|
|
|
if (wait_for_completion_interruptible(&io_data->done)) {
|
2022-06-10 20:17:58 +08:00
|
|
|
|
spin_lock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
if (epfile->ep != ep) {
|
|
|
|
|
ret = -ESHUTDOWN;
|
|
|
|
|
goto error_lock;
|
|
|
|
|
}
|
usb: f_fs: avoid race condition with ffs_epfile_io_complete
ffs_epfile_io and ffs_epfile_io_complete runs in different context, but
there is no synchronization between them.
consider the following scenario:
1) ffs_epfile_io interrupted by sigal while
wait_for_completion_interruptible
2) then ffs_epfile_io set ret to -EINTR
3) just before or during usb_ep_dequeue, the request completed
4) ffs_epfile_io return with -EINTR
In this case, ffs_epfile_io tell caller no transfer success but actually
it may has been done. This break the caller's pipe.
Below script can help test it (adbd is the process which lies on f_fs).
while true
do
pkill -19 adbd #SIGSTOP
pkill -18 adbd #SIGCONT
sleep 0.1
done
To avoid this, just dequeue the request first. After usb_ep_dequeue, the
request must be done or canceled.
With this change, we can ensure no race condition in f_fs driver. But
actually I found some of the udc driver has analogical issue in its
dequeue implementation. For example,
1) the dequeue function hold the controller's lock.
2) before driver request controller to stop transfer, a request
completed.
3) the controller trigger a interrupt, but its irq handler need wait
dequeue function to release the lock.
4) dequeue function give back the request with negative status, and
release lock.
5) irq handler get lock but the request has already been given back.
So, the dequeue implementation should take care of this case. IMO, it
can be done as below steps to dequeue a already started request,
1) request controller to stop transfer on the given ep. HW know the
actual transfer status.
2) after hw stop transfer, driver scan if there are any completed one.
3) if found, process it with real status. if no, the request can
canceled.
Signed-off-by: "Du, Changbin" <changbin.du@intel.com>
[mina86@mina86.com: rebased on top of refactoring commits]
Signed-off-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
2015-12-29 14:36:58 +08:00
|
|
|
|
/*
|
|
|
|
|
* To avoid race condition with ffs_epfile_io_complete,
|
|
|
|
|
* dequeue the request first then check
|
|
|
|
|
* status. usb_ep_dequeue API should guarantee no race
|
|
|
|
|
* condition with req->complete callback.
|
|
|
|
|
*/
|
2016-01-04 21:05:59 +01:00
|
|
|
|
usb_ep_dequeue(ep->ep, req);
|
2022-06-10 20:17:58 +08:00
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
2022-06-10 20:17:57 +08:00
|
|
|
|
wait_for_completion(&io_data->done);
|
|
|
|
|
interrupted = io_data->status < 0;
|
2016-01-04 21:05:59 +01:00
|
|
|
|
}
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-05-21 20:47:34 +02:00
|
|
|
|
if (interrupted)
|
|
|
|
|
ret = -EINTR;
|
2022-06-10 20:17:57 +08:00
|
|
|
|
else if (io_data->read && io_data->status > 0)
|
|
|
|
|
ret = __ffs_epfile_read_data(epfile, data, io_data->status,
|
2016-05-21 20:47:35 +02:00
|
|
|
|
&io_data->data);
|
2016-05-21 20:47:34 +02:00
|
|
|
|
else
|
2022-06-10 20:17:57 +08:00
|
|
|
|
ret = io_data->status;
|
2016-01-04 21:05:59 +01:00
|
|
|
|
goto error_mutex;
|
2017-11-26 06:52:53 +00:00
|
|
|
|
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
|
2016-01-04 21:05:59 +01:00
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
} else {
|
2018-11-14 10:47:48 +01:00
|
|
|
|
if (io_data->use_sg) {
|
|
|
|
|
req->buf = NULL;
|
|
|
|
|
req->sg = io_data->sgt.sgl;
|
|
|
|
|
req->num_sgs = io_data->sgt.nents;
|
|
|
|
|
} else {
|
|
|
|
|
req->buf = data;
|
2019-12-12 16:35:03 +08:00
|
|
|
|
req->num_sgs = 0;
|
2018-11-14 10:47:48 +01:00
|
|
|
|
}
|
|
|
|
|
req->length = data_len;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
io_data->buf = data;
|
|
|
|
|
io_data->ep = ep->ep;
|
|
|
|
|
io_data->req = req;
|
|
|
|
|
io_data->ffs = epfile->ffs;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
req->context = io_data;
|
|
|
|
|
req->complete = ffs_epfile_async_io_complete;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret) {
|
2020-03-26 17:26:20 +05:30
|
|
|
|
io_data->req = NULL;
|
2016-01-04 21:05:59 +01:00
|
|
|
|
usb_ep_free_request(ep->ep, req);
|
|
|
|
|
goto error_lock;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 21:05:59 +01:00
|
|
|
|
ret = -EIOCBQUEUED;
|
|
|
|
|
/*
|
|
|
|
|
* Do not kfree the buffer in this function. It will be freed
|
|
|
|
|
* by ffs_user_copy_worker.
|
|
|
|
|
*/
|
|
|
|
|
data = NULL;
|
|
|
|
|
}
|
2014-03-10 09:33:37 +01:00
|
|
|
|
|
|
|
|
|
error_lock:
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
2016-01-04 21:05:59 +01:00
|
|
|
|
error_mutex:
|
2014-03-10 09:33:37 +01:00
|
|
|
|
mutex_unlock(&epfile->mutex);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
error:
|
2019-03-19 22:32:20 -07:00
|
|
|
|
if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */
|
|
|
|
|
ffs_free_buffer(io_data);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ffs_epfile_open(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = inode->i_private;
|
|
|
|
|
|
|
|
|
|
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
file->private_data = epfile;
|
|
|
|
|
ffs_data_opened(epfile->ffs);
|
|
|
|
|
|
2021-11-12 15:54:40 +05:30
|
|
|
|
return stream_open(inode, file);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
static int ffs_aio_cancel(struct kiocb *kiocb)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_io_data *io_data = kiocb->private;
|
|
|
|
|
int value;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (io_data && io_data->ep && io_data->req)
|
2018-11-01 15:35:17 +05:30
|
|
|
|
value = usb_ep_dequeue(io_data->ep, io_data->req);
|
|
|
|
|
else
|
2014-02-10 10:42:44 +01:00
|
|
|
|
value = -EINVAL;
|
2018-11-01 15:35:17 +05:30
|
|
|
|
|
2014-02-10 10:42:44 +01:00
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
|
2014-02-10 10:42:44 +01:00
|
|
|
|
{
|
2015-01-31 23:55:39 -05:00
|
|
|
|
struct ffs_io_data io_data, *p = &io_data;
|
2015-01-31 23:42:34 -05:00
|
|
|
|
ssize_t res;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
if (!is_sync_kiocb(kiocb)) {
|
2019-06-03 19:05:28 +02:00
|
|
|
|
p = kzalloc(sizeof(io_data), GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!p)
|
2015-01-31 23:55:39 -05:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
p->aio = true;
|
|
|
|
|
} else {
|
2019-06-03 19:05:28 +02:00
|
|
|
|
memset(p, 0, sizeof(*p));
|
2015-01-31 23:55:39 -05:00
|
|
|
|
p->aio = false;
|
|
|
|
|
}
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
p->read = false;
|
|
|
|
|
p->kiocb = kiocb;
|
|
|
|
|
p->data = *from;
|
|
|
|
|
p->mm = current->mm;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
kiocb->private = p;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-05-18 16:02:07 +01:00
|
|
|
|
if (p->aio)
|
|
|
|
|
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
res = ffs_epfile_io(kiocb->ki_filp, p);
|
|
|
|
|
if (res == -EIOCBQUEUED)
|
|
|
|
|
return res;
|
|
|
|
|
if (p->aio)
|
|
|
|
|
kfree(p);
|
|
|
|
|
else
|
|
|
|
|
*from = p->data;
|
2015-01-31 23:42:34 -05:00
|
|
|
|
return res;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
|
2014-02-10 10:42:44 +01:00
|
|
|
|
{
|
2015-01-31 23:55:39 -05:00
|
|
|
|
struct ffs_io_data io_data, *p = &io_data;
|
2015-01-31 23:42:34 -05:00
|
|
|
|
ssize_t res;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
if (!is_sync_kiocb(kiocb)) {
|
2019-06-03 19:05:28 +02:00
|
|
|
|
p = kzalloc(sizeof(io_data), GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!p)
|
2015-01-31 23:55:39 -05:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
p->aio = true;
|
|
|
|
|
} else {
|
2019-06-03 19:05:28 +02:00
|
|
|
|
memset(p, 0, sizeof(*p));
|
2015-01-31 23:55:39 -05:00
|
|
|
|
p->aio = false;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
p->read = true;
|
|
|
|
|
p->kiocb = kiocb;
|
|
|
|
|
if (p->aio) {
|
|
|
|
|
p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
|
2023-04-01 06:05:08 +00:00
|
|
|
|
if (!iter_is_ubuf(&p->data) && !p->to_free) {
|
2015-01-31 23:55:39 -05:00
|
|
|
|
kfree(p);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
p->data = *to;
|
|
|
|
|
p->to_free = NULL;
|
|
|
|
|
}
|
|
|
|
|
p->mm = current->mm;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
kiocb->private = p;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-05-18 16:02:07 +01:00
|
|
|
|
if (p->aio)
|
|
|
|
|
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
2014-02-10 10:42:44 +01:00
|
|
|
|
|
2015-01-31 23:55:39 -05:00
|
|
|
|
res = ffs_epfile_io(kiocb->ki_filp, p);
|
|
|
|
|
if (res == -EIOCBQUEUED)
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
|
|
if (p->aio) {
|
|
|
|
|
kfree(p->to_free);
|
|
|
|
|
kfree(p);
|
|
|
|
|
} else {
|
|
|
|
|
*to = p->data;
|
2015-01-31 23:42:34 -05:00
|
|
|
|
}
|
|
|
|
|
return res;
|
2014-02-10 10:42:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 13:23:39 +01:00
|
|
|
|
static void ffs_dmabuf_release(struct kref *ref)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dmabuf_priv *priv = container_of(ref, struct ffs_dmabuf_priv, ref);
|
|
|
|
|
struct dma_buf_attachment *attach = priv->attach;
|
|
|
|
|
struct dma_buf *dmabuf = attach->dmabuf;
|
|
|
|
|
|
|
|
|
|
pr_vdebug("FFS DMABUF release\n");
|
|
|
|
|
dma_resv_lock(dmabuf->resv, NULL);
|
|
|
|
|
dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
|
|
|
|
|
dma_resv_unlock(dmabuf->resv);
|
|
|
|
|
|
|
|
|
|
dma_buf_detach(attach->dmabuf, attach);
|
|
|
|
|
dma_buf_put(dmabuf);
|
|
|
|
|
kfree(priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_dmabuf_get(struct dma_buf_attachment *attach)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dmabuf_priv *priv = attach->importer_priv;
|
|
|
|
|
|
|
|
|
|
kref_get(&priv->ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_dmabuf_put(struct dma_buf_attachment *attach)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dmabuf_priv *priv = attach->importer_priv;
|
|
|
|
|
|
|
|
|
|
kref_put(&priv->ref, ffs_dmabuf_release);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int
|
|
|
|
|
ffs_epfile_release(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = inode->i_private;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
struct ffs_dmabuf_priv *priv, *tmp;
|
|
|
|
|
struct ffs_data *ffs = epfile->ffs;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&epfile->dmabufs_mutex);
|
|
|
|
|
|
|
|
|
|
/* Close all attached DMABUFs */
|
|
|
|
|
list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) {
|
|
|
|
|
/* Cancel any pending transfer */
|
|
|
|
|
spin_lock_irq(&ffs->eps_lock);
|
|
|
|
|
if (priv->ep && priv->req)
|
|
|
|
|
usb_ep_dequeue(priv->ep, priv->req);
|
|
|
|
|
spin_unlock_irq(&ffs->eps_lock);
|
|
|
|
|
|
|
|
|
|
list_del(&priv->entry);
|
|
|
|
|
ffs_dmabuf_put(priv->attach);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&epfile->dmabufs_mutex);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2016-10-04 02:07:34 +02:00
|
|
|
|
__ffs_epfile_read_buffer_free(epfile);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_data_closed(epfile->ffs);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 13:23:39 +01:00
|
|
|
|
static void ffs_dmabuf_cleanup(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dma_fence *dma_fence =
|
|
|
|
|
container_of(work, struct ffs_dma_fence, work);
|
|
|
|
|
struct ffs_dmabuf_priv *priv = dma_fence->priv;
|
|
|
|
|
struct dma_buf_attachment *attach = priv->attach;
|
|
|
|
|
struct dma_fence *fence = &dma_fence->base;
|
|
|
|
|
|
|
|
|
|
ffs_dmabuf_put(attach);
|
|
|
|
|
dma_fence_put(fence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_dmabuf_signal_done(struct ffs_dma_fence *dma_fence, int ret)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dmabuf_priv *priv = dma_fence->priv;
|
|
|
|
|
struct dma_fence *fence = &dma_fence->base;
|
|
|
|
|
bool cookie = dma_fence_begin_signalling();
|
|
|
|
|
|
|
|
|
|
dma_fence_get(fence);
|
|
|
|
|
fence->error = ret;
|
|
|
|
|
dma_fence_signal(fence);
|
|
|
|
|
dma_fence_end_signalling(cookie);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The fence will be unref'd in ffs_dmabuf_cleanup.
|
|
|
|
|
* It can't be done here, as the unref functions might try to lock
|
|
|
|
|
* the resv object, which would deadlock.
|
|
|
|
|
*/
|
|
|
|
|
INIT_WORK(&dma_fence->work, ffs_dmabuf_cleanup);
|
|
|
|
|
queue_work(priv->ffs->io_completion_wq, &dma_fence->work);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep,
|
|
|
|
|
struct usb_request *req)
|
|
|
|
|
{
|
|
|
|
|
pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status);
|
|
|
|
|
ffs_dmabuf_signal_done(req->context, req->status);
|
|
|
|
|
usb_ep_free_request(ep, req);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence)
|
|
|
|
|
{
|
|
|
|
|
return "functionfs";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *ffs_dmabuf_get_timeline_name(struct dma_fence *fence)
|
|
|
|
|
{
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_dmabuf_fence_release(struct dma_fence *fence)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dma_fence *dma_fence =
|
|
|
|
|
container_of(fence, struct ffs_dma_fence, base);
|
|
|
|
|
|
|
|
|
|
kfree(dma_fence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct dma_fence_ops ffs_dmabuf_fence_ops = {
|
|
|
|
|
.get_driver_name = ffs_dmabuf_get_driver_name,
|
|
|
|
|
.get_timeline_name = ffs_dmabuf_get_timeline_name,
|
|
|
|
|
.release = ffs_dmabuf_fence_release,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int ffs_dma_resv_lock(struct dma_buf *dmabuf, bool nonblock)
|
|
|
|
|
{
|
|
|
|
|
if (!nonblock)
|
|
|
|
|
return dma_resv_lock_interruptible(dmabuf->resv, NULL);
|
|
|
|
|
|
|
|
|
|
if (!dma_resv_trylock(dmabuf->resv))
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct dma_buf_attachment *
|
|
|
|
|
ffs_dmabuf_find_attachment(struct ffs_epfile *epfile, struct dma_buf *dmabuf)
|
|
|
|
|
{
|
|
|
|
|
struct device *dev = epfile->ffs->gadget->dev.parent;
|
|
|
|
|
struct dma_buf_attachment *attach = NULL;
|
|
|
|
|
struct ffs_dmabuf_priv *priv;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&epfile->dmabufs_mutex);
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(priv, &epfile->dmabufs, entry) {
|
|
|
|
|
if (priv->attach->dev == dev
|
|
|
|
|
&& priv->attach->dmabuf == dmabuf) {
|
|
|
|
|
attach = priv->attach;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attach)
|
|
|
|
|
ffs_dmabuf_get(attach);
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&epfile->dmabufs_mutex);
|
|
|
|
|
|
|
|
|
|
return attach ?: ERR_PTR(-EPERM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_dmabuf_attach(struct file *file, int fd)
|
|
|
|
|
{
|
|
|
|
|
bool nonblock = file->f_flags & O_NONBLOCK;
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
|
|
|
|
struct usb_gadget *gadget = epfile->ffs->gadget;
|
|
|
|
|
struct dma_buf_attachment *attach;
|
|
|
|
|
struct ffs_dmabuf_priv *priv;
|
|
|
|
|
enum dma_data_direction dir;
|
|
|
|
|
struct sg_table *sg_table;
|
|
|
|
|
struct dma_buf *dmabuf;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (!gadget || !gadget->sg_supported)
|
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
|
|
dmabuf = dma_buf_get(fd);
|
|
|
|
|
if (IS_ERR(dmabuf))
|
|
|
|
|
return PTR_ERR(dmabuf);
|
|
|
|
|
|
|
|
|
|
attach = dma_buf_attach(dmabuf, gadget->dev.parent);
|
|
|
|
|
if (IS_ERR(attach)) {
|
|
|
|
|
err = PTR_ERR(attach);
|
|
|
|
|
goto err_dmabuf_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
|
|
|
if (!priv) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto err_dmabuf_detach;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
|
|
|
|
|
|
|
|
|
err = ffs_dma_resv_lock(dmabuf, nonblock);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_free_priv;
|
|
|
|
|
|
|
|
|
|
sg_table = dma_buf_map_attachment(attach, dir);
|
|
|
|
|
dma_resv_unlock(dmabuf->resv);
|
|
|
|
|
|
|
|
|
|
if (IS_ERR(sg_table)) {
|
|
|
|
|
err = PTR_ERR(sg_table);
|
|
|
|
|
goto err_free_priv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attach->importer_priv = priv;
|
|
|
|
|
|
|
|
|
|
priv->sgt = sg_table;
|
|
|
|
|
priv->dir = dir;
|
|
|
|
|
priv->ffs = epfile->ffs;
|
|
|
|
|
priv->attach = attach;
|
|
|
|
|
spin_lock_init(&priv->lock);
|
|
|
|
|
kref_init(&priv->ref);
|
|
|
|
|
priv->context = dma_fence_context_alloc(1);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&epfile->dmabufs_mutex);
|
|
|
|
|
list_add(&priv->entry, &epfile->dmabufs);
|
|
|
|
|
mutex_unlock(&epfile->dmabufs_mutex);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_free_priv:
|
|
|
|
|
kfree(priv);
|
|
|
|
|
err_dmabuf_detach:
|
|
|
|
|
dma_buf_detach(dmabuf, attach);
|
|
|
|
|
err_dmabuf_put:
|
|
|
|
|
dma_buf_put(dmabuf);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_dmabuf_detach(struct file *file, int fd)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
|
|
|
|
struct ffs_data *ffs = epfile->ffs;
|
|
|
|
|
struct device *dev = ffs->gadget->dev.parent;
|
|
|
|
|
struct ffs_dmabuf_priv *priv, *tmp;
|
|
|
|
|
struct dma_buf *dmabuf;
|
|
|
|
|
int ret = -EPERM;
|
|
|
|
|
|
|
|
|
|
dmabuf = dma_buf_get(fd);
|
|
|
|
|
if (IS_ERR(dmabuf))
|
|
|
|
|
return PTR_ERR(dmabuf);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&epfile->dmabufs_mutex);
|
|
|
|
|
|
|
|
|
|
list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) {
|
|
|
|
|
if (priv->attach->dev == dev
|
|
|
|
|
&& priv->attach->dmabuf == dmabuf) {
|
|
|
|
|
/* Cancel any pending transfer */
|
|
|
|
|
spin_lock_irq(&ffs->eps_lock);
|
|
|
|
|
if (priv->ep && priv->req)
|
|
|
|
|
usb_ep_dequeue(priv->ep, priv->req);
|
|
|
|
|
spin_unlock_irq(&ffs->eps_lock);
|
|
|
|
|
|
|
|
|
|
list_del(&priv->entry);
|
|
|
|
|
|
|
|
|
|
/* Unref the reference from ffs_dmabuf_attach() */
|
|
|
|
|
ffs_dmabuf_put(priv->attach);
|
|
|
|
|
ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&epfile->dmabufs_mutex);
|
|
|
|
|
dma_buf_put(dmabuf);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_dmabuf_transfer(struct file *file,
|
|
|
|
|
const struct usb_ffs_dmabuf_transfer_req *req)
|
|
|
|
|
{
|
|
|
|
|
bool nonblock = file->f_flags & O_NONBLOCK;
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
|
|
|
|
struct dma_buf_attachment *attach;
|
|
|
|
|
struct ffs_dmabuf_priv *priv;
|
|
|
|
|
struct ffs_dma_fence *fence;
|
|
|
|
|
struct usb_request *usb_req;
|
2024-04-02 13:09:50 +02:00
|
|
|
|
enum dma_resv_usage resv_dir;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
struct dma_buf *dmabuf;
|
2024-04-02 13:09:51 +02:00
|
|
|
|
unsigned long timeout;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
struct ffs_ep *ep;
|
|
|
|
|
bool cookie;
|
|
|
|
|
u32 seqno;
|
2024-04-02 13:09:51 +02:00
|
|
|
|
long retl;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (req->flags & ~USB_FFS_DMABUF_TRANSFER_MASK)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
dmabuf = dma_buf_get(req->fd);
|
|
|
|
|
if (IS_ERR(dmabuf))
|
|
|
|
|
return PTR_ERR(dmabuf);
|
|
|
|
|
|
|
|
|
|
if (req->length > dmabuf->size || req->length == 0) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto err_dmabuf_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attach = ffs_dmabuf_find_attachment(epfile, dmabuf);
|
|
|
|
|
if (IS_ERR(attach)) {
|
|
|
|
|
ret = PTR_ERR(attach);
|
|
|
|
|
goto err_dmabuf_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
priv = attach->importer_priv;
|
|
|
|
|
|
|
|
|
|
ep = ffs_epfile_wait_ep(file);
|
|
|
|
|
if (IS_ERR(ep)) {
|
|
|
|
|
ret = PTR_ERR(ep);
|
|
|
|
|
goto err_attachment_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ffs_dma_resv_lock(dmabuf, nonblock);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto err_attachment_put;
|
|
|
|
|
|
|
|
|
|
/* Make sure we don't have writers */
|
2024-04-02 13:09:51 +02:00
|
|
|
|
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
|
|
|
|
|
retl = dma_resv_wait_timeout(dmabuf->resv,
|
|
|
|
|
dma_resv_usage_rw(epfile->in),
|
|
|
|
|
true, timeout);
|
|
|
|
|
if (retl == 0)
|
|
|
|
|
retl = -EBUSY;
|
|
|
|
|
if (retl < 0) {
|
|
|
|
|
ret = (int)retl;
|
2024-01-30 13:23:39 +01:00
|
|
|
|
goto err_resv_unlock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = dma_resv_reserve_fences(dmabuf->resv, 1);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto err_resv_unlock;
|
|
|
|
|
|
|
|
|
|
fence = kmalloc(sizeof(*fence), GFP_KERNEL);
|
|
|
|
|
if (!fence) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err_resv_unlock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fence->priv = priv;
|
|
|
|
|
|
|
|
|
|
spin_lock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
|
|
|
|
|
/* In the meantime, endpoint got disabled or changed. */
|
|
|
|
|
if (epfile->ep != ep) {
|
|
|
|
|
ret = -ESHUTDOWN;
|
|
|
|
|
goto err_fence_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usb_req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
|
|
|
|
|
if (!usb_req) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err_fence_put;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* usb_ep_queue() guarantees that all transfers are processed in the
|
|
|
|
|
* order they are enqueued, so we can use a simple incrementing
|
|
|
|
|
* sequence number for the dma_fence.
|
|
|
|
|
*/
|
|
|
|
|
seqno = atomic_add_return(1, &epfile->seqno);
|
|
|
|
|
|
|
|
|
|
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
|
|
|
|
|
&priv->lock, priv->context, seqno);
|
|
|
|
|
|
2024-04-02 13:09:50 +02:00
|
|
|
|
resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
|
|
|
|
|
|
|
|
|
|
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
|
2024-01-30 13:23:39 +01:00
|
|
|
|
dma_resv_unlock(dmabuf->resv);
|
|
|
|
|
|
|
|
|
|
/* Now that the dma_fence is in place, queue the transfer. */
|
|
|
|
|
|
|
|
|
|
usb_req->length = req->length;
|
|
|
|
|
usb_req->buf = NULL;
|
|
|
|
|
usb_req->sg = priv->sgt->sgl;
|
|
|
|
|
usb_req->num_sgs = sg_nents_for_len(priv->sgt->sgl, req->length);
|
|
|
|
|
usb_req->sg_was_mapped = true;
|
|
|
|
|
usb_req->context = fence;
|
|
|
|
|
usb_req->complete = ffs_epfile_dmabuf_io_complete;
|
|
|
|
|
|
|
|
|
|
cookie = dma_fence_begin_signalling();
|
|
|
|
|
ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC);
|
|
|
|
|
dma_fence_end_signalling(cookie);
|
|
|
|
|
if (!ret) {
|
|
|
|
|
priv->req = usb_req;
|
|
|
|
|
priv->ep = ep->ep;
|
|
|
|
|
} else {
|
|
|
|
|
pr_warn("FFS: Failed to queue DMABUF: %d\n", ret);
|
|
|
|
|
ffs_dmabuf_signal_done(fence, ret);
|
|
|
|
|
usb_ep_free_request(ep->ep, usb_req);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
dma_buf_put(dmabuf);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
err_fence_put:
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
dma_fence_put(&fence->base);
|
|
|
|
|
err_resv_unlock:
|
|
|
|
|
dma_resv_unlock(dmabuf->resv);
|
|
|
|
|
err_attachment_put:
|
|
|
|
|
ffs_dmabuf_put(attach);
|
|
|
|
|
err_dmabuf_put:
|
|
|
|
|
dma_buf_put(dmabuf);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|
|
|
|
unsigned long value)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = file->private_data;
|
2017-04-19 18:23:38 -07:00
|
|
|
|
struct ffs_ep *ep;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
2024-01-30 13:23:39 +01:00
|
|
|
|
switch (code) {
|
|
|
|
|
case FUNCTIONFS_DMABUF_ATTACH:
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ffs_dmabuf_attach(file, fd);
|
|
|
|
|
}
|
|
|
|
|
case FUNCTIONFS_DMABUF_DETACH:
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ffs_dmabuf_detach(file, fd);
|
|
|
|
|
}
|
|
|
|
|
case FUNCTIONFS_DMABUF_TRANSFER:
|
|
|
|
|
{
|
|
|
|
|
struct usb_ffs_dmabuf_transfer_req req;
|
|
|
|
|
|
|
|
|
|
if (copy_from_user(&req, (void __user *)value, sizeof(req))) {
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ffs_dmabuf_transfer(file, &req);
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 18:23:38 -07:00
|
|
|
|
/* Wait for endpoint to be enabled */
|
2024-01-30 13:23:38 +01:00
|
|
|
|
ep = ffs_epfile_wait_ep(file);
|
|
|
|
|
if (IS_ERR(ep))
|
|
|
|
|
return PTR_ERR(ep);
|
2017-04-19 18:23:38 -07:00
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
spin_lock_irq(&epfile->ffs->eps_lock);
|
2017-04-19 18:23:38 -07:00
|
|
|
|
|
|
|
|
|
/* In the meantime, endpoint got disabled or changed. */
|
|
|
|
|
if (epfile->ep != ep) {
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
return -ESHUTDOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
case FUNCTIONFS_FIFO_STATUS:
|
|
|
|
|
ret = usb_ep_fifo_status(epfile->ep->ep);
|
|
|
|
|
break;
|
|
|
|
|
case FUNCTIONFS_FIFO_FLUSH:
|
|
|
|
|
usb_ep_fifo_flush(epfile->ep->ep);
|
|
|
|
|
ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
case FUNCTIONFS_CLEAR_HALT:
|
|
|
|
|
ret = usb_ep_clear_halt(epfile->ep->ep);
|
|
|
|
|
break;
|
|
|
|
|
case FUNCTIONFS_ENDPOINT_REVMAP:
|
|
|
|
|
ret = epfile->ep->num;
|
|
|
|
|
break;
|
|
|
|
|
case FUNCTIONFS_ENDPOINT_DESC:
|
|
|
|
|
{
|
|
|
|
|
int desc_idx;
|
2020-11-30 12:34:53 -08:00
|
|
|
|
struct usb_endpoint_descriptor desc1, *desc;
|
2017-04-19 18:23:38 -07:00
|
|
|
|
|
|
|
|
|
switch (epfile->ffs->gadget->speed) {
|
|
|
|
|
case USB_SPEED_SUPER:
|
2020-10-27 16:07:31 -07:00
|
|
|
|
case USB_SPEED_SUPER_PLUS:
|
2017-04-19 18:23:38 -07:00
|
|
|
|
desc_idx = 2;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
2017-04-19 18:23:38 -07:00
|
|
|
|
case USB_SPEED_HIGH:
|
|
|
|
|
desc_idx = 1;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-04-19 18:23:38 -07:00
|
|
|
|
desc_idx = 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
2020-11-30 12:34:53 -08:00
|
|
|
|
|
2017-04-19 18:23:38 -07:00
|
|
|
|
desc = epfile->ep->descs[desc_idx];
|
2020-11-30 12:34:53 -08:00
|
|
|
|
memcpy(&desc1, desc, desc->bLength);
|
2017-04-19 18:23:38 -07:00
|
|
|
|
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
2020-11-30 12:34:53 -08:00
|
|
|
|
ret = copy_to_user((void __user *)value, &desc1, desc1.bLength);
|
2017-04-19 18:23:38 -07:00
|
|
|
|
if (ret)
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
ret = -ENOTTY;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
spin_unlock_irq(&epfile->ffs->eps_lock);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct file_operations ffs_epfile_operations = {
|
|
|
|
|
|
|
|
|
|
.open = ffs_epfile_open,
|
2015-01-31 23:55:39 -05:00
|
|
|
|
.write_iter = ffs_epfile_write_iter,
|
|
|
|
|
.read_iter = ffs_epfile_read_iter,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
.release = ffs_epfile_release,
|
|
|
|
|
.unlocked_ioctl = ffs_epfile_ioctl,
|
2018-09-11 17:28:08 +02:00
|
|
|
|
.compat_ioctl = compat_ptr_ioctl,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* File system and super block operations ***********************************/
|
|
|
|
|
|
|
|
|
|
/*
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* Mounting the file system creates a controller file, used first for
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* function configuration then later for event monitoring.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static struct inode *__must_check
|
|
|
|
|
ffs_sb_make_inode(struct super_block *sb, void *data,
|
|
|
|
|
const struct file_operations *fops,
|
|
|
|
|
const struct inode_operations *iops,
|
|
|
|
|
struct ffs_file_perms *perms)
|
|
|
|
|
{
|
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
|
|
inode = new_inode(sb);
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (inode) {
|
2023-07-05 15:00:45 -04:00
|
|
|
|
struct timespec64 ts = inode_set_ctime_current(inode);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2010-10-27 04:19:36 +01:00
|
|
|
|
inode->i_ino = get_next_ino();
|
2010-05-05 12:53:14 +02:00
|
|
|
|
inode->i_mode = perms->mode;
|
|
|
|
|
inode->i_uid = perms->uid;
|
|
|
|
|
inode->i_gid = perms->gid;
|
2023-10-04 14:51:57 -04:00
|
|
|
|
inode_set_atime_to_ts(inode, ts);
|
|
|
|
|
inode_set_mtime_to_ts(inode, ts);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
inode->i_private = data;
|
|
|
|
|
if (fops)
|
|
|
|
|
inode->i_fop = fops;
|
|
|
|
|
if (iops)
|
|
|
|
|
inode->i_op = iops;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return inode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create "regular" file */
|
2014-09-03 13:32:19 -04:00
|
|
|
|
static struct dentry *ffs_sb_create_file(struct super_block *sb,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
const char *name, void *data,
|
2014-09-03 13:32:19 -04:00
|
|
|
|
const struct file_operations *fops)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = sb->s_fs_info;
|
|
|
|
|
struct dentry *dentry;
|
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
|
|
dentry = d_alloc_name(sb->s_root, name);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!dentry)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!inode) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
dput(dentry);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d_add(dentry, inode);
|
2014-09-03 13:32:19 -04:00
|
|
|
|
return dentry;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Super block */
|
|
|
|
|
static const struct super_operations ffs_sb_operations = {
|
|
|
|
|
.statfs = simple_statfs,
|
|
|
|
|
.drop_inode = generic_delete_inode,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ffs_sb_fill_data {
|
|
|
|
|
struct ffs_file_perms perms;
|
|
|
|
|
umode_t root_mode;
|
|
|
|
|
const char *dev_name;
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
bool no_disconnect;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
struct ffs_data *ffs_data;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
2019-03-20 23:31:05 +00:00
|
|
|
|
struct ffs_sb_fill_data *data = fc->fs_private;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
struct inode *inode;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
struct ffs_data *ffs = data->ffs_data;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
ffs->sb = sb;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
data->ffs_data = NULL;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
sb->s_fs_info = ffs;
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 15:29:47 +03:00
|
|
|
|
sb->s_blocksize = PAGE_SIZE;
|
|
|
|
|
sb->s_blocksize_bits = PAGE_SHIFT;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
sb->s_magic = FUNCTIONFS_MAGIC;
|
|
|
|
|
sb->s_op = &ffs_sb_operations;
|
|
|
|
|
sb->s_time_gran = 1;
|
|
|
|
|
|
|
|
|
|
/* Root inode */
|
|
|
|
|
data->perms.mode = data->root_mode;
|
|
|
|
|
inode = ffs_sb_make_inode(sb, NULL,
|
|
|
|
|
&simple_dir_operations,
|
|
|
|
|
&simple_dir_inode_operations,
|
|
|
|
|
&data->perms);
|
2012-01-08 22:15:13 -05:00
|
|
|
|
sb->s_root = d_make_root(inode);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!sb->s_root)
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
return -ENOMEM;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
/* EP0 file */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations))
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
return -ENOMEM;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
enum {
|
|
|
|
|
Opt_no_disconnect,
|
|
|
|
|
Opt_rmode,
|
|
|
|
|
Opt_fmode,
|
|
|
|
|
Opt_mode,
|
|
|
|
|
Opt_uid,
|
|
|
|
|
Opt_gid,
|
|
|
|
|
};
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-09-07 07:23:15 -04:00
|
|
|
|
static const struct fs_parameter_spec ffs_fs_fs_parameters[] = {
|
2019-03-20 23:31:05 +00:00
|
|
|
|
fsparam_bool ("no_disconnect", Opt_no_disconnect),
|
|
|
|
|
fsparam_u32 ("rmode", Opt_rmode),
|
|
|
|
|
fsparam_u32 ("fmode", Opt_fmode),
|
|
|
|
|
fsparam_u32 ("mode", Opt_mode),
|
|
|
|
|
fsparam_u32 ("uid", Opt_uid),
|
|
|
|
|
fsparam_u32 ("gid", Opt_gid),
|
|
|
|
|
{}
|
|
|
|
|
};
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_sb_fill_data *data = fc->fs_private;
|
|
|
|
|
struct fs_parse_result result;
|
|
|
|
|
int opt;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-09-07 07:23:15 -04:00
|
|
|
|
opt = fs_parse(fc, ffs_fs_fs_parameters, param, &result);
|
2019-03-20 23:31:05 +00:00
|
|
|
|
if (opt < 0)
|
|
|
|
|
return opt;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
switch (opt) {
|
|
|
|
|
case Opt_no_disconnect:
|
|
|
|
|
data->no_disconnect = result.boolean;
|
|
|
|
|
break;
|
|
|
|
|
case Opt_rmode:
|
|
|
|
|
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
|
|
|
|
|
break;
|
|
|
|
|
case Opt_fmode:
|
|
|
|
|
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
|
|
|
|
|
break;
|
|
|
|
|
case Opt_mode:
|
|
|
|
|
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
|
|
|
|
|
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
|
|
|
|
|
break;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
case Opt_uid:
|
|
|
|
|
data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
|
|
|
|
|
if (!uid_valid(data->perms.uid))
|
|
|
|
|
goto unmapped_value;
|
|
|
|
|
break;
|
|
|
|
|
case Opt_gid:
|
|
|
|
|
data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
|
|
|
|
|
if (!gid_valid(data->perms.gid))
|
|
|
|
|
goto unmapped_value;
|
|
|
|
|
break;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
default:
|
|
|
|
|
return -ENOPARAM;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
unmapped_value:
|
|
|
|
|
return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* Set up the superblock for a mount.
|
|
|
|
|
*/
|
|
|
|
|
static int ffs_fs_get_tree(struct fs_context *fc)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
2019-03-20 23:31:05 +00:00
|
|
|
|
struct ffs_sb_fill_data *ctx = fc->fs_private;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
struct ffs_data *ffs;
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
int ret;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
if (!fc->source)
|
|
|
|
|
return invalf(fc, "No source specified");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
ffs = ffs_data_new(fc->source);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ffs)
|
2019-03-20 23:31:05 +00:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
ffs->file_perms = ctx->perms;
|
|
|
|
|
ffs->no_disconnect = ctx->no_disconnect;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
ffs->dev_name = kstrdup(fc->source, GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ffs->dev_name) {
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
ffs_data_put(ffs);
|
2019-03-20 23:31:05 +00:00
|
|
|
|
return -ENOMEM;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
ret = ffs_acquire_dev(ffs->dev_name, ffs);
|
|
|
|
|
if (ret) {
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
ffs_data_put(ffs);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
return ret;
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
}
|
2019-03-20 23:31:05 +00:00
|
|
|
|
|
|
|
|
|
ctx->ffs_data = ffs;
|
|
|
|
|
return get_tree_nodev(fc, ffs_sb_fill);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_fs_free_fc(struct fs_context *fc)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_sb_fill_data *ctx = fc->fs_private;
|
|
|
|
|
|
|
|
|
|
if (ctx) {
|
|
|
|
|
if (ctx->ffs_data) {
|
|
|
|
|
ffs_data_put(ctx->ffs_data);
|
|
|
|
|
}
|
2012-05-14 15:51:52 +02:00
|
|
|
|
|
2019-03-20 23:31:05 +00:00
|
|
|
|
kfree(ctx);
|
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-20 17:14:21 +01:00
|
|
|
|
}
|
2019-03-20 23:31:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct fs_context_operations ffs_fs_context_ops = {
|
|
|
|
|
.free = ffs_fs_free_fc,
|
|
|
|
|
.parse_param = ffs_fs_parse_param,
|
|
|
|
|
.get_tree = ffs_fs_get_tree,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int ffs_fs_init_fs_context(struct fs_context *fc)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_sb_fill_data *ctx;
|
|
|
|
|
|
|
|
|
|
ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
|
|
|
|
|
if (!ctx)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
ctx->perms.mode = S_IFREG | 0600;
|
|
|
|
|
ctx->perms.uid = GLOBAL_ROOT_UID;
|
|
|
|
|
ctx->perms.gid = GLOBAL_ROOT_GID;
|
|
|
|
|
ctx->root_mode = S_IFDIR | 0500;
|
|
|
|
|
ctx->no_disconnect = false;
|
|
|
|
|
|
|
|
|
|
fc->fs_private = ctx;
|
|
|
|
|
fc->ops = &ffs_fs_context_ops;
|
|
|
|
|
return 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ffs_fs_kill_sb(struct super_block *sb)
|
|
|
|
|
{
|
|
|
|
|
kill_litter_super(sb);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
if (sb->s_fs_info)
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
ffs_data_closed(sb->s_fs_info);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct file_system_type ffs_fs_type = {
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.name = "functionfs",
|
2019-03-20 23:31:05 +00:00
|
|
|
|
.init_fs_context = ffs_fs_init_fs_context,
|
2019-09-07 07:23:15 -04:00
|
|
|
|
.parameters = ffs_fs_fs_parameters,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
.kill_sb = ffs_fs_kill_sb,
|
|
|
|
|
};
|
2013-03-02 19:39:14 -08:00
|
|
|
|
MODULE_ALIAS_FS("functionfs");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Driver's main init/cleanup functions *************************************/
|
|
|
|
|
|
|
|
|
|
static int functionfs_init(void)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = register_filesystem(&ffs_fs_type);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ret)
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_info("file system registered\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
else
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_err("failed registering file system (%d)\n", ret);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void functionfs_cleanup(void)
|
|
|
|
|
{
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_info("unloading\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
unregister_filesystem(&ffs_fs_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ffs_data and ffs_function construction and destruction code **************/
|
|
|
|
|
|
|
|
|
|
static void ffs_data_clear(struct ffs_data *ffs);
|
|
|
|
|
static void ffs_data_reset(struct ffs_data *ffs);
|
|
|
|
|
|
|
|
|
|
static void ffs_data_get(struct ffs_data *ffs)
|
|
|
|
|
{
|
2017-03-06 16:21:12 +02:00
|
|
|
|
refcount_inc(&ffs->ref);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_data_opened(struct ffs_data *ffs)
|
|
|
|
|
{
|
2017-03-06 16:21:12 +02:00
|
|
|
|
refcount_inc(&ffs->ref);
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
if (atomic_add_return(1, &ffs->opened) == 1 &&
|
|
|
|
|
ffs->state == FFS_DEACTIVATED) {
|
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
ffs_data_reset(ffs);
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_data_put(struct ffs_data *ffs)
|
|
|
|
|
{
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (refcount_dec_and_test(&ffs->ref)) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_info("%s(): freeing\n", __func__);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_data_clear(ffs);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
ffs_release_dev(ffs->private_data);
|
2012-03-16 12:01:02 -07:00
|
|
|
|
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
|
2020-03-21 12:26:00 +01:00
|
|
|
|
swait_active(&ffs->ep0req_completion.wait) ||
|
2017-04-18 16:11:48 -07:00
|
|
|
|
waitqueue_active(&ffs->wait));
|
2017-09-12 10:24:40 +01:00
|
|
|
|
destroy_workqueue(ffs->io_completion_wq);
|
2012-05-14 15:51:52 +02:00
|
|
|
|
kfree(ffs->dev_name);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
kfree(ffs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_data_closed(struct ffs_data *ffs)
|
|
|
|
|
{
|
2022-01-27 09:39:55 +05:30
|
|
|
|
struct ffs_epfile *epfiles;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (atomic_dec_and_test(&ffs->opened)) {
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
if (ffs->no_disconnect) {
|
|
|
|
|
ffs->state = FFS_DEACTIVATED;
|
2022-01-27 09:39:55 +05:30
|
|
|
|
spin_lock_irqsave(&ffs->eps_lock, flags);
|
|
|
|
|
epfiles = ffs->epfiles;
|
|
|
|
|
ffs->epfiles = NULL;
|
|
|
|
|
spin_unlock_irqrestore(&ffs->eps_lock,
|
|
|
|
|
flags);
|
|
|
|
|
|
|
|
|
|
if (epfiles)
|
|
|
|
|
ffs_epfiles_destroy(epfiles,
|
|
|
|
|
ffs->eps_count);
|
|
|
|
|
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
if (ffs->setup_state == FFS_SETUP_PENDING)
|
|
|
|
|
__ffs_ep0_stall(ffs);
|
|
|
|
|
} else {
|
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
ffs_data_reset(ffs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (atomic_read(&ffs->opened) < 0) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
ffs_data_reset(ffs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs_data_put(ffs);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 10:24:40 +01:00
|
|
|
|
static struct ffs_data *ffs_data_new(const char *dev_name)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ffs)
|
2013-12-12 12:15:43 -06:00
|
|
|
|
return NULL;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2017-09-12 10:24:40 +01:00
|
|
|
|
ffs->io_completion_wq = alloc_ordered_workqueue("%s", 0, dev_name);
|
|
|
|
|
if (!ffs->io_completion_wq) {
|
|
|
|
|
kfree(ffs);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 16:21:12 +02:00
|
|
|
|
refcount_set(&ffs->ref, 1);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
atomic_set(&ffs->opened, 0);
|
|
|
|
|
ffs->state = FFS_READ_DESCRIPTORS;
|
|
|
|
|
mutex_init(&ffs->mutex);
|
|
|
|
|
spin_lock_init(&ffs->eps_lock);
|
|
|
|
|
init_waitqueue_head(&ffs->ev.waitq);
|
2017-04-18 16:11:48 -07:00
|
|
|
|
init_waitqueue_head(&ffs->wait);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
init_completion(&ffs->ep0req_completion);
|
|
|
|
|
|
|
|
|
|
/* XXX REVISIT need to update it in some places, or do we? */
|
|
|
|
|
ffs->ev.can_stall = 1;
|
|
|
|
|
|
|
|
|
|
return ffs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_data_clear(struct ffs_data *ffs)
|
|
|
|
|
{
|
2022-01-27 09:39:55 +05:30
|
|
|
|
struct ffs_epfile *epfiles;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
2015-05-22 17:25:18 +02:00
|
|
|
|
ffs_closed(ffs);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
BUG_ON(ffs->gadget);
|
|
|
|
|
|
2022-01-27 09:39:55 +05:30
|
|
|
|
spin_lock_irqsave(&ffs->eps_lock, flags);
|
|
|
|
|
epfiles = ffs->epfiles;
|
|
|
|
|
ffs->epfiles = NULL;
|
|
|
|
|
spin_unlock_irqrestore(&ffs->eps_lock, flags);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* potential race possible between ffs_func_eps_disable
|
|
|
|
|
* & ffs_epfile_release therefore maintaining a local
|
|
|
|
|
* copy of epfile will save us from use-after-free.
|
|
|
|
|
*/
|
|
|
|
|
if (epfiles) {
|
|
|
|
|
ffs_epfiles_destroy(epfiles, ffs->eps_count);
|
usb: gadget: f_fs: Clear ffs_eventfd in ffs_data_clear.
ffs_data_clear is indirectly called from both ffs_fs_kill_sb and
ffs_ep0_release, so it ends up being called twice when userland closes ep0
and then unmounts f_fs.
If userland provided an eventfd along with function's USB descriptors, it
ends up calling eventfd_ctx_put as many times, causing a refcount
underflow.
NULL-ify ffs_eventfd to prevent these extraneous eventfd_ctx_put calls.
Also, set epfiles to NULL right after de-allocating it, for readability.
For completeness, ffs_data_clear actually ends up being called thrice, the
last call being before the whole ffs structure gets freed, so when this
specific sequence happens there is a second underflow happening (but not
being reported):
/sys/kernel/debug/tracing# modprobe usb_f_fs
/sys/kernel/debug/tracing# echo ffs_data_clear > set_ftrace_filter
/sys/kernel/debug/tracing# echo function > current_tracer
/sys/kernel/debug/tracing# echo 1 > tracing_on
(setup gadget, run and kill function userland process, teardown gadget)
/sys/kernel/debug/tracing# echo 0 > tracing_on
/sys/kernel/debug/tracing# cat trace
smartcard-openp-436 [000] ..... 1946.208786: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] ..... 1946.279147: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] .n... 1946.905512: ffs_data_clear <-ffs_data_put
Warning output corresponding to above trace:
[ 1946.284139] WARNING: CPU: 0 PID: 431 at lib/refcount.c:28 refcount_warn_saturate+0x110/0x15c
[ 1946.293094] refcount_t: underflow; use-after-free.
[ 1946.298164] Modules linked in: usb_f_ncm(E) u_ether(E) usb_f_fs(E) hci_uart(E) btqca(E) btrtl(E) btbcm(E) btintel(E) bluetooth(E) nls_ascii(E) nls_cp437(E) vfat(E) fat(E) bcm2835_v4l2(CE) bcm2835_mmal_vchiq(CE) videobuf2_vmalloc(E) videobuf2_memops(E) sha512_generic(E) videobuf2_v4l2(E) sha512_arm(E) videobuf2_common(E) videodev(E) cpufreq_dt(E) snd_bcm2835(CE) brcmfmac(E) mc(E) vc4(E) ctr(E) brcmutil(E) snd_soc_core(E) snd_pcm_dmaengine(E) drbg(E) snd_pcm(E) snd_timer(E) snd(E) soundcore(E) drm_kms_helper(E) cec(E) ansi_cprng(E) rc_core(E) syscopyarea(E) raspberrypi_cpufreq(E) sysfillrect(E) sysimgblt(E) cfg80211(E) max17040_battery(OE) raspberrypi_hwmon(E) fb_sys_fops(E) regmap_i2c(E) ecdh_generic(E) rfkill(E) ecc(E) bcm2835_rng(E) rng_core(E) vchiq(CE) leds_gpio(E) libcomposite(E) fuse(E) configfs(E) ip_tables(E) x_tables(E) autofs4(E) ext4(E) crc16(E) mbcache(E) jbd2(E) crc32c_generic(E) sdhci_iproc(E) sdhci_pltfm(E) sdhci(E)
[ 1946.399633] CPU: 0 PID: 431 Comm: smartcard-openp Tainted: G C OE 5.15.0-1-rpi #1 Debian 5.15.3-1
[ 1946.417950] Hardware name: BCM2835
[ 1946.425442] Backtrace:
[ 1946.432048] [<c08d60a0>] (dump_backtrace) from [<c08d62ec>] (show_stack+0x20/0x24)
[ 1946.448226] r7:00000009 r6:0000001c r5:c04a948c r4:c0a64e2c
[ 1946.458412] [<c08d62cc>] (show_stack) from [<c08d9ae0>] (dump_stack+0x28/0x30)
[ 1946.470380] [<c08d9ab8>] (dump_stack) from [<c0123500>] (__warn+0xe8/0x154)
[ 1946.482067] r5:c04a948c r4:c0a71dc8
[ 1946.490184] [<c0123418>] (__warn) from [<c08d6948>] (warn_slowpath_fmt+0xa0/0xe4)
[ 1946.506758] r7:00000009 r6:0000001c r5:c0a71dc8 r4:c0a71e04
[ 1946.517070] [<c08d68ac>] (warn_slowpath_fmt) from [<c04a948c>] (refcount_warn_saturate+0x110/0x15c)
[ 1946.535309] r8:c0100224 r7:c0dfcb84 r6:ffffffff r5:c3b84c00 r4:c24a17c0
[ 1946.546708] [<c04a937c>] (refcount_warn_saturate) from [<c0380134>] (eventfd_ctx_put+0x48/0x74)
[ 1946.564476] [<c03800ec>] (eventfd_ctx_put) from [<bf5464e8>] (ffs_data_clear+0xd0/0x118 [usb_f_fs])
[ 1946.582664] r5:c3b84c00 r4:c2695b00
[ 1946.590668] [<bf546418>] (ffs_data_clear [usb_f_fs]) from [<bf547cc0>] (ffs_data_closed+0x9c/0x150 [usb_f_fs])
[ 1946.609608] r5:bf54d014 r4:c2695b00
[ 1946.617522] [<bf547c24>] (ffs_data_closed [usb_f_fs]) from [<bf547da0>] (ffs_fs_kill_sb+0x2c/0x30 [usb_f_fs])
[ 1946.636217] r7:c0dfcb84 r6:c3a12260 r5:bf54d014 r4:c229f000
[ 1946.646273] [<bf547d74>] (ffs_fs_kill_sb [usb_f_fs]) from [<c0326d50>] (deactivate_locked_super+0x54/0x9c)
[ 1946.664893] r5:bf54d014 r4:c229f000
[ 1946.672921] [<c0326cfc>] (deactivate_locked_super) from [<c0326df8>] (deactivate_super+0x60/0x64)
[ 1946.690722] r5:c2a09000 r4:c229f000
[ 1946.698706] [<c0326d98>] (deactivate_super) from [<c0349a28>] (cleanup_mnt+0xe4/0x14c)
[ 1946.715553] r5:c2a09000 r4:00000000
[ 1946.723528] [<c0349944>] (cleanup_mnt) from [<c0349b08>] (__cleanup_mnt+0x1c/0x20)
[ 1946.739922] r7:c0dfcb84 r6:c3a12260 r5:c3a126fc r4:00000000
[ 1946.750088] [<c0349aec>] (__cleanup_mnt) from [<c0143d10>] (task_work_run+0x84/0xb8)
[ 1946.766602] [<c0143c8c>] (task_work_run) from [<c010bdc8>] (do_work_pending+0x470/0x56c)
[ 1946.783540] r7:5ac3c35a r6:c0d0424c r5:c200bfb0 r4:c200a000
[ 1946.793614] [<c010b958>] (do_work_pending) from [<c01000c0>] (slow_work_pending+0xc/0x20)
[ 1946.810553] Exception stack(0xc200bfb0 to 0xc200bff8)
[ 1946.820129] bfa0: 00000000 00000000 000000aa b5e21430
[ 1946.837104] bfc0: bef867a0 00000001 bef86840 00000034 bef86838 bef86790 bef86794 bef867a0
[ 1946.854125] bfe0: 00000000 bef86798 b67b7a1c b6d626a4 60000010 b5a23760
[ 1946.865335] r10:00000000 r9:c200a000 r8:c0100224 r7:00000034 r6:bef86840 r5:00000001
[ 1946.881914] r4:bef867a0
[ 1946.888793] ---[ end trace 7387f2a9725b28d0 ]---
Fixes: 5e33f6fdf735 ("usb: gadget: ffs: add eventfd notification about ffs events")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Link: https://lore.kernel.org/r/f79eeea29f3f98de6782a064ec0f7351ad2f598f.1639793920.git.plr.vincent@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-12-18 02:18:40 +00:00
|
|
|
|
ffs->epfiles = NULL;
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
usb: gadget: f_fs: Clear ffs_eventfd in ffs_data_clear.
ffs_data_clear is indirectly called from both ffs_fs_kill_sb and
ffs_ep0_release, so it ends up being called twice when userland closes ep0
and then unmounts f_fs.
If userland provided an eventfd along with function's USB descriptors, it
ends up calling eventfd_ctx_put as many times, causing a refcount
underflow.
NULL-ify ffs_eventfd to prevent these extraneous eventfd_ctx_put calls.
Also, set epfiles to NULL right after de-allocating it, for readability.
For completeness, ffs_data_clear actually ends up being called thrice, the
last call being before the whole ffs structure gets freed, so when this
specific sequence happens there is a second underflow happening (but not
being reported):
/sys/kernel/debug/tracing# modprobe usb_f_fs
/sys/kernel/debug/tracing# echo ffs_data_clear > set_ftrace_filter
/sys/kernel/debug/tracing# echo function > current_tracer
/sys/kernel/debug/tracing# echo 1 > tracing_on
(setup gadget, run and kill function userland process, teardown gadget)
/sys/kernel/debug/tracing# echo 0 > tracing_on
/sys/kernel/debug/tracing# cat trace
smartcard-openp-436 [000] ..... 1946.208786: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] ..... 1946.279147: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] .n... 1946.905512: ffs_data_clear <-ffs_data_put
Warning output corresponding to above trace:
[ 1946.284139] WARNING: CPU: 0 PID: 431 at lib/refcount.c:28 refcount_warn_saturate+0x110/0x15c
[ 1946.293094] refcount_t: underflow; use-after-free.
[ 1946.298164] Modules linked in: usb_f_ncm(E) u_ether(E) usb_f_fs(E) hci_uart(E) btqca(E) btrtl(E) btbcm(E) btintel(E) bluetooth(E) nls_ascii(E) nls_cp437(E) vfat(E) fat(E) bcm2835_v4l2(CE) bcm2835_mmal_vchiq(CE) videobuf2_vmalloc(E) videobuf2_memops(E) sha512_generic(E) videobuf2_v4l2(E) sha512_arm(E) videobuf2_common(E) videodev(E) cpufreq_dt(E) snd_bcm2835(CE) brcmfmac(E) mc(E) vc4(E) ctr(E) brcmutil(E) snd_soc_core(E) snd_pcm_dmaengine(E) drbg(E) snd_pcm(E) snd_timer(E) snd(E) soundcore(E) drm_kms_helper(E) cec(E) ansi_cprng(E) rc_core(E) syscopyarea(E) raspberrypi_cpufreq(E) sysfillrect(E) sysimgblt(E) cfg80211(E) max17040_battery(OE) raspberrypi_hwmon(E) fb_sys_fops(E) regmap_i2c(E) ecdh_generic(E) rfkill(E) ecc(E) bcm2835_rng(E) rng_core(E) vchiq(CE) leds_gpio(E) libcomposite(E) fuse(E) configfs(E) ip_tables(E) x_tables(E) autofs4(E) ext4(E) crc16(E) mbcache(E) jbd2(E) crc32c_generic(E) sdhci_iproc(E) sdhci_pltfm(E) sdhci(E)
[ 1946.399633] CPU: 0 PID: 431 Comm: smartcard-openp Tainted: G C OE 5.15.0-1-rpi #1 Debian 5.15.3-1
[ 1946.417950] Hardware name: BCM2835
[ 1946.425442] Backtrace:
[ 1946.432048] [<c08d60a0>] (dump_backtrace) from [<c08d62ec>] (show_stack+0x20/0x24)
[ 1946.448226] r7:00000009 r6:0000001c r5:c04a948c r4:c0a64e2c
[ 1946.458412] [<c08d62cc>] (show_stack) from [<c08d9ae0>] (dump_stack+0x28/0x30)
[ 1946.470380] [<c08d9ab8>] (dump_stack) from [<c0123500>] (__warn+0xe8/0x154)
[ 1946.482067] r5:c04a948c r4:c0a71dc8
[ 1946.490184] [<c0123418>] (__warn) from [<c08d6948>] (warn_slowpath_fmt+0xa0/0xe4)
[ 1946.506758] r7:00000009 r6:0000001c r5:c0a71dc8 r4:c0a71e04
[ 1946.517070] [<c08d68ac>] (warn_slowpath_fmt) from [<c04a948c>] (refcount_warn_saturate+0x110/0x15c)
[ 1946.535309] r8:c0100224 r7:c0dfcb84 r6:ffffffff r5:c3b84c00 r4:c24a17c0
[ 1946.546708] [<c04a937c>] (refcount_warn_saturate) from [<c0380134>] (eventfd_ctx_put+0x48/0x74)
[ 1946.564476] [<c03800ec>] (eventfd_ctx_put) from [<bf5464e8>] (ffs_data_clear+0xd0/0x118 [usb_f_fs])
[ 1946.582664] r5:c3b84c00 r4:c2695b00
[ 1946.590668] [<bf546418>] (ffs_data_clear [usb_f_fs]) from [<bf547cc0>] (ffs_data_closed+0x9c/0x150 [usb_f_fs])
[ 1946.609608] r5:bf54d014 r4:c2695b00
[ 1946.617522] [<bf547c24>] (ffs_data_closed [usb_f_fs]) from [<bf547da0>] (ffs_fs_kill_sb+0x2c/0x30 [usb_f_fs])
[ 1946.636217] r7:c0dfcb84 r6:c3a12260 r5:bf54d014 r4:c229f000
[ 1946.646273] [<bf547d74>] (ffs_fs_kill_sb [usb_f_fs]) from [<c0326d50>] (deactivate_locked_super+0x54/0x9c)
[ 1946.664893] r5:bf54d014 r4:c229f000
[ 1946.672921] [<c0326cfc>] (deactivate_locked_super) from [<c0326df8>] (deactivate_super+0x60/0x64)
[ 1946.690722] r5:c2a09000 r4:c229f000
[ 1946.698706] [<c0326d98>] (deactivate_super) from [<c0349a28>] (cleanup_mnt+0xe4/0x14c)
[ 1946.715553] r5:c2a09000 r4:00000000
[ 1946.723528] [<c0349944>] (cleanup_mnt) from [<c0349b08>] (__cleanup_mnt+0x1c/0x20)
[ 1946.739922] r7:c0dfcb84 r6:c3a12260 r5:c3a126fc r4:00000000
[ 1946.750088] [<c0349aec>] (__cleanup_mnt) from [<c0143d10>] (task_work_run+0x84/0xb8)
[ 1946.766602] [<c0143c8c>] (task_work_run) from [<c010bdc8>] (do_work_pending+0x470/0x56c)
[ 1946.783540] r7:5ac3c35a r6:c0d0424c r5:c200bfb0 r4:c200a000
[ 1946.793614] [<c010b958>] (do_work_pending) from [<c01000c0>] (slow_work_pending+0xc/0x20)
[ 1946.810553] Exception stack(0xc200bfb0 to 0xc200bff8)
[ 1946.820129] bfa0: 00000000 00000000 000000aa b5e21430
[ 1946.837104] bfc0: bef867a0 00000001 bef86840 00000034 bef86838 bef86790 bef86794 bef867a0
[ 1946.854125] bfe0: 00000000 bef86798 b67b7a1c b6d626a4 60000010 b5a23760
[ 1946.865335] r10:00000000 r9:c200a000 r8:c0100224 r7:00000034 r6:bef86840 r5:00000001
[ 1946.881914] r4:bef867a0
[ 1946.888793] ---[ end trace 7387f2a9725b28d0 ]---
Fixes: 5e33f6fdf735 ("usb: gadget: ffs: add eventfd notification about ffs events")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Link: https://lore.kernel.org/r/f79eeea29f3f98de6782a064ec0f7351ad2f598f.1639793920.git.plr.vincent@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-12-18 02:18:40 +00:00
|
|
|
|
if (ffs->ffs_eventfd) {
|
2015-01-23 13:41:01 +01:00
|
|
|
|
eventfd_ctx_put(ffs->ffs_eventfd);
|
usb: gadget: f_fs: Clear ffs_eventfd in ffs_data_clear.
ffs_data_clear is indirectly called from both ffs_fs_kill_sb and
ffs_ep0_release, so it ends up being called twice when userland closes ep0
and then unmounts f_fs.
If userland provided an eventfd along with function's USB descriptors, it
ends up calling eventfd_ctx_put as many times, causing a refcount
underflow.
NULL-ify ffs_eventfd to prevent these extraneous eventfd_ctx_put calls.
Also, set epfiles to NULL right after de-allocating it, for readability.
For completeness, ffs_data_clear actually ends up being called thrice, the
last call being before the whole ffs structure gets freed, so when this
specific sequence happens there is a second underflow happening (but not
being reported):
/sys/kernel/debug/tracing# modprobe usb_f_fs
/sys/kernel/debug/tracing# echo ffs_data_clear > set_ftrace_filter
/sys/kernel/debug/tracing# echo function > current_tracer
/sys/kernel/debug/tracing# echo 1 > tracing_on
(setup gadget, run and kill function userland process, teardown gadget)
/sys/kernel/debug/tracing# echo 0 > tracing_on
/sys/kernel/debug/tracing# cat trace
smartcard-openp-436 [000] ..... 1946.208786: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] ..... 1946.279147: ffs_data_clear <-ffs_data_closed
smartcard-openp-431 [000] .n... 1946.905512: ffs_data_clear <-ffs_data_put
Warning output corresponding to above trace:
[ 1946.284139] WARNING: CPU: 0 PID: 431 at lib/refcount.c:28 refcount_warn_saturate+0x110/0x15c
[ 1946.293094] refcount_t: underflow; use-after-free.
[ 1946.298164] Modules linked in: usb_f_ncm(E) u_ether(E) usb_f_fs(E) hci_uart(E) btqca(E) btrtl(E) btbcm(E) btintel(E) bluetooth(E) nls_ascii(E) nls_cp437(E) vfat(E) fat(E) bcm2835_v4l2(CE) bcm2835_mmal_vchiq(CE) videobuf2_vmalloc(E) videobuf2_memops(E) sha512_generic(E) videobuf2_v4l2(E) sha512_arm(E) videobuf2_common(E) videodev(E) cpufreq_dt(E) snd_bcm2835(CE) brcmfmac(E) mc(E) vc4(E) ctr(E) brcmutil(E) snd_soc_core(E) snd_pcm_dmaengine(E) drbg(E) snd_pcm(E) snd_timer(E) snd(E) soundcore(E) drm_kms_helper(E) cec(E) ansi_cprng(E) rc_core(E) syscopyarea(E) raspberrypi_cpufreq(E) sysfillrect(E) sysimgblt(E) cfg80211(E) max17040_battery(OE) raspberrypi_hwmon(E) fb_sys_fops(E) regmap_i2c(E) ecdh_generic(E) rfkill(E) ecc(E) bcm2835_rng(E) rng_core(E) vchiq(CE) leds_gpio(E) libcomposite(E) fuse(E) configfs(E) ip_tables(E) x_tables(E) autofs4(E) ext4(E) crc16(E) mbcache(E) jbd2(E) crc32c_generic(E) sdhci_iproc(E) sdhci_pltfm(E) sdhci(E)
[ 1946.399633] CPU: 0 PID: 431 Comm: smartcard-openp Tainted: G C OE 5.15.0-1-rpi #1 Debian 5.15.3-1
[ 1946.417950] Hardware name: BCM2835
[ 1946.425442] Backtrace:
[ 1946.432048] [<c08d60a0>] (dump_backtrace) from [<c08d62ec>] (show_stack+0x20/0x24)
[ 1946.448226] r7:00000009 r6:0000001c r5:c04a948c r4:c0a64e2c
[ 1946.458412] [<c08d62cc>] (show_stack) from [<c08d9ae0>] (dump_stack+0x28/0x30)
[ 1946.470380] [<c08d9ab8>] (dump_stack) from [<c0123500>] (__warn+0xe8/0x154)
[ 1946.482067] r5:c04a948c r4:c0a71dc8
[ 1946.490184] [<c0123418>] (__warn) from [<c08d6948>] (warn_slowpath_fmt+0xa0/0xe4)
[ 1946.506758] r7:00000009 r6:0000001c r5:c0a71dc8 r4:c0a71e04
[ 1946.517070] [<c08d68ac>] (warn_slowpath_fmt) from [<c04a948c>] (refcount_warn_saturate+0x110/0x15c)
[ 1946.535309] r8:c0100224 r7:c0dfcb84 r6:ffffffff r5:c3b84c00 r4:c24a17c0
[ 1946.546708] [<c04a937c>] (refcount_warn_saturate) from [<c0380134>] (eventfd_ctx_put+0x48/0x74)
[ 1946.564476] [<c03800ec>] (eventfd_ctx_put) from [<bf5464e8>] (ffs_data_clear+0xd0/0x118 [usb_f_fs])
[ 1946.582664] r5:c3b84c00 r4:c2695b00
[ 1946.590668] [<bf546418>] (ffs_data_clear [usb_f_fs]) from [<bf547cc0>] (ffs_data_closed+0x9c/0x150 [usb_f_fs])
[ 1946.609608] r5:bf54d014 r4:c2695b00
[ 1946.617522] [<bf547c24>] (ffs_data_closed [usb_f_fs]) from [<bf547da0>] (ffs_fs_kill_sb+0x2c/0x30 [usb_f_fs])
[ 1946.636217] r7:c0dfcb84 r6:c3a12260 r5:bf54d014 r4:c229f000
[ 1946.646273] [<bf547d74>] (ffs_fs_kill_sb [usb_f_fs]) from [<c0326d50>] (deactivate_locked_super+0x54/0x9c)
[ 1946.664893] r5:bf54d014 r4:c229f000
[ 1946.672921] [<c0326cfc>] (deactivate_locked_super) from [<c0326df8>] (deactivate_super+0x60/0x64)
[ 1946.690722] r5:c2a09000 r4:c229f000
[ 1946.698706] [<c0326d98>] (deactivate_super) from [<c0349a28>] (cleanup_mnt+0xe4/0x14c)
[ 1946.715553] r5:c2a09000 r4:00000000
[ 1946.723528] [<c0349944>] (cleanup_mnt) from [<c0349b08>] (__cleanup_mnt+0x1c/0x20)
[ 1946.739922] r7:c0dfcb84 r6:c3a12260 r5:c3a126fc r4:00000000
[ 1946.750088] [<c0349aec>] (__cleanup_mnt) from [<c0143d10>] (task_work_run+0x84/0xb8)
[ 1946.766602] [<c0143c8c>] (task_work_run) from [<c010bdc8>] (do_work_pending+0x470/0x56c)
[ 1946.783540] r7:5ac3c35a r6:c0d0424c r5:c200bfb0 r4:c200a000
[ 1946.793614] [<c010b958>] (do_work_pending) from [<c01000c0>] (slow_work_pending+0xc/0x20)
[ 1946.810553] Exception stack(0xc200bfb0 to 0xc200bff8)
[ 1946.820129] bfa0: 00000000 00000000 000000aa b5e21430
[ 1946.837104] bfc0: bef867a0 00000001 bef86840 00000034 bef86838 bef86790 bef86794 bef867a0
[ 1946.854125] bfe0: 00000000 bef86798 b67b7a1c b6d626a4 60000010 b5a23760
[ 1946.865335] r10:00000000 r9:c200a000 r8:c0100224 r7:00000034 r6:bef86840 r5:00000001
[ 1946.881914] r4:bef867a0
[ 1946.888793] ---[ end trace 7387f2a9725b28d0 ]---
Fixes: 5e33f6fdf735 ("usb: gadget: ffs: add eventfd notification about ffs events")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Link: https://lore.kernel.org/r/f79eeea29f3f98de6782a064ec0f7351ad2f598f.1639793920.git.plr.vincent@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-12-18 02:18:40 +00:00
|
|
|
|
ffs->ffs_eventfd = NULL;
|
|
|
|
|
}
|
2015-01-23 13:41:01 +01:00
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
kfree(ffs->raw_descs_data);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
kfree(ffs->raw_strings);
|
|
|
|
|
kfree(ffs->stringtabs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_data_reset(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
ffs_data_clear(ffs);
|
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
ffs->raw_descs_data = NULL;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->raw_descs = NULL;
|
|
|
|
|
ffs->raw_strings = NULL;
|
|
|
|
|
ffs->stringtabs = NULL;
|
|
|
|
|
|
|
|
|
|
ffs->raw_descs_length = 0;
|
|
|
|
|
ffs->fs_descs_count = 0;
|
|
|
|
|
ffs->hs_descs_count = 0;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
ffs->ss_descs_count = 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
ffs->strings_count = 0;
|
|
|
|
|
ffs->interfaces_count = 0;
|
|
|
|
|
ffs->eps_count = 0;
|
|
|
|
|
|
|
|
|
|
ffs->ev.count = 0;
|
|
|
|
|
|
|
|
|
|
ffs->state = FFS_READ_DESCRIPTORS;
|
|
|
|
|
ffs->setup_state = FFS_NO_SETUP;
|
|
|
|
|
ffs->flags = 0;
|
2020-04-02 10:15:21 +05:30
|
|
|
|
|
|
|
|
|
ffs->ms_os_descs_ext_prop_count = 0;
|
|
|
|
|
ffs->ms_os_descs_ext_prop_name_len = 0;
|
|
|
|
|
ffs->ms_os_descs_ext_prop_data_len = 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
|
|
|
|
|
{
|
2010-06-16 12:08:00 +02:00
|
|
|
|
struct usb_gadget_strings **lang;
|
|
|
|
|
int first_id;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2024-12-19 18:22:19 +05:30
|
|
|
|
if ((ffs->state != FFS_ACTIVE
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|| test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
|
|
|
|
|
return -EBADFD;
|
|
|
|
|
|
2010-06-16 12:08:00 +02:00
|
|
|
|
first_id = usb_string_ids_n(cdev, ffs->strings_count);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (first_id < 0)
|
2010-06-16 12:08:00 +02:00
|
|
|
|
return first_id;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ffs->ep0req)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
ffs->ep0req->complete = ffs_ep0_complete;
|
|
|
|
|
ffs->ep0req->context = ffs;
|
|
|
|
|
|
2010-06-16 12:08:00 +02:00
|
|
|
|
lang = ffs->stringtabs;
|
2014-06-17 17:47:41 +02:00
|
|
|
|
if (lang) {
|
|
|
|
|
for (; *lang; ++lang) {
|
|
|
|
|
struct usb_string *str = (*lang)->strings;
|
|
|
|
|
int id = first_id;
|
|
|
|
|
for (; str->s; ++id, ++str)
|
|
|
|
|
str->id = id;
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs->gadget = cdev->gadget;
|
2010-06-16 12:08:00 +02:00
|
|
|
|
ffs_data_get(ffs);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void functionfs_unbind(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
if (!WARN_ON(!ffs->gadget)) {
|
2022-12-15 10:59:06 +05:30
|
|
|
|
/* dequeue before freeing ep0req */
|
|
|
|
|
usb_ep_dequeue(ffs->gadget->ep0, ffs->ep0req);
|
2022-12-15 10:59:05 +05:30
|
|
|
|
mutex_lock(&ffs->mutex);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
|
|
|
|
|
ffs->ep0req = NULL;
|
|
|
|
|
ffs->gadget = NULL;
|
2012-03-12 12:55:41 +01:00
|
|
|
|
clear_bit(FFS_FL_BOUND, &ffs->flags);
|
2022-12-15 10:59:05 +05:30
|
|
|
|
mutex_unlock(&ffs->mutex);
|
2013-08-23 11:16:15 +03:00
|
|
|
|
ffs_data_put(ffs);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_epfiles_create(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile, *epfiles;
|
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
|
|
count = ffs->eps_count;
|
2011-11-29 22:08:00 +01:00
|
|
|
|
epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (!epfiles)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
epfile = epfiles;
|
|
|
|
|
for (i = 1; i <= count; ++i, ++epfile) {
|
|
|
|
|
epfile->ffs = ffs;
|
|
|
|
|
mutex_init(&epfile->mutex);
|
2024-01-30 13:23:39 +01:00
|
|
|
|
mutex_init(&epfile->dmabufs_mutex);
|
|
|
|
|
INIT_LIST_HEAD(&epfile->dmabufs);
|
2014-09-09 08:23:17 +02:00
|
|
|
|
if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
2015-01-26 20:40:21 +01:00
|
|
|
|
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
|
2014-09-09 08:23:17 +02:00
|
|
|
|
else
|
2015-01-26 20:40:21 +01:00
|
|
|
|
sprintf(epfile->name, "ep%u", i);
|
|
|
|
|
epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
|
2014-09-03 13:32:19 -04:00
|
|
|
|
epfile,
|
|
|
|
|
&ffs_epfile_operations);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!epfile->dentry) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_epfiles_destroy(epfiles, i - 1);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs->epfiles = epfiles;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_epfile *epfile = epfiles;
|
|
|
|
|
|
|
|
|
|
for (; count; --count, ++epfile) {
|
2017-04-18 16:11:48 -07:00
|
|
|
|
BUG_ON(mutex_is_locked(&epfile->mutex));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (epfile->dentry) {
|
2025-03-10 00:45:20 -04:00
|
|
|
|
simple_recursive_removal(epfile->dentry, NULL);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
epfile->dentry = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kfree(epfiles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_func_eps_disable(struct ffs_function *func)
|
|
|
|
|
{
|
2022-01-27 09:39:55 +05:30
|
|
|
|
struct ffs_ep *ep;
|
|
|
|
|
struct ffs_epfile *epfile;
|
|
|
|
|
unsigned short count;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
2016-10-04 02:07:34 +02:00
|
|
|
|
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
2022-01-27 09:39:55 +05:30
|
|
|
|
count = func->ffs->eps_count;
|
|
|
|
|
epfile = func->ffs->epfiles;
|
|
|
|
|
ep = func->eps;
|
2017-01-09 13:46:00 +00:00
|
|
|
|
while (count--) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
/* pending requests get nuked */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ep->ep)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
usb_ep_disable(ep->ep);
|
|
|
|
|
++ep;
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
|
|
|
|
|
if (epfile) {
|
2016-10-04 02:07:34 +02:00
|
|
|
|
epfile->ep = NULL;
|
|
|
|
|
__ffs_epfile_read_buffer_free(epfile);
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
++epfile;
|
|
|
|
|
}
|
2017-01-09 13:46:00 +00:00
|
|
|
|
}
|
2016-10-04 02:07:34 +02:00
|
|
|
|
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_func_eps_enable(struct ffs_function *func)
|
|
|
|
|
{
|
2022-01-27 09:39:55 +05:30
|
|
|
|
struct ffs_data *ffs;
|
|
|
|
|
struct ffs_ep *ep;
|
|
|
|
|
struct ffs_epfile *epfile;
|
|
|
|
|
unsigned short count;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
unsigned long flags;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
2022-01-27 09:39:55 +05:30
|
|
|
|
ffs = func->ffs;
|
|
|
|
|
ep = func->eps;
|
|
|
|
|
epfile = ffs->epfiles;
|
|
|
|
|
count = ffs->eps_count;
|
2017-01-09 13:46:00 +00:00
|
|
|
|
while(count--) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ep->ep->driver_data = ep;
|
2017-01-31 14:54:45 +02:00
|
|
|
|
|
2018-01-24 23:58:20 -08:00
|
|
|
|
ret = config_ep_by_speed(func->gadget, &func->function, ep->ep);
|
|
|
|
|
if (ret) {
|
|
|
|
|
pr_err("%s: config_ep_by_speed(%s) returned %d\n",
|
|
|
|
|
__func__, ep->ep->name, ret);
|
|
|
|
|
break;
|
2017-04-25 17:45:48 +08:00
|
|
|
|
}
|
2017-01-31 14:54:45 +02:00
|
|
|
|
|
2011-06-28 16:33:48 +03:00
|
|
|
|
ret = usb_ep_enable(ep->ep);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ret) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
epfile->ep = ep;
|
2018-01-24 23:58:20 -08:00
|
|
|
|
epfile->in = usb_endpoint_dir_in(ep->ep->desc);
|
|
|
|
|
epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++ep;
|
|
|
|
|
++epfile;
|
2017-01-09 13:46:00 +00:00
|
|
|
|
}
|
2017-04-18 16:11:48 -07:00
|
|
|
|
|
|
|
|
|
wake_up_interruptible(&ffs->wait);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Parsing and building descriptors and strings *****************************/
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* This validates if data pointed by data is a valid USB descriptor as
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* well as record how many interfaces, endpoints and strings are
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* required by given configuration. Returns address after the
|
|
|
|
|
* descriptor or NULL if data is invalid.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
enum ffs_entity_type {
|
|
|
|
|
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
|
|
|
|
|
};
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
enum ffs_os_desc_type {
|
|
|
|
|
FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
|
|
|
|
|
u8 *valuep,
|
|
|
|
|
struct usb_descriptor_header *desc,
|
|
|
|
|
void *priv);
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
|
|
|
|
|
struct usb_os_desc_header *h, void *data,
|
|
|
|
|
unsigned len, void *priv);
|
|
|
|
|
|
2014-07-09 12:20:06 +02:00
|
|
|
|
static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
|
|
|
|
ffs_entity_callback entity,
|
2024-08-10 20:00:05 -04:00
|
|
|
|
void *priv, int *current_class, int *current_subclass)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct usb_descriptor_header *_ds = (void *)data;
|
|
|
|
|
u8 length;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* At least two bytes are required: length and type */
|
|
|
|
|
if (len < 2) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("descriptor too short\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we have at least as many bytes as the descriptor takes? */
|
|
|
|
|
length = _ds->bLength;
|
|
|
|
|
if (len < length) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("descriptor longer then available data\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define __entity_check_INTERFACE(val) 1
|
|
|
|
|
#define __entity_check_STRING(val) (val)
|
|
|
|
|
#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
|
|
|
|
|
#define __entity(type, val) do { \
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("entity " #type "(%02x)\n", (val)); \
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!__entity_check_ ##type(val)) { \
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("invalid entity's value\n"); \
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
ret = entity(FFS_ ##type, &val, _ds, priv); \
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) { \
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_debug("entity " #type "(%02x); ret = %d\n", \
|
2010-11-12 14:29:29 +01:00
|
|
|
|
(val), ret); \
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
/* Parse descriptor depending on type. */
|
|
|
|
|
switch (_ds->bDescriptorType) {
|
|
|
|
|
case USB_DT_DEVICE:
|
|
|
|
|
case USB_DT_CONFIG:
|
|
|
|
|
case USB_DT_STRING:
|
|
|
|
|
case USB_DT_DEVICE_QUALIFIER:
|
|
|
|
|
/* function can't have any of those */
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("descriptor reserved for gadget: %d\n",
|
2010-11-12 14:29:28 +01:00
|
|
|
|
_ds->bDescriptorType);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
case USB_DT_INTERFACE: {
|
|
|
|
|
struct usb_interface_descriptor *ds = (void *)_ds;
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("interface descriptor\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (length != sizeof *ds)
|
|
|
|
|
goto inv_length;
|
|
|
|
|
|
|
|
|
|
__entity(INTERFACE, ds->bInterfaceNumber);
|
|
|
|
|
if (ds->iInterface)
|
|
|
|
|
__entity(STRING, ds->iInterface);
|
2018-10-09 14:43:18 +00:00
|
|
|
|
*current_class = ds->bInterfaceClass;
|
2024-08-10 20:00:05 -04:00
|
|
|
|
*current_subclass = ds->bInterfaceSubClass;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USB_DT_ENDPOINT: {
|
|
|
|
|
struct usb_endpoint_descriptor *ds = (void *)_ds;
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("endpoint descriptor\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (length != USB_DT_ENDPOINT_SIZE &&
|
|
|
|
|
length != USB_DT_ENDPOINT_AUDIO_SIZE)
|
|
|
|
|
goto inv_length;
|
|
|
|
|
__entity(ENDPOINT, ds->bEndpointAddress);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2018-10-09 14:43:18 +00:00
|
|
|
|
case USB_TYPE_CLASS | 0x01:
|
2021-08-01 13:54:54 +08:00
|
|
|
|
if (*current_class == USB_INTERFACE_CLASS_HID) {
|
2018-10-09 14:43:18 +00:00
|
|
|
|
pr_vdebug("hid descriptor\n");
|
|
|
|
|
if (length != sizeof(struct hid_descriptor))
|
|
|
|
|
goto inv_length;
|
|
|
|
|
break;
|
|
|
|
|
} else if (*current_class == USB_INTERFACE_CLASS_CCID) {
|
|
|
|
|
pr_vdebug("ccid descriptor\n");
|
|
|
|
|
if (length != sizeof(struct ccid_descriptor))
|
|
|
|
|
goto inv_length;
|
|
|
|
|
break;
|
2024-08-10 20:00:05 -04:00
|
|
|
|
} else if (*current_class == USB_CLASS_APP_SPEC &&
|
|
|
|
|
*current_subclass == USB_SUBCLASS_DFU) {
|
|
|
|
|
pr_vdebug("dfu functional descriptor\n");
|
|
|
|
|
if (length != sizeof(struct usb_dfu_functional_descriptor))
|
|
|
|
|
goto inv_length;
|
|
|
|
|
break;
|
2018-10-09 14:43:18 +00:00
|
|
|
|
} else {
|
|
|
|
|
pr_vdebug("unknown descriptor: %d for class %d\n",
|
|
|
|
|
_ds->bDescriptorType, *current_class);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2012-05-30 20:43:37 +02:00
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
case USB_DT_OTG:
|
|
|
|
|
if (length != sizeof(struct usb_otg_descriptor))
|
|
|
|
|
goto inv_length;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USB_DT_INTERFACE_ASSOCIATION: {
|
|
|
|
|
struct usb_interface_assoc_descriptor *ds = (void *)_ds;
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("interface association descriptor\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (length != sizeof *ds)
|
|
|
|
|
goto inv_length;
|
|
|
|
|
if (ds->iFunction)
|
|
|
|
|
__entity(STRING, ds->iFunction);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2014-02-28 16:50:22 +05:30
|
|
|
|
case USB_DT_SS_ENDPOINT_COMP:
|
|
|
|
|
pr_vdebug("EP SS companion descriptor\n");
|
|
|
|
|
if (length != sizeof(struct usb_ss_ep_comp_descriptor))
|
|
|
|
|
goto inv_length;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
case USB_DT_OTHER_SPEED_CONFIG:
|
|
|
|
|
case USB_DT_INTERFACE_POWER:
|
|
|
|
|
case USB_DT_DEBUG:
|
|
|
|
|
case USB_DT_SECURITY:
|
|
|
|
|
case USB_DT_CS_RADIO_CONTROL:
|
|
|
|
|
/* TODO */
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
/* We should never be here */
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
inv_length:
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("invalid length: %d (descriptor %d)\n",
|
2010-11-12 14:29:29 +01:00
|
|
|
|
_ds->bLength, _ds->bDescriptorType);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef __entity
|
|
|
|
|
#undef __entity_check_DESCRIPTOR
|
|
|
|
|
#undef __entity_check_INTERFACE
|
|
|
|
|
#undef __entity_check_STRING
|
|
|
|
|
#undef __entity_check_ENDPOINT
|
|
|
|
|
|
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
|
|
|
|
ffs_entity_callback entity, void *priv)
|
|
|
|
|
{
|
|
|
|
|
const unsigned _len = len;
|
|
|
|
|
unsigned long num = 0;
|
2018-10-09 14:43:18 +00:00
|
|
|
|
int current_class = -1;
|
2024-08-10 20:00:05 -04:00
|
|
|
|
int current_subclass = -1;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (num == count)
|
|
|
|
|
data = NULL;
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Record "descriptor" entity */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
|
2010-11-12 14:29:29 +01:00
|
|
|
|
num, ret);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
|
return _len - len;
|
|
|
|
|
|
2018-10-09 14:43:18 +00:00
|
|
|
|
ret = ffs_do_single_desc(data, len, entity, priv,
|
2024-08-10 20:00:05 -04:00
|
|
|
|
¤t_class, ¤t_subclass);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_debug("%s returns %d\n", __func__, ret);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len -= ret;
|
|
|
|
|
data += ret;
|
|
|
|
|
++num;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __ffs_data_do_entity(enum ffs_entity_type type,
|
|
|
|
|
u8 *valuep, struct usb_descriptor_header *desc,
|
|
|
|
|
void *priv)
|
|
|
|
|
{
|
2014-08-25 11:16:27 +02:00
|
|
|
|
struct ffs_desc_helper *helper = priv;
|
|
|
|
|
struct usb_endpoint_descriptor *d;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case FFS_DESCRIPTOR:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_INTERFACE:
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* Interfaces are indexed from zero so if we
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* encountered interface "n" then there are at least
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* "n+1" interfaces.
|
|
|
|
|
*/
|
2014-08-25 11:16:27 +02:00
|
|
|
|
if (*valuep >= helper->interfaces_count)
|
|
|
|
|
helper->interfaces_count = *valuep + 1;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_STRING:
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
2016-12-15 12:47:41 +00:00
|
|
|
|
* Strings are indexed from 1 (0 is reserved
|
|
|
|
|
* for languages list)
|
2010-11-12 14:29:28 +01:00
|
|
|
|
*/
|
2014-08-25 11:16:27 +02:00
|
|
|
|
if (*valuep > helper->ffs->strings_count)
|
|
|
|
|
helper->ffs->strings_count = *valuep;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_ENDPOINT:
|
2014-08-25 11:16:27 +02:00
|
|
|
|
d = (void *)desc;
|
|
|
|
|
helper->eps_count++;
|
2017-01-23 14:41:04 +00:00
|
|
|
|
if (helper->eps_count >= FFS_MAX_EPS_COUNT)
|
2014-08-25 11:16:27 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
/* Check if descriptors for any speed were already parsed */
|
|
|
|
|
if (!helper->ffs->eps_count && !helper->ffs->interfaces_count)
|
|
|
|
|
helper->ffs->eps_addrmap[helper->eps_count] =
|
|
|
|
|
d->bEndpointAddress;
|
|
|
|
|
else if (helper->ffs->eps_addrmap[helper->eps_count] !=
|
|
|
|
|
d->bEndpointAddress)
|
|
|
|
|
return -EINVAL;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
|
|
|
|
|
struct usb_os_desc_header *desc)
|
|
|
|
|
{
|
|
|
|
|
u16 bcd_version = le16_to_cpu(desc->bcdVersion);
|
|
|
|
|
u16 w_index = le16_to_cpu(desc->wIndex);
|
|
|
|
|
|
usb: gadget: f_fs: Fix incorrect version checking of OS descs
Currently, the USB gadget framework supports only version 1.0 of the MS OS
descriptor. OS desc has a field bcdVersion indicating its version, with
v1.0 represented by the value 0x100. However, __ffs_do_os_desc_header()
was expecting the incorrect value 0x1, so allow the correct value 0x100.
The bcdVersion field of the descriptor that is actually sent to the host
is set by composite_setup() (in composite.c) to the fixed value 0x100.
Therefore, it can be understood that __ffs_do_os_desc_header() is only
performing a format check of the OS desc passed to functionfs. If a value
other than 0x100 is accepted, there is no effect on communication over
the USB bus. Indeed, until now __ffs_do_os_desc_header() has only accepted
the incorrect value 0x1, but there was no problem with the communication
over the USB bus.
However, this can be confusing for functionfs userspace drivers. Since
bcdVersion=0x100 is used in actual communication, functionfs should accept
the value 0x100.
Note that the correct value for bcdVersion in OS desc v1.0 is 0x100, but
to avoid breaking old userspace drivers, the value 0x1 is also accepted as
an exception. At this time, a message is output to notify the user to fix
the userspace driver.
Signed-off-by: Yuta Hayama <hayama@lineo.co.jp>
Reviewed-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Link: https://lore.kernel.org/r/290f96db-1877-5137-373a-318e7b4f2dde@lineo.co.jp
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-02-28 17:56:37 +09:00
|
|
|
|
if (bcd_version == 0x1) {
|
|
|
|
|
pr_warn("bcdVersion must be 0x0100, stored in Little Endian order. "
|
|
|
|
|
"Userspace driver should be fixed, accepting 0x0001 for compatibility.\n");
|
|
|
|
|
} else if (bcd_version != 0x100) {
|
|
|
|
|
pr_vdebug("unsupported os descriptors version: 0x%x\n",
|
2014-07-09 12:20:08 +02:00
|
|
|
|
bcd_version);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
switch (w_index) {
|
|
|
|
|
case 0x4:
|
|
|
|
|
*next_type = FFS_OS_DESC_EXT_COMPAT;
|
|
|
|
|
break;
|
|
|
|
|
case 0x5:
|
|
|
|
|
*next_type = FFS_OS_DESC_EXT_PROP;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_vdebug("unsupported os descriptor type: %d", w_index);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sizeof(*desc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Process all extended compatibility/extended property descriptors
|
|
|
|
|
* of a feature descriptor
|
|
|
|
|
*/
|
|
|
|
|
static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
|
|
|
|
|
enum ffs_os_desc_type type,
|
|
|
|
|
u16 feature_count,
|
|
|
|
|
ffs_os_desc_callback entity,
|
|
|
|
|
void *priv,
|
|
|
|
|
struct usb_os_desc_header *h)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
const unsigned _len = len;
|
|
|
|
|
|
|
|
|
|
/* loop over all ext compat/ext prop descriptors */
|
|
|
|
|
while (feature_count--) {
|
|
|
|
|
ret = entity(type, h, data, len, priv);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2014-07-09 12:20:08 +02:00
|
|
|
|
pr_debug("bad OS descriptor, type: %d\n", type);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
data += ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
}
|
|
|
|
|
return _len - len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
|
|
|
|
|
static int __must_check ffs_do_os_descs(unsigned count,
|
|
|
|
|
char *data, unsigned len,
|
|
|
|
|
ffs_os_desc_callback entity, void *priv)
|
|
|
|
|
{
|
|
|
|
|
const unsigned _len = len;
|
|
|
|
|
unsigned long num = 0;
|
|
|
|
|
|
|
|
|
|
for (num = 0; num < count; ++num) {
|
|
|
|
|
int ret;
|
|
|
|
|
enum ffs_os_desc_type type;
|
|
|
|
|
u16 feature_count;
|
|
|
|
|
struct usb_os_desc_header *desc = (void *)data;
|
|
|
|
|
|
|
|
|
|
if (len < sizeof(*desc))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Record "descriptor" entity.
|
|
|
|
|
* Process dwLength, bcdVersion, wIndex, get b/wCount.
|
|
|
|
|
* Move the data pointer to the beginning of extended
|
|
|
|
|
* compatibilities proper or extended properties proper
|
|
|
|
|
* portions of the data
|
|
|
|
|
*/
|
|
|
|
|
if (le32_to_cpu(desc->dwLength) > len)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
ret = __ffs_do_os_desc_header(&type, desc);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2014-07-09 12:20:08 +02:00
|
|
|
|
pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
|
|
|
|
|
num, ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
|
|
|
|
|
*/
|
|
|
|
|
feature_count = le16_to_cpu(desc->wCount);
|
|
|
|
|
if (type == FFS_OS_DESC_EXT_COMPAT &&
|
|
|
|
|
(feature_count > 255 || desc->Reserved))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
len -= ret;
|
|
|
|
|
data += ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Process all function/property descriptors
|
|
|
|
|
* of this Feature Descriptor
|
|
|
|
|
*/
|
|
|
|
|
ret = ffs_do_single_os_desc(data, len, type,
|
|
|
|
|
feature_count, entity, priv, desc);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0) {
|
2014-07-09 12:20:08 +02:00
|
|
|
|
pr_debug("%s returns %d\n", __func__, ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len -= ret;
|
|
|
|
|
data += ret;
|
|
|
|
|
}
|
|
|
|
|
return _len - len;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-06 14:33:20 +01:00
|
|
|
|
/*
|
2014-07-09 12:20:08 +02:00
|
|
|
|
* Validate contents of the buffer from userspace related to OS descriptors.
|
|
|
|
|
*/
|
|
|
|
|
static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
|
|
|
|
|
struct usb_os_desc_header *h, void *data,
|
|
|
|
|
unsigned len, void *priv)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = priv;
|
|
|
|
|
u8 length;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case FFS_OS_DESC_EXT_COMPAT: {
|
|
|
|
|
struct usb_ext_compat_desc *d = data;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (len < sizeof(*d) ||
|
2017-11-27 18:15:40 +00:00
|
|
|
|
d->bFirstInterfaceNumber >= ffs->interfaces_count)
|
2014-07-09 12:20:08 +02:00
|
|
|
|
return -EINVAL;
|
2017-11-27 18:15:40 +00:00
|
|
|
|
if (d->Reserved1 != 1) {
|
|
|
|
|
/*
|
|
|
|
|
* According to the spec, Reserved1 must be set to 1
|
|
|
|
|
* but older kernels incorrectly rejected non-zero
|
|
|
|
|
* values. We fix it here to avoid returning EINVAL
|
|
|
|
|
* in response to values we used to accept.
|
|
|
|
|
*/
|
|
|
|
|
pr_debug("usb_ext_compat_desc::Reserved1 forced to 1\n");
|
|
|
|
|
d->Reserved1 = 1;
|
|
|
|
|
}
|
2014-07-09 12:20:08 +02:00
|
|
|
|
for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
|
|
|
|
|
if (d->Reserved2[i])
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
length = sizeof(struct usb_ext_compat_desc);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FFS_OS_DESC_EXT_PROP: {
|
|
|
|
|
struct usb_ext_prop_desc *d = data;
|
|
|
|
|
u32 type, pdl;
|
|
|
|
|
u16 pnl;
|
|
|
|
|
|
|
|
|
|
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
length = le32_to_cpu(d->dwSize);
|
2017-01-18 00:57:44 +00:00
|
|
|
|
if (len < length)
|
|
|
|
|
return -EINVAL;
|
2014-07-09 12:20:08 +02:00
|
|
|
|
type = le32_to_cpu(d->dwPropertyDataType);
|
|
|
|
|
if (type < USB_EXT_PROP_UNICODE ||
|
|
|
|
|
type > USB_EXT_PROP_UNICODE_MULTI) {
|
|
|
|
|
pr_vdebug("unsupported os descriptor property type: %d",
|
|
|
|
|
type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
pnl = le16_to_cpu(d->wPropertyNameLength);
|
2017-01-18 00:57:44 +00:00
|
|
|
|
if (length < 14 + pnl) {
|
|
|
|
|
pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
|
|
|
|
|
length, pnl, type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2017-11-28 15:20:53 +00:00
|
|
|
|
pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl));
|
2014-07-09 12:20:08 +02:00
|
|
|
|
if (length != 14 + pnl + pdl) {
|
|
|
|
|
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
|
|
|
|
|
length, pnl, pdl, type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
++ffs->ms_os_descs_ext_prop_count;
|
|
|
|
|
/* property name reported to the host as "WCHAR"s */
|
|
|
|
|
ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
|
|
|
|
|
ffs->ms_os_descs_ext_prop_data_len += pdl;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_vdebug("unknown descriptor: %d\n", type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int __ffs_data_got_descs(struct ffs_data *ffs,
|
|
|
|
|
char *const _data, size_t len)
|
|
|
|
|
{
|
2014-02-28 16:50:23 +05:30
|
|
|
|
char *data = _data, *raw_descs;
|
2014-07-09 12:20:08 +02:00
|
|
|
|
unsigned os_descs_count = 0, counts[3], flags;
|
2014-02-28 16:50:23 +05:30
|
|
|
|
int ret = -EINVAL, i;
|
2014-08-25 11:16:27 +02:00
|
|
|
|
struct ffs_desc_helper helper;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
if (get_unaligned_le32(data + 4) != len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
switch (get_unaligned_le32(data)) {
|
|
|
|
|
case FUNCTIONFS_DESCRIPTORS_MAGIC:
|
|
|
|
|
flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
|
|
|
|
|
data += 8;
|
|
|
|
|
len -= 8;
|
|
|
|
|
break;
|
|
|
|
|
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
|
|
|
|
|
flags = get_unaligned_le32(data + 8);
|
2014-09-09 08:23:17 +02:00
|
|
|
|
ffs->user_flags = flags;
|
2014-02-28 16:50:23 +05:30
|
|
|
|
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
|
|
|
|
|
FUNCTIONFS_HAS_HS_DESC |
|
2014-07-09 12:20:08 +02:00
|
|
|
|
FUNCTIONFS_HAS_SS_DESC |
|
2014-09-09 08:23:17 +02:00
|
|
|
|
FUNCTIONFS_HAS_MS_OS_DESC |
|
2015-01-23 13:41:01 +01:00
|
|
|
|
FUNCTIONFS_VIRTUAL_ADDR |
|
2016-06-22 01:12:07 +02:00
|
|
|
|
FUNCTIONFS_EVENTFD |
|
2016-06-22 01:12:09 +02:00
|
|
|
|
FUNCTIONFS_ALL_CTRL_RECIP |
|
|
|
|
|
FUNCTIONFS_CONFIG0_SETUP)) {
|
2014-02-28 16:50:23 +05:30
|
|
|
|
ret = -ENOSYS;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
2014-02-28 16:50:23 +05:30
|
|
|
|
data += 12;
|
|
|
|
|
len -= 12;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
goto error;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-23 13:41:01 +01:00
|
|
|
|
if (flags & FUNCTIONFS_EVENTFD) {
|
|
|
|
|
if (len < 4)
|
|
|
|
|
goto error;
|
|
|
|
|
ffs->ffs_eventfd =
|
|
|
|
|
eventfd_ctx_fdget((int)get_unaligned_le32(data));
|
|
|
|
|
if (IS_ERR(ffs->ffs_eventfd)) {
|
|
|
|
|
ret = PTR_ERR(ffs->ffs_eventfd);
|
|
|
|
|
ffs->ffs_eventfd = NULL;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
data += 4;
|
|
|
|
|
len -= 4;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
/* Read fs_count, hs_count and ss_count (if present) */
|
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
|
|
|
if (!(flags & (1 << i))) {
|
|
|
|
|
counts[i] = 0;
|
|
|
|
|
} else if (len < 4) {
|
2014-02-28 16:50:22 +05:30
|
|
|
|
goto error;
|
2014-02-28 16:50:23 +05:30
|
|
|
|
} else {
|
|
|
|
|
counts[i] = get_unaligned_le32(data);
|
|
|
|
|
data += 4;
|
|
|
|
|
len -= 4;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
2014-07-09 12:20:08 +02:00
|
|
|
|
if (flags & (1 << i)) {
|
2017-01-18 00:57:44 +00:00
|
|
|
|
if (len < 4) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
2014-07-09 12:20:08 +02:00
|
|
|
|
os_descs_count = get_unaligned_le32(data);
|
|
|
|
|
data += 4;
|
|
|
|
|
len -= 4;
|
2020-04-18 16:18:07 +08:00
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
/* Read descriptors */
|
|
|
|
|
raw_descs = data;
|
2014-08-25 11:16:27 +02:00
|
|
|
|
helper.ffs = ffs;
|
2014-02-28 16:50:23 +05:30
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
|
|
|
if (!counts[i])
|
|
|
|
|
continue;
|
2014-08-25 11:16:27 +02:00
|
|
|
|
helper.interfaces_count = 0;
|
|
|
|
|
helper.eps_count = 0;
|
2014-02-28 16:50:23 +05:30
|
|
|
|
ret = ffs_do_descs(counts[i], data, len,
|
2014-08-25 11:16:27 +02:00
|
|
|
|
__ffs_data_do_entity, &helper);
|
2014-02-28 16:50:23 +05:30
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
2014-08-25 11:16:27 +02:00
|
|
|
|
if (!ffs->eps_count && !ffs->interfaces_count) {
|
|
|
|
|
ffs->eps_count = helper.eps_count;
|
|
|
|
|
ffs->interfaces_count = helper.interfaces_count;
|
|
|
|
|
} else {
|
|
|
|
|
if (ffs->eps_count != helper.eps_count) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
if (ffs->interfaces_count != helper.interfaces_count) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-28 16:50:23 +05:30
|
|
|
|
data += ret;
|
|
|
|
|
len -= ret;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
2014-07-09 12:20:08 +02:00
|
|
|
|
if (os_descs_count) {
|
|
|
|
|
ret = ffs_do_os_descs(os_descs_count, data, len,
|
|
|
|
|
__ffs_data_do_os_desc, ffs);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
data += ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
if (raw_descs == data || len) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
ffs->raw_descs_data = _data;
|
|
|
|
|
ffs->raw_descs = raw_descs;
|
|
|
|
|
ffs->raw_descs_length = data - raw_descs;
|
|
|
|
|
ffs->fs_descs_count = counts[0];
|
|
|
|
|
ffs->hs_descs_count = counts[1];
|
|
|
|
|
ffs->ss_descs_count = counts[2];
|
2014-07-09 12:20:08 +02:00
|
|
|
|
ffs->ms_os_descs_count = os_descs_count;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
kfree(_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __ffs_data_got_strings(struct ffs_data *ffs,
|
|
|
|
|
char *const _data, size_t len)
|
|
|
|
|
{
|
|
|
|
|
u32 str_count, needed_count, lang_count;
|
|
|
|
|
struct usb_gadget_strings **stringtabs, *t;
|
|
|
|
|
const char *data = _data;
|
2016-05-31 14:17:21 +02:00
|
|
|
|
struct usb_string *s;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (len < 16 ||
|
|
|
|
|
get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
|
|
|
|
|
get_unaligned_le32(data + 4) != len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
str_count = get_unaligned_le32(data + 8);
|
|
|
|
|
lang_count = get_unaligned_le32(data + 12);
|
|
|
|
|
|
|
|
|
|
/* if one is zero the other must be zero */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!str_count != !lang_count)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
/* Do we have at least as many strings as descriptors need? */
|
|
|
|
|
needed_count = ffs->strings_count;
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (str_count < needed_count)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* If we don't need any strings just return and free all
|
|
|
|
|
* memory.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (!needed_count) {
|
|
|
|
|
kfree(_data);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Allocate everything in one chunk so there's less maintenance. */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned i = 0;
|
2013-12-03 15:15:30 +01:00
|
|
|
|
vla_group(d);
|
|
|
|
|
vla_item(d, struct usb_gadget_strings *, stringtabs,
|
2022-09-01 17:59:42 +03:00
|
|
|
|
size_add(lang_count, 1));
|
2013-12-03 15:15:30 +01:00
|
|
|
|
vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
|
|
|
|
|
vla_item(d, struct usb_string, strings,
|
2022-09-01 17:59:42 +03:00
|
|
|
|
size_mul(lang_count, (needed_count + 1)));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!vlabuf) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
kfree(_data);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
/* Initialize the VLA pointers */
|
|
|
|
|
stringtabs = vla_ptr(vlabuf, d, stringtabs);
|
|
|
|
|
t = vla_ptr(vlabuf, d, stringtab);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
i = lang_count;
|
|
|
|
|
do {
|
|
|
|
|
*stringtabs++ = t++;
|
|
|
|
|
} while (--i);
|
|
|
|
|
*stringtabs = NULL;
|
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
/* stringtabs = vlabuf = d_stringtabs for later kfree */
|
|
|
|
|
stringtabs = vla_ptr(vlabuf, d, stringtabs);
|
|
|
|
|
t = vla_ptr(vlabuf, d, stringtab);
|
|
|
|
|
s = vla_ptr(vlabuf, d, strings);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For each language */
|
|
|
|
|
data += 16;
|
|
|
|
|
len -= 16;
|
|
|
|
|
|
|
|
|
|
do { /* lang_count > 0 so we can use do-while */
|
|
|
|
|
unsigned needed = needed_count;
|
2021-03-17 15:41:09 -07:00
|
|
|
|
u32 str_per_lang = str_count;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (len < 3)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error_free;
|
|
|
|
|
t->language = get_unaligned_le16(data);
|
|
|
|
|
t->strings = s;
|
|
|
|
|
++t;
|
|
|
|
|
|
|
|
|
|
data += 2;
|
|
|
|
|
len -= 2;
|
|
|
|
|
|
|
|
|
|
/* For each string */
|
|
|
|
|
do { /* str_count > 0 so we can use do-while */
|
|
|
|
|
size_t length = strnlen(data, len);
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (length == len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error_free;
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* User may provide more strings then we need,
|
|
|
|
|
* if that's the case we simply ignore the
|
|
|
|
|
* rest
|
|
|
|
|
*/
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (needed) {
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* s->id will be set while adding
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* function to configuration so for
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* now just leave garbage here.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
s->s = data;
|
|
|
|
|
--needed;
|
|
|
|
|
++s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data += length + 1;
|
|
|
|
|
len -= length + 1;
|
2021-03-17 15:41:09 -07:00
|
|
|
|
} while (--str_per_lang);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
s->id = 0; /* terminator */
|
|
|
|
|
s->s = NULL;
|
|
|
|
|
++s;
|
|
|
|
|
|
|
|
|
|
} while (--lang_count);
|
|
|
|
|
|
|
|
|
|
/* Some garbage left? */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error_free;
|
|
|
|
|
|
|
|
|
|
/* Done! */
|
|
|
|
|
ffs->stringtabs = stringtabs;
|
|
|
|
|
ffs->raw_strings = _data;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error_free:
|
|
|
|
|
kfree(stringtabs);
|
|
|
|
|
error:
|
|
|
|
|
kfree(_data);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Events handling and management *******************************************/
|
|
|
|
|
|
|
|
|
|
static void __ffs_event_add(struct ffs_data *ffs,
|
|
|
|
|
enum usb_functionfs_event_type type)
|
|
|
|
|
{
|
|
|
|
|
enum usb_functionfs_event_type rem_type1, rem_type2 = type;
|
|
|
|
|
int neg = 0;
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* Abort any unhandled setup
|
|
|
|
|
*
|
|
|
|
|
* We do not need to worry about some cmpxchg() changing value
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* of ffs->setup_state without holding the lock because when
|
|
|
|
|
* state is FFS_SETUP_PENDING cmpxchg() in several places in
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* the source does nothing.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (ffs->setup_state == FFS_SETUP_PENDING)
|
2014-02-10 10:42:40 +01:00
|
|
|
|
ffs->setup_state = FFS_SETUP_CANCELLED;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-09-10 17:50:24 +02:00
|
|
|
|
/*
|
|
|
|
|
* Logic of this function guarantees that there are at most four pending
|
|
|
|
|
* evens on ffs->ev.types queue. This is important because the queue
|
|
|
|
|
* has space for four elements only and __ffs_ep0_read_events function
|
|
|
|
|
* depends on that limit as well. If more event types are added, those
|
|
|
|
|
* limits have to be revisited or guaranteed to still hold.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
switch (type) {
|
|
|
|
|
case FUNCTIONFS_RESUME:
|
|
|
|
|
rem_type2 = FUNCTIONFS_SUSPEND;
|
2020-07-07 12:15:00 -05:00
|
|
|
|
fallthrough;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
case FUNCTIONFS_SUSPEND:
|
|
|
|
|
case FUNCTIONFS_SETUP:
|
|
|
|
|
rem_type1 = type;
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Discard all similar events */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FUNCTIONFS_BIND:
|
|
|
|
|
case FUNCTIONFS_UNBIND:
|
|
|
|
|
case FUNCTIONFS_DISABLE:
|
|
|
|
|
case FUNCTIONFS_ENABLE:
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Discard everything other then power management. */
|
2010-05-05 12:53:14 +02:00
|
|
|
|
rem_type1 = FUNCTIONFS_SUSPEND;
|
|
|
|
|
rem_type2 = FUNCTIONFS_RESUME;
|
|
|
|
|
neg = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2014-09-11 18:52:49 +02:00
|
|
|
|
WARN(1, "%d: unknown event, this should not happen\n", type);
|
|
|
|
|
return;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
u8 *ev = ffs->ev.types, *out = ev;
|
|
|
|
|
unsigned n = ffs->ev.count;
|
|
|
|
|
for (; n; --n, ++ev)
|
|
|
|
|
if ((*ev == rem_type1 || *ev == rem_type2) == neg)
|
|
|
|
|
*out++ = *ev;
|
|
|
|
|
else
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("purging event %d\n", *ev);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->ev.count = out - ffs->ev.types;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("adding event %d\n", type);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs->ev.types[ffs->ev.count++] = type;
|
|
|
|
|
wake_up_locked(&ffs->ev.waitq);
|
2015-01-23 13:41:01 +01:00
|
|
|
|
if (ffs->ffs_eventfd)
|
2023-11-22 13:48:23 +01:00
|
|
|
|
eventfd_signal(ffs->ffs_eventfd);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_event_add(struct ffs_data *ffs,
|
|
|
|
|
enum usb_functionfs_event_type type)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
|
|
|
|
|
__ffs_event_add(ffs, type);
|
|
|
|
|
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Bind/unbind USB function hooks *******************************************/
|
|
|
|
|
|
2014-08-25 11:16:27 +02:00
|
|
|
|
static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < ARRAY_SIZE(ffs->eps_addrmap); ++i)
|
|
|
|
|
if (ffs->eps_addrmap[i] == endpoint_address)
|
|
|
|
|
return i;
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|
|
|
|
struct usb_descriptor_header *desc,
|
|
|
|
|
void *priv)
|
|
|
|
|
{
|
|
|
|
|
struct usb_endpoint_descriptor *ds = (void *)desc;
|
|
|
|
|
struct ffs_function *func = priv;
|
|
|
|
|
struct ffs_ep *ffs_ep;
|
2014-09-09 15:06:09 +03:00
|
|
|
|
unsigned ep_desc_id;
|
|
|
|
|
int idx;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
static const char *speed_names[] = { "full", "high", "super" };
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
if (type != FFS_DESCRIPTOR)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-02-28 16:50:22 +05:30
|
|
|
|
/*
|
|
|
|
|
* If ss_descriptors is not NULL, we are reading super speed
|
|
|
|
|
* descriptors; if hs_descriptors is not NULL, we are reading high
|
|
|
|
|
* speed descriptors; otherwise, we are reading full speed
|
|
|
|
|
* descriptors.
|
|
|
|
|
*/
|
|
|
|
|
if (func->function.ss_descriptors) {
|
|
|
|
|
ep_desc_id = 2;
|
|
|
|
|
func->function.ss_descriptors[(long)valuep] = desc;
|
|
|
|
|
} else if (func->function.hs_descriptors) {
|
|
|
|
|
ep_desc_id = 1;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
func->function.hs_descriptors[(long)valuep] = desc;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
} else {
|
|
|
|
|
ep_desc_id = 0;
|
2012-10-22 22:15:06 +02:00
|
|
|
|
func->function.fs_descriptors[(long)valuep] = desc;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-08-25 11:16:27 +02:00
|
|
|
|
idx = ffs_ep_addr2idx(func->ffs, ds->bEndpointAddress) - 1;
|
|
|
|
|
if (idx < 0)
|
|
|
|
|
return idx;
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_ep = func->eps + idx;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ffs_ep->descs[ep_desc_id]) {
|
2014-02-28 16:50:22 +05:30
|
|
|
|
pr_err("two %sspeed descriptors for EP %d\n",
|
|
|
|
|
speed_names[ep_desc_id],
|
2025-06-18 15:02:16 +08:00
|
|
|
|
usb_endpoint_num(ds));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2014-02-28 16:50:22 +05:30
|
|
|
|
ffs_ep->descs[ep_desc_id] = ds;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
ffs_dump_mem(": Original ep desc", ds, ds->bLength);
|
|
|
|
|
if (ffs_ep->ep) {
|
|
|
|
|
ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
|
|
|
|
|
if (!ds->wMaxPacketSize)
|
|
|
|
|
ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
|
|
|
|
|
} else {
|
|
|
|
|
struct usb_request *req;
|
|
|
|
|
struct usb_ep *ep;
|
2014-09-09 08:23:17 +02:00
|
|
|
|
u8 bEndpointAddress;
|
2019-01-31 15:53:40 +01:00
|
|
|
|
u16 wMaxPacketSize;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-09-09 08:23:17 +02:00
|
|
|
|
/*
|
|
|
|
|
* We back up bEndpointAddress because autoconfig overwrites
|
|
|
|
|
* it with physical endpoint address.
|
|
|
|
|
*/
|
|
|
|
|
bEndpointAddress = ds->bEndpointAddress;
|
2019-01-31 15:53:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* We back up wMaxPacketSize because autoconfig treats
|
|
|
|
|
* endpoint descriptors as if they were full speed.
|
|
|
|
|
*/
|
|
|
|
|
wMaxPacketSize = ds->wMaxPacketSize;
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("autoconfig\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ep = usb_ep_autoconfig(func->gadget, ds);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!ep)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ENOTSUPP;
|
2010-11-14 19:04:49 -08:00
|
|
|
|
ep->driver_data = func->eps + idx;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
req = usb_ep_alloc_request(ep, GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!req)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
ffs_ep->ep = ep;
|
|
|
|
|
ffs_ep->req = req;
|
|
|
|
|
func->eps_revmap[ds->bEndpointAddress &
|
|
|
|
|
USB_ENDPOINT_NUMBER_MASK] = idx + 1;
|
2014-09-09 08:23:17 +02:00
|
|
|
|
/*
|
|
|
|
|
* If we use virtual address mapping, we restore
|
|
|
|
|
* original bEndpointAddress value.
|
|
|
|
|
*/
|
|
|
|
|
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
|
|
|
|
ds->bEndpointAddress = bEndpointAddress;
|
2019-01-31 15:53:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Restore wMaxPacketSize which was potentially
|
|
|
|
|
* overwritten by autoconfig.
|
|
|
|
|
*/
|
|
|
|
|
ds->wMaxPacketSize = wMaxPacketSize;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
|
|
|
|
|
struct usb_descriptor_header *desc,
|
|
|
|
|
void *priv)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = priv;
|
|
|
|
|
unsigned idx;
|
|
|
|
|
u8 newValue;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
default:
|
|
|
|
|
case FFS_DESCRIPTOR:
|
|
|
|
|
/* Handled in previous pass by __ffs_func_bind_do_descs() */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case FFS_INTERFACE:
|
|
|
|
|
idx = *valuep;
|
|
|
|
|
if (func->interfaces_nums[idx] < 0) {
|
|
|
|
|
int id = usb_interface_id(func->conf, &func->function);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (id < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return id;
|
|
|
|
|
func->interfaces_nums[idx] = id;
|
|
|
|
|
}
|
|
|
|
|
newValue = func->interfaces_nums[idx];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_STRING:
|
|
|
|
|
/* String' IDs are allocated when fsf_data is bound to cdev */
|
|
|
|
|
newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FFS_ENDPOINT:
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* USB_DT_ENDPOINT are handled in
|
|
|
|
|
* __ffs_func_bind_do_descs().
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (desc->bDescriptorType == USB_DT_ENDPOINT)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!func->eps[idx].ep)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
struct usb_endpoint_descriptor **descs;
|
|
|
|
|
descs = func->eps[idx].descs;
|
|
|
|
|
newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("%02x -> %02x\n", *valuep, newValue);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
*valuep = newValue;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
|
|
|
|
|
struct usb_os_desc_header *h, void *data,
|
|
|
|
|
unsigned len, void *priv)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = priv;
|
|
|
|
|
u8 length = 0;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case FFS_OS_DESC_EXT_COMPAT: {
|
|
|
|
|
struct usb_ext_compat_desc *desc = data;
|
|
|
|
|
struct usb_os_desc_table *t;
|
|
|
|
|
|
|
|
|
|
t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
|
|
|
|
|
t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
|
2023-12-14 12:04:15 +03:00
|
|
|
|
memcpy(t->os_desc->ext_compat_id, &desc->IDs,
|
|
|
|
|
sizeof_field(struct usb_ext_compat_desc, IDs));
|
2014-07-09 12:20:08 +02:00
|
|
|
|
length = sizeof(*desc);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FFS_OS_DESC_EXT_PROP: {
|
|
|
|
|
struct usb_ext_prop_desc *desc = data;
|
|
|
|
|
struct usb_os_desc_table *t;
|
|
|
|
|
struct usb_os_desc_ext_prop *ext_prop;
|
|
|
|
|
char *ext_prop_name;
|
|
|
|
|
char *ext_prop_data;
|
|
|
|
|
|
|
|
|
|
t = &func->function.os_desc_table[h->interface];
|
|
|
|
|
t->if_id = func->interfaces_nums[h->interface];
|
|
|
|
|
|
|
|
|
|
ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
|
|
|
|
|
func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
|
|
|
|
|
|
|
|
|
|
ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
|
|
|
|
|
ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
|
2017-11-28 15:20:53 +00:00
|
|
|
|
ext_prop->data_len = le32_to_cpu(*(__le32 *)
|
2014-07-09 12:20:08 +02:00
|
|
|
|
usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
|
|
|
|
|
length = ext_prop->name_len + ext_prop->data_len + 14;
|
|
|
|
|
|
|
|
|
|
ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
|
|
|
|
|
func->ffs->ms_os_descs_ext_prop_name_avail +=
|
|
|
|
|
ext_prop->name_len;
|
|
|
|
|
|
|
|
|
|
ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
|
|
|
|
|
func->ffs->ms_os_descs_ext_prop_data_avail +=
|
|
|
|
|
ext_prop->data_len;
|
|
|
|
|
memcpy(ext_prop_data,
|
|
|
|
|
usb_ext_prop_data_ptr(data, ext_prop->name_len),
|
|
|
|
|
ext_prop->data_len);
|
|
|
|
|
/* unicode data reported to the host as "WCHAR"s */
|
|
|
|
|
switch (ext_prop->type) {
|
|
|
|
|
case USB_EXT_PROP_UNICODE:
|
|
|
|
|
case USB_EXT_PROP_UNICODE_ENV:
|
|
|
|
|
case USB_EXT_PROP_UNICODE_LINK:
|
|
|
|
|
case USB_EXT_PROP_UNICODE_MULTI:
|
|
|
|
|
ext_prop->data_len *= 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ext_prop->data = ext_prop_data;
|
|
|
|
|
|
|
|
|
|
memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
|
|
|
|
|
ext_prop->name_len);
|
|
|
|
|
/* property name reported to the host as "WCHAR"s */
|
|
|
|
|
ext_prop->name_len *= 2;
|
|
|
|
|
ext_prop->name = ext_prop_name;
|
|
|
|
|
|
|
|
|
|
t->os_desc->ext_prop_len +=
|
|
|
|
|
ext_prop->name_len + ext_prop->data_len + 14;
|
|
|
|
|
++t->os_desc->ext_prop_count;
|
|
|
|
|
list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_vdebug("unknown descriptor: %d\n", type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:33 +01:00
|
|
|
|
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
|
|
|
|
|
struct usb_configuration *c)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct f_fs_opts *ffs_opts =
|
|
|
|
|
container_of(f->fi, struct f_fs_opts, func_inst);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
struct ffs_data *ffs_data;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Legacy gadget triggers binding in functionfs_ready_callback,
|
|
|
|
|
* which already uses locking; taking the same lock here would
|
|
|
|
|
* cause a deadlock.
|
|
|
|
|
*
|
|
|
|
|
* Configfs-enabled gadgets however do need ffs_dev_lock.
|
|
|
|
|
*/
|
|
|
|
|
if (!ffs_opts->no_configfs)
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
ffs_data = ffs_opts->dev->ffs_data;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
if (!ffs_opts->no_configfs)
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
if (ret)
|
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
func->ffs = ffs_data;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
func->conf = c;
|
|
|
|
|
func->gadget = c->cdev->gadget;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
|
|
|
|
* configurations are bound in sequence with list_for_each_entry,
|
|
|
|
|
* in each configuration its functions are bound in sequence
|
|
|
|
|
* with list_for_each_entry, so we assume no race condition
|
|
|
|
|
* with regard to ffs_opts->bound access
|
|
|
|
|
*/
|
|
|
|
|
if (!ffs_opts->refcnt) {
|
|
|
|
|
ret = functionfs_bind(func->ffs, c->cdev);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
}
|
|
|
|
|
ffs_opts->refcnt++;
|
|
|
|
|
func->function.strings = func->ffs->stringtabs;
|
|
|
|
|
|
|
|
|
|
return ffs_opts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int _ffs_func_bind(struct usb_configuration *c,
|
|
|
|
|
struct usb_function *f)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct ffs_data *ffs = func->ffs;
|
|
|
|
|
|
|
|
|
|
const int full = !!func->ffs->fs_descs_count;
|
usb: gadget: f_fs: Process all descriptors during bind
During _ffs_func_bind(), the received descriptors are evaluated
to prepare for binding with the gadget in order to allocate
endpoints and optionally set up OS descriptors. However, the
high- and super-speed descriptors are only parsed based on
whether the gadget_is_dualspeed() and gadget_is_superspeed()
calls are true, respectively.
This is a problem in case a userspace program always provides
all of the {full,high,super,OS} descriptors when configuring a
function. Then, for example if a gadget device is not capable
of SuperSpeed, the call to ffs_do_descs() for the SS descriptors
is skipped, resulting in an incorrect offset calculation for
the vla_ptr when moving on to the OS descriptors that follow.
This causes ffs_do_os_descs() to fail as it is now looking at
the SS descriptors' offset within the raw_descs buffer instead.
_ffs_func_bind() should evaluate the descriptors unconditionally,
so remove the checks for gadget speed.
Fixes: f0175ab51993 ("usb: gadget: f_fs: OS descriptors support")
Cc: stable@vger.kernel.org
Co-Developed-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-01-24 00:11:53 -08:00
|
|
|
|
const int high = !!func->ffs->hs_descs_count;
|
|
|
|
|
const int super = !!func->ffs->ss_descs_count;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
int fs_len, hs_len, ss_len, ret, i;
|
2016-05-28 07:48:10 +03:00
|
|
|
|
struct ffs_ep *eps_ptr;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
/* Make it a single chunk, less management later on */
|
2013-12-03 15:15:30 +01:00
|
|
|
|
vla_group(d);
|
|
|
|
|
vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
|
|
|
|
|
vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
|
|
|
|
|
full ? ffs->fs_descs_count + 1 : 0);
|
|
|
|
|
vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
|
|
|
|
|
high ? ffs->hs_descs_count + 1 : 0);
|
2014-02-28 16:50:22 +05:30
|
|
|
|
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
|
|
|
|
|
super ? ffs->ss_descs_count + 1 : 0);
|
2013-12-03 15:15:30 +01:00
|
|
|
|
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
|
2014-07-09 12:20:08 +02:00
|
|
|
|
vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
|
|
|
|
|
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
|
|
|
|
vla_item_with_sz(d, char[16], ext_compat,
|
|
|
|
|
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
|
|
|
|
vla_item_with_sz(d, struct usb_os_desc, os_desc,
|
|
|
|
|
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
|
|
|
|
vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
|
|
|
|
|
ffs->ms_os_descs_ext_prop_count);
|
|
|
|
|
vla_item_with_sz(d, char, ext_prop_name,
|
|
|
|
|
ffs->ms_os_descs_ext_prop_name_len);
|
|
|
|
|
vla_item_with_sz(d, char, ext_prop_data,
|
|
|
|
|
ffs->ms_os_descs_ext_prop_data_len);
|
2014-02-28 16:50:23 +05:30
|
|
|
|
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
|
2013-12-03 15:15:30 +01:00
|
|
|
|
char *vlabuf;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2014-02-28 16:50:22 +05:30
|
|
|
|
/* Has descriptors only for speeds gadget does not support */
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!(full | high | super))
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
/* Allocate a single chunk, less management later on */
|
2014-07-09 12:20:08 +02:00
|
|
|
|
vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!vlabuf)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
|
|
|
|
|
ffs->ms_os_descs_ext_prop_name_avail =
|
|
|
|
|
vla_ptr(vlabuf, d, ext_prop_name);
|
|
|
|
|
ffs->ms_os_descs_ext_prop_data_avail =
|
|
|
|
|
vla_ptr(vlabuf, d, ext_prop_data);
|
|
|
|
|
|
2014-02-28 16:50:23 +05:30
|
|
|
|
/* Copy descriptors */
|
|
|
|
|
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
|
|
|
|
|
ffs->raw_descs_length);
|
2014-02-28 16:50:22 +05:30
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
|
2016-05-28 07:48:10 +03:00
|
|
|
|
eps_ptr = vla_ptr(vlabuf, d, eps);
|
|
|
|
|
for (i = 0; i < ffs->eps_count; i++)
|
|
|
|
|
eps_ptr[i].num = -1;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2013-12-03 15:15:30 +01:00
|
|
|
|
/* Save pointers
|
|
|
|
|
* d_eps == vlabuf, func->eps used to kfree vlabuf later
|
|
|
|
|
*/
|
|
|
|
|
func->eps = vla_ptr(vlabuf, d, eps);
|
|
|
|
|
func->interfaces_nums = vla_ptr(vlabuf, d, inums);
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* Go through all the endpoint descriptors and allocate
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* endpoints first, so that later we can rewrite the endpoint
|
2010-11-12 14:29:28 +01:00
|
|
|
|
* numbers without worrying that it may be described later on.
|
|
|
|
|
*/
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (full) {
|
2013-12-03 15:15:30 +01:00
|
|
|
|
func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
|
2014-02-28 16:50:22 +05:30
|
|
|
|
fs_len = ffs_do_descs(ffs->fs_descs_count,
|
|
|
|
|
vla_ptr(vlabuf, d, raw_descs),
|
|
|
|
|
d_raw_descs__sz,
|
|
|
|
|
__ffs_func_bind_do_descs, func);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (fs_len < 0) {
|
2014-02-28 16:50:22 +05:30
|
|
|
|
ret = fs_len;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
} else {
|
2014-02-28 16:50:22 +05:30
|
|
|
|
fs_len = 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (high) {
|
2013-12-03 15:15:30 +01:00
|
|
|
|
func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
|
2014-02-28 16:50:22 +05:30
|
|
|
|
hs_len = ffs_do_descs(ffs->hs_descs_count,
|
|
|
|
|
vla_ptr(vlabuf, d, raw_descs) + fs_len,
|
|
|
|
|
d_raw_descs__sz - fs_len,
|
|
|
|
|
__ffs_func_bind_do_descs, func);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (hs_len < 0) {
|
2014-02-28 16:50:22 +05:30
|
|
|
|
ret = hs_len;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
hs_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (super) {
|
2020-10-27 16:07:31 -07:00
|
|
|
|
func->function.ss_descriptors = func->function.ssp_descriptors =
|
|
|
|
|
vla_ptr(vlabuf, d, ss_descs);
|
2014-07-09 12:20:08 +02:00
|
|
|
|
ss_len = ffs_do_descs(ffs->ss_descs_count,
|
2014-02-28 16:50:22 +05:30
|
|
|
|
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
|
|
|
|
|
d_raw_descs__sz - fs_len - hs_len,
|
|
|
|
|
__ffs_func_bind_do_descs, func);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ss_len < 0) {
|
2014-07-09 12:20:08 +02:00
|
|
|
|
ret = ss_len;
|
2013-09-27 12:28:54 +02:00
|
|
|
|
goto error;
|
2014-07-09 12:20:08 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ss_len = 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* Now handle interface numbers allocation and interface and
|
|
|
|
|
* endpoint numbers rewriting. We can do that in one go
|
|
|
|
|
* now.
|
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ret = ffs_do_descs(ffs->fs_descs_count +
|
2014-02-28 16:50:22 +05:30
|
|
|
|
(high ? ffs->hs_descs_count : 0) +
|
|
|
|
|
(super ? ffs->ss_descs_count : 0),
|
2013-12-03 15:15:30 +01:00
|
|
|
|
vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
|
2010-05-05 12:53:14 +02:00
|
|
|
|
__ffs_func_bind_do_nums, func);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
goto error;
|
|
|
|
|
|
2014-07-09 12:20:08 +02:00
|
|
|
|
func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
|
2016-05-13 20:32:16 +08:00
|
|
|
|
if (c->cdev->use_os_string) {
|
2014-07-09 12:20:08 +02:00
|
|
|
|
for (i = 0; i < ffs->interfaces_count; ++i) {
|
|
|
|
|
struct usb_os_desc *desc;
|
|
|
|
|
|
|
|
|
|
desc = func->function.os_desc_table[i].os_desc =
|
|
|
|
|
vla_ptr(vlabuf, d, os_desc) +
|
|
|
|
|
i * sizeof(struct usb_os_desc);
|
|
|
|
|
desc->ext_compat_id =
|
|
|
|
|
vla_ptr(vlabuf, d, ext_compat) + i * 16;
|
|
|
|
|
INIT_LIST_HEAD(&desc->ext_prop);
|
|
|
|
|
}
|
2016-05-13 20:32:16 +08:00
|
|
|
|
ret = ffs_do_os_descs(ffs->ms_os_descs_count,
|
|
|
|
|
vla_ptr(vlabuf, d, raw_descs) +
|
|
|
|
|
fs_len + hs_len + ss_len,
|
|
|
|
|
d_raw_descs__sz - fs_len - hs_len -
|
|
|
|
|
ss_len,
|
|
|
|
|
__ffs_func_bind_do_os_desc, func);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2016-05-13 20:32:16 +08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
2014-07-09 12:20:08 +02:00
|
|
|
|
func->function.os_desc_n =
|
|
|
|
|
c->cdev->use_os_string ? ffs->interfaces_count : 0;
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
/* And we're done */
|
|
|
|
|
ffs_event_add(ffs, FUNCTIONFS_BIND);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
/* XXX Do we need to release all claimed endpoints here? */
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:33 +01:00
|
|
|
|
static int ffs_func_bind(struct usb_configuration *c,
|
|
|
|
|
struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
|
2015-07-13 11:03:50 +02:00
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
int ret;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
|
|
|
|
|
if (IS_ERR(ffs_opts))
|
|
|
|
|
return PTR_ERR(ffs_opts);
|
|
|
|
|
|
2015-07-13 11:03:50 +02:00
|
|
|
|
ret = _ffs_func_bind(c, f);
|
|
|
|
|
if (ret && !--ffs_opts->refcnt)
|
|
|
|
|
functionfs_unbind(func->ffs);
|
|
|
|
|
|
|
|
|
|
return ret;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
/* Other USB function hooks *************************************************/
|
|
|
|
|
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
static void ffs_reset_work(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_data *ffs = container_of(work,
|
|
|
|
|
struct ffs_data, reset_work);
|
|
|
|
|
ffs_data_reset(ffs);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 13:47:08 +01:00
|
|
|
|
static int ffs_func_get_alt(struct usb_function *f,
|
|
|
|
|
unsigned int interface)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
int intf = ffs_func_revmap_intf(func, interface);
|
|
|
|
|
|
|
|
|
|
return (intf < 0) ? intf : func->cur_alt[interface];
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static int ffs_func_set_alt(struct usb_function *f,
|
|
|
|
|
unsigned interface, unsigned alt)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct ffs_data *ffs = func->ffs;
|
|
|
|
|
int ret = 0, intf;
|
|
|
|
|
|
2024-08-02 14:04:28 +00:00
|
|
|
|
if (alt > MAX_ALT_SETTINGS)
|
|
|
|
|
return -EINVAL;
|
2024-08-02 14:04:27 +00:00
|
|
|
|
|
2024-08-02 14:04:28 +00:00
|
|
|
|
intf = ffs_func_revmap_intf(func, interface);
|
|
|
|
|
if (intf < 0)
|
|
|
|
|
return intf;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
if (ffs->func)
|
|
|
|
|
ffs_func_eps_disable(ffs->func);
|
|
|
|
|
|
usb: gadget: f_fs: add "no_disconnect" mode
Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.
We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.
When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closed, and then FunctionFS
is ready for descriptors and string write,
- altsetting change causes the function to be closed - we want to keep
function alive until another functions are potentialy used, altsetting
change means that another configuration is being selected or USB cable
was unplugged, which indicates that we don't need to stay longer in
FFS_DEACTIVATED state
- unmounting of the FunctionFS instance causes the function to be closed.
Tested-by: David Cohen <david.a.cohen@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2014-12-18 09:55:10 +01:00
|
|
|
|
if (ffs->state == FFS_DEACTIVATED) {
|
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
INIT_WORK(&ffs->reset_work, ffs_reset_work);
|
|
|
|
|
schedule_work(&ffs->reset_work);
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (ffs->state != FFS_ACTIVE)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
ffs->func = func;
|
|
|
|
|
ret = ffs_func_eps_enable(func);
|
2024-03-01 13:47:08 +01:00
|
|
|
|
if (ret >= 0) {
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_event_add(ffs, FUNCTIONFS_ENABLE);
|
2024-03-01 13:47:08 +01:00
|
|
|
|
func->cur_alt[interface] = alt;
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_func_disable(struct usb_function *f)
|
|
|
|
|
{
|
2024-08-02 14:04:28 +00:00
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct ffs_data *ffs = func->ffs;
|
|
|
|
|
|
|
|
|
|
if (ffs->func)
|
|
|
|
|
ffs_func_eps_disable(ffs->func);
|
|
|
|
|
|
|
|
|
|
if (ffs->state == FFS_DEACTIVATED) {
|
|
|
|
|
ffs->state = FFS_CLOSING;
|
|
|
|
|
INIT_WORK(&ffs->reset_work, ffs_reset_work);
|
|
|
|
|
schedule_work(&ffs->reset_work);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ffs->state == FFS_ACTIVE) {
|
|
|
|
|
ffs->func = NULL;
|
|
|
|
|
ffs_event_add(ffs, FUNCTIONFS_DISABLE);
|
|
|
|
|
}
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_func_setup(struct usb_function *f,
|
|
|
|
|
const struct usb_ctrlrequest *creq)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct ffs_data *ffs = func->ffs;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
|
|
|
|
|
pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
|
|
|
|
|
pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
|
|
|
|
|
pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex));
|
|
|
|
|
pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength));
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/*
|
|
|
|
|
* Most requests directed to interface go through here
|
2010-05-05 12:53:14 +02:00
|
|
|
|
* (notable exceptions are set/get interface) so we need to
|
|
|
|
|
* handle them. All other either handled by composite or
|
|
|
|
|
* passed to usb_configuration->setup() (if one is set). No
|
|
|
|
|
* matter, we will handle requests directed to endpoint here
|
2016-06-22 01:12:07 +02:00
|
|
|
|
* as well (as it's straightforward). Other request recipient
|
|
|
|
|
* types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
|
|
|
|
|
* is being used.
|
2010-11-12 14:29:28 +01:00
|
|
|
|
*/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
if (ffs->state != FFS_ACTIVE)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
switch (creq->bRequestType & USB_RECIP_MASK) {
|
|
|
|
|
case USB_RECIP_INTERFACE:
|
|
|
|
|
ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USB_RECIP_ENDPOINT:
|
|
|
|
|
ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (ret < 0)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return ret;
|
2014-09-09 08:23:17 +02:00
|
|
|
|
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
|
|
|
|
ret = func->ffs->eps_addrmap[ret];
|
2010-05-05 12:53:14 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-06-22 01:12:07 +02:00
|
|
|
|
if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
|
|
|
|
|
ret = le16_to_cpu(creq->wIndex);
|
|
|
|
|
else
|
|
|
|
|
return -EOPNOTSUPP;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
|
|
|
|
|
ffs->ev.setup = *creq;
|
|
|
|
|
ffs->ev.setup.wIndex = cpu_to_le16(ret);
|
|
|
|
|
__ffs_event_add(ffs, FUNCTIONFS_SETUP);
|
|
|
|
|
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
|
|
|
|
|
|
2024-04-23 18:02:15 +00:00
|
|
|
|
return ffs->ev.setup.wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-22 01:12:07 +02:00
|
|
|
|
static bool ffs_func_req_match(struct usb_function *f,
|
2016-06-22 01:12:08 +02:00
|
|
|
|
const struct usb_ctrlrequest *creq,
|
|
|
|
|
bool config0)
|
2016-06-22 01:12:07 +02:00
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
|
2016-06-22 01:12:09 +02:00
|
|
|
|
if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
|
2016-06-22 01:12:08 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
2016-06-22 01:12:07 +02:00
|
|
|
|
switch (creq->bRequestType & USB_RECIP_MASK) {
|
|
|
|
|
case USB_RECIP_INTERFACE:
|
2016-11-04 00:23:26 +01:00
|
|
|
|
return (ffs_func_revmap_intf(func,
|
|
|
|
|
le16_to_cpu(creq->wIndex)) >= 0);
|
2016-06-22 01:12:07 +02:00
|
|
|
|
case USB_RECIP_ENDPOINT:
|
2016-11-04 00:23:26 +01:00
|
|
|
|
return (ffs_func_revmap_ep(func,
|
|
|
|
|
le16_to_cpu(creq->wIndex)) >= 0);
|
2016-06-22 01:12:07 +02:00
|
|
|
|
default:
|
|
|
|
|
return (bool) (func->ffs->user_flags &
|
|
|
|
|
FUNCTIONFS_ALL_CTRL_RECIP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
static void ffs_func_suspend(struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_func_resume(struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-11-12 14:29:28 +01:00
|
|
|
|
/* Endpoint and interface numbers reverse mapping ***************************/
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
|
|
|
|
static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
|
|
|
|
|
{
|
|
|
|
|
num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
|
|
|
|
|
return num ? num : -EDOM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
|
|
|
|
|
{
|
|
|
|
|
short *nums = func->interfaces_nums;
|
|
|
|
|
unsigned count = func->ffs->interfaces_count;
|
|
|
|
|
|
|
|
|
|
for (; count; --count, ++nums) {
|
|
|
|
|
if (*nums >= 0 && *nums == intf)
|
|
|
|
|
return nums - func->interfaces_nums;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -EDOM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
/* Devices management *******************************************************/
|
|
|
|
|
|
|
|
|
|
static LIST_HEAD(ffs_devices);
|
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static struct ffs_dev *_ffs_do_find_dev(const char *name)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *dev;
|
|
|
|
|
|
2017-03-10 22:45:58 +01:00
|
|
|
|
if (!name)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
list_for_each_entry(dev, &ffs_devices, entry) {
|
|
|
|
|
if (strcmp(dev->name, name) == 0)
|
|
|
|
|
return dev;
|
|
|
|
|
}
|
2013-12-03 15:15:36 +01:00
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ffs_lock must be taken by the caller of this function
|
|
|
|
|
*/
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static struct ffs_dev *_ffs_get_single_dev(void)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *dev;
|
|
|
|
|
|
|
|
|
|
if (list_is_singular(&ffs_devices)) {
|
|
|
|
|
dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
|
|
|
|
|
if (dev->single)
|
|
|
|
|
return dev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ffs_lock must be taken by the caller of this function
|
|
|
|
|
*/
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static struct ffs_dev *_ffs_find_dev(const char *name)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *dev;
|
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
dev = _ffs_get_single_dev();
|
2013-12-03 15:15:32 +01:00
|
|
|
|
if (dev)
|
|
|
|
|
return dev;
|
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
return _ffs_do_find_dev(name);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:36 +01:00
|
|
|
|
/* Configfs support *********************************************************/
|
|
|
|
|
|
|
|
|
|
static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
|
|
|
|
|
{
|
|
|
|
|
return container_of(to_config_group(item), struct f_fs_opts,
|
|
|
|
|
func_inst.group);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-26 21:32:08 +01:00
|
|
|
|
static ssize_t f_fs_opts_ready_show(struct config_item *item, char *page)
|
|
|
|
|
{
|
|
|
|
|
struct f_fs_opts *opts = to_ffs_opts(item);
|
|
|
|
|
int ready;
|
|
|
|
|
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
ready = opts->dev->desc_ready;
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
|
|
|
|
|
return sprintf(page, "%d\n", ready);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CONFIGFS_ATTR_RO(f_fs_opts_, ready);
|
|
|
|
|
|
|
|
|
|
static struct configfs_attribute *ffs_attrs[] = {
|
|
|
|
|
&f_fs_opts_attr_ready,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-03 15:15:36 +01:00
|
|
|
|
static void ffs_attr_release(struct config_item *item)
|
|
|
|
|
{
|
|
|
|
|
struct f_fs_opts *opts = to_ffs_opts(item);
|
|
|
|
|
|
|
|
|
|
usb_put_function_instance(&opts->func_inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct configfs_item_operations ffs_item_ops = {
|
|
|
|
|
.release = ffs_attr_release,
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-16 17:18:41 +02:00
|
|
|
|
static const struct config_item_type ffs_func_type = {
|
2013-12-03 15:15:36 +01:00
|
|
|
|
.ct_item_ops = &ffs_item_ops,
|
2024-01-26 21:32:08 +01:00
|
|
|
|
.ct_attrs = ffs_attrs,
|
2013-12-03 15:15:36 +01:00
|
|
|
|
.ct_owner = THIS_MODULE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-12-03 15:15:33 +01:00
|
|
|
|
/* Function registration interface ******************************************/
|
|
|
|
|
|
|
|
|
|
static void ffs_free_inst(struct usb_function_instance *f)
|
|
|
|
|
{
|
|
|
|
|
struct f_fs_opts *opts;
|
|
|
|
|
|
|
|
|
|
opts = to_f_fs_opts(f);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
ffs_release_dev(opts->dev);
|
2013-12-03 15:15:33 +01:00
|
|
|
|
ffs_dev_lock();
|
2014-01-13 16:49:38 +01:00
|
|
|
|
_ffs_free_dev(opts->dev);
|
2013-12-03 15:15:33 +01:00
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
kfree(opts);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:36 +01:00
|
|
|
|
static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
|
|
|
|
|
{
|
2019-12-09 10:31:43 -08:00
|
|
|
|
if (strlen(name) >= sizeof_field(struct ffs_dev, name))
|
2013-12-03 15:15:36 +01:00
|
|
|
|
return -ENAMETOOLONG;
|
2017-03-10 22:45:58 +01:00
|
|
|
|
return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
|
2013-12-03 15:15:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:33 +01:00
|
|
|
|
static struct usb_function_instance *ffs_alloc_inst(void)
|
|
|
|
|
{
|
|
|
|
|
struct f_fs_opts *opts;
|
|
|
|
|
struct ffs_dev *dev;
|
|
|
|
|
|
|
|
|
|
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
|
|
|
|
if (!opts)
|
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
2013-12-03 15:15:36 +01:00
|
|
|
|
opts->func_inst.set_inst_name = ffs_set_inst_name;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
opts->func_inst.free_func_inst = ffs_free_inst;
|
|
|
|
|
ffs_dev_lock();
|
2014-01-13 16:49:38 +01:00
|
|
|
|
dev = _ffs_alloc_dev();
|
2013-12-03 15:15:33 +01:00
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
|
kfree(opts);
|
|
|
|
|
return ERR_CAST(dev);
|
|
|
|
|
}
|
|
|
|
|
opts->dev = dev;
|
2013-12-03 15:15:36 +01:00
|
|
|
|
dev->opts = opts;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
|
2013-12-03 15:15:36 +01:00
|
|
|
|
config_group_init_type_name(&opts->func_inst.group, "",
|
|
|
|
|
&ffs_func_type);
|
2013-12-03 15:15:33 +01:00
|
|
|
|
return &opts->func_inst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_free(struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
kfree(ffs_func_from_usb(f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_func_unbind(struct usb_configuration *c,
|
|
|
|
|
struct usb_function *f)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func = ffs_func_from_usb(f);
|
|
|
|
|
struct ffs_data *ffs = func->ffs;
|
|
|
|
|
struct f_fs_opts *opts =
|
|
|
|
|
container_of(f->fi, struct f_fs_opts, func_inst);
|
|
|
|
|
struct ffs_ep *ep = func->eps;
|
|
|
|
|
unsigned count = ffs->eps_count;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
if (ffs->func == func) {
|
|
|
|
|
ffs_func_eps_disable(func);
|
|
|
|
|
ffs->func = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 17:44:21 -07:00
|
|
|
|
/* Drain any pending AIO completions */
|
|
|
|
|
drain_workqueue(ffs->io_completion_wq);
|
|
|
|
|
|
2023-05-25 14:58:54 +05:30
|
|
|
|
ffs_event_add(ffs, FUNCTIONFS_UNBIND);
|
2013-12-03 15:15:33 +01:00
|
|
|
|
if (!--opts->refcnt)
|
|
|
|
|
functionfs_unbind(ffs);
|
|
|
|
|
|
|
|
|
|
/* cleanup after autoconfig */
|
|
|
|
|
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
2017-01-09 13:46:00 +00:00
|
|
|
|
while (count--) {
|
2013-12-03 15:15:33 +01:00
|
|
|
|
if (ep->ep && ep->req)
|
|
|
|
|
usb_ep_free_request(ep->ep, ep->req);
|
|
|
|
|
ep->req = NULL;
|
|
|
|
|
++ep;
|
2017-01-09 13:46:00 +00:00
|
|
|
|
}
|
2013-12-03 15:15:33 +01:00
|
|
|
|
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
|
|
|
|
kfree(func->eps);
|
|
|
|
|
func->eps = NULL;
|
|
|
|
|
/*
|
|
|
|
|
* eps, descriptors and interfaces_nums are allocated in the
|
|
|
|
|
* same chunk so only one free is required.
|
|
|
|
|
*/
|
|
|
|
|
func->function.fs_descriptors = NULL;
|
|
|
|
|
func->function.hs_descriptors = NULL;
|
2014-02-28 16:50:22 +05:30
|
|
|
|
func->function.ss_descriptors = NULL;
|
2020-10-27 16:07:31 -07:00
|
|
|
|
func->function.ssp_descriptors = NULL;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
func->interfaces_nums = NULL;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_function *func;
|
|
|
|
|
|
|
|
|
|
func = kzalloc(sizeof(*func), GFP_KERNEL);
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!func)
|
2013-12-03 15:15:33 +01:00
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
|
|
func->function.name = "Function FS Gadget";
|
|
|
|
|
|
|
|
|
|
func->function.bind = ffs_func_bind;
|
|
|
|
|
func->function.unbind = ffs_func_unbind;
|
|
|
|
|
func->function.set_alt = ffs_func_set_alt;
|
2024-03-01 13:47:08 +01:00
|
|
|
|
func->function.get_alt = ffs_func_get_alt;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
func->function.disable = ffs_func_disable;
|
|
|
|
|
func->function.setup = ffs_func_setup;
|
2016-06-22 01:12:07 +02:00
|
|
|
|
func->function.req_match = ffs_func_req_match;
|
2013-12-03 15:15:33 +01:00
|
|
|
|
func->function.suspend = ffs_func_suspend;
|
|
|
|
|
func->function.resume = ffs_func_resume;
|
|
|
|
|
func->function.free_func = ffs_free;
|
|
|
|
|
|
|
|
|
|
return &func->function;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
/*
|
|
|
|
|
* ffs_lock must be taken by the caller of this function
|
|
|
|
|
*/
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static struct ffs_dev *_ffs_alloc_dev(void)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *dev;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
if (_ffs_get_single_dev())
|
2013-12-03 15:15:32 +01:00
|
|
|
|
return ERR_PTR(-EBUSY);
|
|
|
|
|
|
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
|
|
|
if (!dev)
|
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
|
|
if (list_empty(&ffs_devices)) {
|
|
|
|
|
ret = functionfs_init();
|
|
|
|
|
if (ret) {
|
|
|
|
|
kfree(dev);
|
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_add(&dev->entry, &ffs_devices);
|
|
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-10 22:45:58 +01:00
|
|
|
|
int ffs_name_dev(struct ffs_dev *dev, const char *name)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *existing;
|
2017-03-10 22:45:58 +01:00
|
|
|
|
int ret = 0;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
2017-03-10 22:45:58 +01:00
|
|
|
|
ffs_dev_lock();
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
2017-03-10 22:45:58 +01:00
|
|
|
|
existing = _ffs_do_find_dev(name);
|
|
|
|
|
if (!existing)
|
2022-08-18 23:01:15 +02:00
|
|
|
|
strscpy(dev->name, name, ARRAY_SIZE(dev->name));
|
2017-03-10 22:45:58 +01:00
|
|
|
|
else if (existing != dev)
|
|
|
|
|
ret = -EBUSY;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2014-04-01 13:19:32 -05:00
|
|
|
|
EXPORT_SYMBOL_GPL(ffs_name_dev);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
int ffs_single_dev(struct ffs_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
|
|
|
|
|
if (!list_is_singular(&ffs_devices))
|
|
|
|
|
ret = -EBUSY;
|
|
|
|
|
else
|
|
|
|
|
dev->single = true;
|
|
|
|
|
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2014-04-01 13:19:32 -05:00
|
|
|
|
EXPORT_SYMBOL_GPL(ffs_single_dev);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ffs_lock must be taken by the caller of this function
|
|
|
|
|
*/
|
2014-01-13 16:49:38 +01:00
|
|
|
|
static void _ffs_free_dev(struct ffs_dev *dev)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
list_del(&dev->entry);
|
2016-09-08 11:18:16 +02:00
|
|
|
|
|
2013-12-03 15:15:32 +01:00
|
|
|
|
kfree(dev);
|
|
|
|
|
if (list_empty(&ffs_devices))
|
|
|
|
|
functionfs_cleanup();
|
|
|
|
|
}
|
|
|
|
|
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
int ret = 0;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
struct ffs_dev *ffs_dev;
|
|
|
|
|
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
|
2014-01-13 16:49:38 +01:00
|
|
|
|
ffs_dev = _ffs_find_dev(dev_name);
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
if (!ffs_dev) {
|
|
|
|
|
ret = -ENOENT;
|
|
|
|
|
} else if (ffs_dev->mounted) {
|
|
|
|
|
ret = -EBUSY;
|
|
|
|
|
} else if (ffs_dev->ffs_acquire_dev_callback &&
|
|
|
|
|
ffs_dev->ffs_acquire_dev_callback(ffs_dev)) {
|
|
|
|
|
ret = -ENOENT;
|
|
|
|
|
} else {
|
2013-12-03 15:15:32 +01:00
|
|
|
|
ffs_dev->mounted = true;
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
ffs_dev->ffs_data = ffs_data;
|
|
|
|
|
ffs_data->private_data = ffs_dev;
|
|
|
|
|
}
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
ffs_dev_unlock();
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
return ret;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
static void ffs_release_dev(struct ffs_dev *ffs_dev)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
{
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
if (ffs_dev && ffs_dev->mounted) {
|
2013-12-03 15:15:32 +01:00
|
|
|
|
ffs_dev->mounted = false;
|
usb: gadget: f_fs: Fix setting of device and driver data cross-references
FunctionFS device structure 'struct ffs_dev' and driver data structure
'struct ffs_data' are bound to each other with cross-reference pointers
'ffs_data->private_data' and 'ffs_dev->ffs_data'. While the first one
is supposed to be valid through the whole life of 'struct ffs_data'
(and while 'struct ffs_dev' exists non-freed), the second one is cleared
in 'ffs_closed()' (called from 'ffs_data_reset()' or the last
'ffs_data_put()'). This can be called several times, alternating in
different order with 'ffs_free_inst()', that, if possible, clears
the other cross-reference.
As a result, different cases of these calls order may leave stale
cross-reference pointers, used when the pointed structure is already
freed. Even if it occasionally doesn't cause kernel crash, this error
is reported by KASAN-enabled kernel configuration.
For example, the case [last 'ffs_data_put()' - 'ffs_free_inst()'] was
fixed by commit cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in
ffs_free_inst").
The other case ['ffs_data_reset()' - 'ffs_free_inst()' - 'ffs_data_put()']
now causes KASAN reported error [1], when 'ffs_data_reset()' clears
'ffs_dev->ffs_data', then 'ffs_free_inst()' frees the 'struct ffs_dev',
but can't clear 'ffs_data->private_data', which is then accessed
in 'ffs_closed()' called from 'ffs_data_put()'. This happens since
'ffs_dev->ffs_data' reference is cleared too early.
Moreover, one more use case, when 'ffs_free_inst()' is called immediately
after mounting FunctionFS device (that is before the descriptors are
written and 'ffs_ready()' is called), and then 'ffs_data_reset()'
or 'ffs_data_put()' is called from accessing "ep0" file or unmounting
the device. This causes KASAN error report like [2], since
'ffs_dev->ffs_data' is not yet set when 'ffs_free_inst()' can't properly
clear 'ffs_data->private_data', that is later accessed to freed structure.
Fix these (and may be other) cases of stale pointers access by moving
setting and clearing of the mentioned cross-references to the single
places, setting both of them when 'struct ffs_data' is created and
bound to 'struct ffs_dev', and clearing both of them when one of the
structures is destroyed. It seems convenient to make this pointer
initialization and structures binding in 'ffs_acquire_dev()' and
make pointers clearing in 'ffs_release_dev()'. This required some
changes in these functions parameters and return types.
Also, 'ffs_release_dev()' calling requires some cleanup, fixing minor
issues, like (1) 'ffs_release_dev()' is not called if 'ffs_free_inst()'
is called without unmounting the device, and "release_dev" callback
is not called at all, or (2) "release_dev" callback is called before
"ffs_closed" callback on unmounting, which seems to be not correctly
nested with "acquire_dev" and "ffs_ready" callbacks.
Make this cleanup togther with other mentioned 'ffs_release_dev()' changes.
[1]
==================================================================
root@rcar-gen3:~# mkdir /dev/cfs
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 64.340664] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# cd /dev/ffs
root@rcar-gen3:/dev/ffs# /home/root/ffs-test
ffs-test: info: ep0: writing descriptors (in v2 format)
[ 83.181442] read descriptors
[ 83.186085] read strings
ffs-test: info: ep0: writing strings
ffs-test: dbg: ep1: starting
ffs-test: dbg: ep2: starting
ffs-test: info: ep1: starts
ffs-test: info: ep2: starts
ffs-test: info: ep0: starts
^C
root@rcar-gen3:/dev/ffs# cd /home/root/
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 98.935061] unloading
root@rcar-gen3:~# umount /dev/ffs
[ 102.734301] ==================================================================
[ 102.742059] BUG: KASAN: use-after-free in ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.749683] Write of size 1 at addr ffff0004d46ff549 by task umount/2997
[ 102.756709]
[ 102.758311] CPU: 0 PID: 2997 Comm: umount Not tainted 5.13.0-rc4+ #8
[ 102.764971] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 102.772179] Call trace:
[ 102.774779] dump_backtrace+0x0/0x330
[ 102.778653] show_stack+0x20/0x2c
[ 102.782152] dump_stack+0x11c/0x1ac
[ 102.785833] print_address_description.constprop.0+0x30/0x274
[ 102.791862] kasan_report+0x14c/0x1c8
[ 102.795719] __asan_report_store1_noabort+0x34/0x58
[ 102.800840] ffs_release_dev+0x64/0xa8 [usb_f_fs]
[ 102.805801] ffs_fs_kill_sb+0x50/0x84 [usb_f_fs]
[ 102.810663] deactivate_locked_super+0xa0/0xf0
[ 102.815339] deactivate_super+0x98/0xac
[ 102.819378] cleanup_mnt+0xd0/0x1b0
[ 102.823057] __cleanup_mnt+0x1c/0x28
[ 102.826823] task_work_run+0x104/0x180
[ 102.830774] do_notify_resume+0x458/0x14e0
[ 102.835083] work_pending+0xc/0x5f8
[ 102.838762]
[ 102.840357] Allocated by task 2988:
[ 102.844032] kasan_save_stack+0x28/0x58
[ 102.848071] kasan_set_track+0x28/0x3c
[ 102.852016] ____kasan_kmalloc+0x84/0x9c
[ 102.856142] __kasan_kmalloc+0x10/0x1c
[ 102.860088] __kmalloc+0x214/0x2f8
[ 102.863678] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 102.868990] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 102.873942] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 102.880629] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 102.886858] function_make+0x128/0x1ec [libcomposite]
[ 102.892185] configfs_mkdir+0x330/0x590 [configfs]
[ 102.897245] vfs_mkdir+0x12c/0x1bc
[ 102.900835] do_mkdirat+0x180/0x1d0
[ 102.904513] __arm64_sys_mkdirat+0x80/0x94
[ 102.908822] invoke_syscall+0xf8/0x25c
[ 102.912772] el0_svc_common.constprop.0+0x150/0x1a0
[ 102.917891] do_el0_svc+0xa0/0xd4
[ 102.921386] el0_svc+0x24/0x34
[ 102.924613] el0_sync_handler+0xcc/0x154
[ 102.928743] el0_sync+0x198/0x1c0
[ 102.932238]
[ 102.933832] Freed by task 2996:
[ 102.937144] kasan_save_stack+0x28/0x58
[ 102.941181] kasan_set_track+0x28/0x3c
[ 102.945128] kasan_set_free_info+0x28/0x4c
[ 102.949435] ____kasan_slab_free+0x104/0x118
[ 102.953921] __kasan_slab_free+0x18/0x24
[ 102.958047] slab_free_freelist_hook+0x148/0x1f0
[ 102.962897] kfree+0x318/0x440
[ 102.966123] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 102.971075] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 102.977302] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 102.982344] config_item_put+0x140/0x1a4 [configfs]
[ 102.987486] configfs_rmdir+0x3fc/0x518 [configfs]
[ 102.992535] vfs_rmdir+0x114/0x234
[ 102.996122] do_rmdir+0x274/0x2b0
[ 102.999617] __arm64_sys_unlinkat+0x94/0xc8
[ 103.004015] invoke_syscall+0xf8/0x25c
[ 103.007961] el0_svc_common.constprop.0+0x150/0x1a0
[ 103.013080] do_el0_svc+0xa0/0xd4
[ 103.016575] el0_svc+0x24/0x34
[ 103.019801] el0_sync_handler+0xcc/0x154
[ 103.023930] el0_sync+0x198/0x1c0
[ 103.027426]
[ 103.029020] The buggy address belongs to the object at ffff0004d46ff500
[ 103.029020] which belongs to the cache kmalloc-128 of size 128
[ 103.042079] The buggy address is located 73 bytes inside of
[ 103.042079] 128-byte region [ffff0004d46ff500, ffff0004d46ff580)
[ 103.054236] The buggy address belongs to the page:
[ 103.059262] page:0000000021aa849b refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff0004d46fee00 pfn:0x5146fe
[ 103.070437] head:0000000021aa849b order:1 compound_mapcount:0
[ 103.076456] flags: 0x8000000000010200(slab|head|zone=2)
[ 103.081948] raw: 8000000000010200 fffffc0013521a80 0000000d0000000d ffff0004c0002300
[ 103.090052] raw: ffff0004d46fee00 000000008020001e 00000001ffffffff 0000000000000000
[ 103.098150] page dumped because: kasan: bad access detected
[ 103.103985]
[ 103.105578] Memory state around the buggy address:
[ 103.110602] ffff0004d46ff400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.118161] ffff0004d46ff480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.125726] >ffff0004d46ff500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.133284] ^
[ 103.139120] ffff0004d46ff580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 103.146679] ffff0004d46ff600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.154238] ==================================================================
[ 103.161792] Disabling lock debugging due to kernel taint
[ 103.167319] Unable to handle kernel paging request at virtual address 0037801d6000018e
[ 103.175406] Mem abort info:
[ 103.178457] ESR = 0x96000004
[ 103.181609] EC = 0x25: DABT (current EL), IL = 32 bits
[ 103.187020] SET = 0, FnV = 0
[ 103.190185] EA = 0, S1PTW = 0
[ 103.193417] Data abort info:
[ 103.196385] ISV = 0, ISS = 0x00000004
[ 103.200315] CM = 0, WnR = 0
[ 103.203366] [0037801d6000018e] address between user and kernel address ranges
[ 103.210611] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 103.216231] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk sata_rc4
[ 103.259233] CPU: 0 PID: 2997 Comm: umount Tainted: G B 5.13.0-rc4+ #8
[ 103.267031] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 103.273951] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 103.280001] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.285197] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 103.290385] sp : ffff800014777a80
[ 103.293725] x29: ffff800014777a80 x28: ffff0004d7649c80 x27: 0000000000000000
[ 103.300931] x26: ffff800014777fb0 x25: ffff60009aec9394 x24: ffff0004d7649ca4
[ 103.308136] x23: 1fffe0009a3d063a x22: dfff800000000000 x21: ffff0004d1e831d0
[ 103.315340] x20: e1c000eb00000bb4 x19: ffff0004d1e83000 x18: 0000000000000000
[ 103.322545] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 103.329748] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000012ef658
[ 103.336952] x11: ffff7000012ef658 x10: 0720072007200720 x9 : ffff800011322648
[ 103.344157] x8 : ffff800014777818 x7 : ffff80000977b2c7 x6 : 0000000000000000
[ 103.351359] x5 : 0000000000000001 x4 : ffff7000012ef659 x3 : 0000000000000001
[ 103.358562] x2 : 0000000000000000 x1 : 1c38001d6000018e x0 : e1c000eb00000c70
[ 103.365766] Call trace:
[ 103.368235] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 103.373076] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 103.377829] ffs_data_closed+0x1ec/0x244 [usb_f_fs]
[ 103.382755] ffs_fs_kill_sb+0x70/0x84 [usb_f_fs]
[ 103.387420] deactivate_locked_super+0xa0/0xf0
[ 103.391905] deactivate_super+0x98/0xac
[ 103.395776] cleanup_mnt+0xd0/0x1b0
[ 103.399299] __cleanup_mnt+0x1c/0x28
[ 103.402906] task_work_run+0x104/0x180
[ 103.406691] do_notify_resume+0x458/0x14e0
[ 103.410823] work_pending+0xc/0x5f8
[ 103.414351] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 103.420490] ---[ end trace 57b43a50e8244f57 ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
[2]
==================================================================
root@rcar-gen3:~# mkdir /dev/ffs
root@rcar-gen3:~# modprobe libcomposite
root@rcar-gen3:~#
root@rcar-gen3:~# mount -t configfs none /dev/cfs
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1
root@rcar-gen3:~# mkdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 54.766480] file system registered
root@rcar-gen3:~# mount -t functionfs ffs /dev/ffs
root@rcar-gen3:~# rmdir /dev/cfs/usb_gadget/g1/functions/ffs.ffs
[ 63.197597] unloading
root@rcar-gen3:~# cat /dev/ffs/ep0
cat: read error:[ 67.213506] ==================================================================
[ 67.222095] BUG: KASAN: use-after-free in ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.229699] Write of size 1 at addr ffff0004c26e974a by task cat/2994
[ 67.236446]
[ 67.238045] CPU: 0 PID: 2994 Comm: cat Not tainted 5.13.0-rc4+ #8
[ 67.244431] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.251624] Call trace:
[ 67.254212] dump_backtrace+0x0/0x330
[ 67.258081] show_stack+0x20/0x2c
[ 67.261579] dump_stack+0x11c/0x1ac
[ 67.265260] print_address_description.constprop.0+0x30/0x274
[ 67.271286] kasan_report+0x14c/0x1c8
[ 67.275143] __asan_report_store1_noabort+0x34/0x58
[ 67.280265] ffs_data_clear+0x70/0x370 [usb_f_fs]
[ 67.285220] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.290172] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.295305] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.300256] __fput+0x304/0x580
[ 67.303576] ____fput+0x18/0x24
[ 67.306893] task_work_run+0x104/0x180
[ 67.310846] do_notify_resume+0x458/0x14e0
[ 67.315154] work_pending+0xc/0x5f8
[ 67.318834]
[ 67.320429] Allocated by task 2988:
[ 67.324105] kasan_save_stack+0x28/0x58
[ 67.328144] kasan_set_track+0x28/0x3c
[ 67.332090] ____kasan_kmalloc+0x84/0x9c
[ 67.336217] __kasan_kmalloc+0x10/0x1c
[ 67.340163] __kmalloc+0x214/0x2f8
[ 67.343754] kzalloc.constprop.0+0x14/0x20 [usb_f_fs]
[ 67.349066] ffs_alloc_inst+0x8c/0x208 [usb_f_fs]
[ 67.354017] try_get_usb_function_instance+0xf0/0x164 [libcomposite]
[ 67.360705] usb_get_function_instance+0x64/0x68 [libcomposite]
[ 67.366934] function_make+0x128/0x1ec [libcomposite]
[ 67.372260] configfs_mkdir+0x330/0x590 [configfs]
[ 67.377320] vfs_mkdir+0x12c/0x1bc
[ 67.380911] do_mkdirat+0x180/0x1d0
[ 67.384589] __arm64_sys_mkdirat+0x80/0x94
[ 67.388899] invoke_syscall+0xf8/0x25c
[ 67.392850] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.397969] do_el0_svc+0xa0/0xd4
[ 67.401464] el0_svc+0x24/0x34
[ 67.404691] el0_sync_handler+0xcc/0x154
[ 67.408819] el0_sync+0x198/0x1c0
[ 67.412315]
[ 67.413909] Freed by task 2993:
[ 67.417220] kasan_save_stack+0x28/0x58
[ 67.421257] kasan_set_track+0x28/0x3c
[ 67.425204] kasan_set_free_info+0x28/0x4c
[ 67.429513] ____kasan_slab_free+0x104/0x118
[ 67.434001] __kasan_slab_free+0x18/0x24
[ 67.438128] slab_free_freelist_hook+0x148/0x1f0
[ 67.442978] kfree+0x318/0x440
[ 67.446205] ffs_free_inst+0x164/0x2d8 [usb_f_fs]
[ 67.451156] usb_put_function_instance+0x84/0xa4 [libcomposite]
[ 67.457385] ffs_attr_release+0x18/0x24 [usb_f_fs]
[ 67.462428] config_item_put+0x140/0x1a4 [configfs]
[ 67.467570] configfs_rmdir+0x3fc/0x518 [configfs]
[ 67.472626] vfs_rmdir+0x114/0x234
[ 67.476215] do_rmdir+0x274/0x2b0
[ 67.479710] __arm64_sys_unlinkat+0x94/0xc8
[ 67.484108] invoke_syscall+0xf8/0x25c
[ 67.488055] el0_svc_common.constprop.0+0x150/0x1a0
[ 67.493175] do_el0_svc+0xa0/0xd4
[ 67.496671] el0_svc+0x24/0x34
[ 67.499896] el0_sync_handler+0xcc/0x154
[ 67.504024] el0_sync+0x198/0x1c0
[ 67.507520]
[ 67.509114] The buggy address belongs to the object at ffff0004c26e9700
[ 67.509114] which belongs to the cache kmalloc-128 of size 128
[ 67.522171] The buggy address is located 74 bytes inside of
[ 67.522171] 128-byte region [ffff0004c26e9700, ffff0004c26e9780)
[ 67.534328] The buggy address belongs to the page:
[ 67.539355] page:000000003177a217 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5026e8
[ 67.549175] head:000000003177a217 order:1 compound_mapcount:0
[ 67.555195] flags: 0x8000000000010200(slab|head|zone=2)
[ 67.560687] raw: 8000000000010200 fffffc0013037100 0000000c00000002 ffff0004c0002300
[ 67.568791] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[ 67.576890] page dumped because: kasan: bad access detected
[ 67.582725]
[ 67.584318] Memory state around the buggy address:
[ 67.589343] ffff0004c26e9600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.596903] ffff0004c26e9680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.604463] >ffff0004c26e9700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 67.612022] ^
[ 67.617860] ffff0004c26e9780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 67.625421] ffff0004c26e9800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 67.632981] ==================================================================
[ 67.640535] Disabling lock debugging due to kernel taint
File descriptor[ 67.646100] Unable to handle kernel paging request at virtual address fabb801d4000018d
in bad state
[ 67.655456] Mem abort info:
[ 67.659619] ESR = 0x96000004
[ 67.662801] EC = 0x25: DABT (current EL), IL = 32 bits
[ 67.668225] SET = 0, FnV = 0
[ 67.671375] EA = 0, S1PTW = 0
[ 67.674613] Data abort info:
[ 67.677587] ISV = 0, ISS = 0x00000004
[ 67.681522] CM = 0, WnR = 0
[ 67.684588] [fabb801d4000018d] address between user and kernel address ranges
[ 67.691849] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 67.697470] Modules linked in: usb_f_fs libcomposite configfs ath9k_htc led_class mac80211 libarc4 ath9k_common ath9k_hw ath cfg80211 aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce gf128mul sha2_ce sha1_ce evdev sata_rcar libata xhci_plat_hcd scsi_mod xhci_hcd rene4
[ 67.740467] CPU: 0 PID: 2994 Comm: cat Tainted: G B 5.13.0-rc4+ #8
[ 67.748005] Hardware name: Renesas Salvator-X board based on r8a77951 (DT)
[ 67.754924] pstate: 00000005 (nzcv daif -PAN -UAO -TCO BTYPE=--)
[ 67.760974] pc : ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.766178] lr : ffs_data_clear+0x124/0x370 [usb_f_fs]
[ 67.771365] sp : ffff800014767ad0
[ 67.774706] x29: ffff800014767ad0 x28: ffff800009cf91c0 x27: ffff0004c54861a0
[ 67.781913] x26: ffff0004dc90b288 x25: 1fffe00099ec10f5 x24: 00000000000a801d
[ 67.789118] x23: 1fffe00099f6953a x22: dfff800000000000 x21: ffff0004cfb4a9d0
[ 67.796322] x20: d5e000ea00000bb1 x19: ffff0004cfb4a800 x18: 0000000000000000
[ 67.803526] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 67.810730] x14: 0720072007200720 x13: 0720072007200720 x12: 1ffff000028ecefa
[ 67.817934] x11: ffff7000028ecefa x10: 0720072007200720 x9 : ffff80001132c014
[ 67.825137] x8 : ffff8000147677d8 x7 : ffff8000147677d7 x6 : 0000000000000000
[ 67.832341] x5 : 0000000000000001 x4 : ffff7000028ecefb x3 : 0000000000000001
[ 67.839544] x2 : 0000000000000005 x1 : 1abc001d4000018d x0 : d5e000ea00000c6d
[ 67.846748] Call trace:
[ 67.849218] ffs_data_clear+0x138/0x370 [usb_f_fs]
[ 67.854058] ffs_data_reset+0x20/0x304 [usb_f_fs]
[ 67.858810] ffs_data_closed+0x240/0x244 [usb_f_fs]
[ 67.863736] ffs_ep0_release+0x40/0x54 [usb_f_fs]
[ 67.868488] __fput+0x304/0x580
[ 67.871665] ____fput+0x18/0x24
[ 67.874837] task_work_run+0x104/0x180
[ 67.878622] do_notify_resume+0x458/0x14e0
[ 67.882754] work_pending+0xc/0x5f8
[ 67.886282] Code: b4000a54 9102f280 12000802 d343fc01 (38f66821)
[ 67.892422] ---[ end trace 6d7cedf53d7abbea ]---
Segmentation fault
root@rcar-gen3:~#
==================================================================
Fixes: 4b187fceec3c ("usb: gadget: FunctionFS: add devices management code")
Fixes: 3262ad824307 ("usb: gadget: f_fs: Stop ffs_closed NULL pointer dereference")
Fixes: cdafb6d8b8da ("usb: gadget: f_fs: Fix use-after-free in ffs_free_inst")
Reported-by: Bhuvanesh Surachari <bhuvanesh_surachari@mentor.com>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Reviewed-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Link: https://lore.kernel.org/r/20210603171507.22514-1-andrew_gabbasov@mentor.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-06-03 12:15:07 -05:00
|
|
|
|
if (ffs_dev->ffs_data) {
|
|
|
|
|
ffs_dev->ffs_data->private_data = NULL;
|
|
|
|
|
ffs_dev->ffs_data = NULL;
|
|
|
|
|
}
|
2014-01-13 16:49:35 +01:00
|
|
|
|
|
|
|
|
|
if (ffs_dev->ffs_release_dev_callback)
|
|
|
|
|
ffs_dev->ffs_release_dev_callback(ffs_dev);
|
|
|
|
|
}
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ffs_ready(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *ffs_obj;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
|
|
|
|
|
ffs_obj = ffs->private_data;
|
|
|
|
|
if (!ffs_obj) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
if (WARN_ON(ffs_obj->desc_ready)) {
|
|
|
|
|
ret = -EBUSY;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffs_obj->desc_ready = true;
|
|
|
|
|
|
2015-05-22 17:25:18 +02:00
|
|
|
|
if (ffs_obj->ffs_ready_callback) {
|
2013-12-03 15:15:32 +01:00
|
|
|
|
ret = ffs_obj->ffs_ready_callback(ffs);
|
2015-05-22 17:25:18 +02:00
|
|
|
|
if (ret)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
2015-05-22 17:25:18 +02:00
|
|
|
|
set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
|
2013-12-03 15:15:32 +01:00
|
|
|
|
done:
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ffs_closed(struct ffs_data *ffs)
|
|
|
|
|
{
|
|
|
|
|
struct ffs_dev *ffs_obj;
|
2015-05-20 14:52:40 +01:00
|
|
|
|
struct f_fs_opts *opts;
|
2016-12-08 19:55:22 +08:00
|
|
|
|
struct config_item *ci;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
|
|
|
|
|
ffs_dev_lock();
|
|
|
|
|
|
|
|
|
|
ffs_obj = ffs->private_data;
|
|
|
|
|
if (!ffs_obj)
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
ffs_obj->desc_ready = false;
|
|
|
|
|
|
2015-05-22 17:25:18 +02:00
|
|
|
|
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
|
|
|
|
|
ffs_obj->ffs_closed_callback)
|
2013-12-03 15:15:32 +01:00
|
|
|
|
ffs_obj->ffs_closed_callback(ffs);
|
2013-12-03 15:15:36 +01:00
|
|
|
|
|
2015-05-20 14:52:40 +01:00
|
|
|
|
if (ffs_obj->opts)
|
|
|
|
|
opts = ffs_obj->opts;
|
|
|
|
|
else
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
|
2016-11-14 17:29:48 +01:00
|
|
|
|
|| !kref_read(&opts->func_inst.group.cg_item.ci_kref))
|
2013-12-03 15:15:36 +01:00
|
|
|
|
goto done;
|
|
|
|
|
|
2016-12-08 19:55:22 +08:00
|
|
|
|
ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
|
2018-01-09 12:30:53 +05:30
|
|
|
|
if (test_bit(FFS_FL_BOUND, &ffs->flags))
|
|
|
|
|
unregister_gadget_item(ci);
|
2016-12-08 19:55:22 +08:00
|
|
|
|
return;
|
2013-12-03 15:15:32 +01:00
|
|
|
|
done:
|
|
|
|
|
ffs_dev_unlock();
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:53:14 +02:00
|
|
|
|
/* Misc helper functions ****************************************************/
|
|
|
|
|
|
|
|
|
|
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
|
|
|
|
|
{
|
|
|
|
|
return nonblock
|
2020-11-27 15:05:59 +01:00
|
|
|
|
? mutex_trylock(mutex) ? 0 : -EAGAIN
|
2010-05-05 12:53:14 +02:00
|
|
|
|
: mutex_lock_interruptible(mutex);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-26 21:43:45 -04:00
|
|
|
|
static char *ffs_prepare_buffer(const char __user *buf, size_t len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
{
|
|
|
|
|
char *data;
|
|
|
|
|
|
2020-11-27 15:05:59 +01:00
|
|
|
|
if (!len)
|
2010-05-05 12:53:14 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
|
2021-03-08 15:09:51 +08:00
|
|
|
|
data = memdup_user(buf, len);
|
|
|
|
|
if (IS_ERR(data))
|
2021-07-31 18:18:38 +01:00
|
|
|
|
return data;
|
2010-05-05 12:53:14 +02:00
|
|
|
|
|
2010-11-17 17:09:47 +01:00
|
|
|
|
pr_vdebug("Buffer from user space:\n");
|
2010-05-05 12:53:14 +02:00
|
|
|
|
ffs_dump_mem("", data, len);
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
2013-12-03 15:15:33 +01:00
|
|
|
|
|
|
|
|
|
DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
|
2024-06-05 20:57:08 -07:00
|
|
|
|
MODULE_DESCRIPTION("user mode file system API for USB composite function controllers");
|
2013-12-03 15:15:33 +01:00
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
MODULE_AUTHOR("Michal Nazarewicz");
|