commit dfd0743f1d9ea76931510ed150334d571fbab49d upstream.
Since the tee subsystem does not keep a strong reference to its idle
shared memory buffers, it races with other threads that try to destroy a
shared memory through a close of its dma-buf fd or by unmapping the
memory.
In tee_shm_get_from_id() when a lookup in teedev->idr has been
successful, it is possible that the tee_shm is in the dma-buf teardown
path, but that path is blocked by the teedev mutex. Since we don't have
an API to tell if the tee_shm is in the dma-buf teardown path or not we
must find another way of detecting this condition.
Fix this by doing the reference counting directly on the tee_shm using a
new refcount_t refcount field. dma-buf is replaced by using
anon_inode_getfd() instead, this separates the life-cycle of the
underlying file from the tee_shm. tee_shm_put() is updated to hold the
mutex when decreasing the refcount to 0 and then remove the tee_shm from
teedev->idr before releasing the mutex. This means that the tee_shm can
never be found unless it has a refcount larger than 0.
Fixes: 967c9cca2c ("tee: generic TEE subsystem")
Cc: stable@vger.kernel.org
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Lars Persson <larper@axis.com>
Reviewed-by: Sumit Garg <sumit.garg@linaro.org>
Reported-by: Patrik Lantz <patrik.lantz@axis.com>
[JW: backported to 4.14-stable]
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
130 lines
3.5 KiB
C
130 lines
3.5 KiB
C
/*
|
|
* Copyright (c) 2015-2016, Linaro Limited
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#ifndef TEE_PRIVATE_H
|
|
#define TEE_PRIVATE_H
|
|
|
|
#include <linux/cdev.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kref.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/types.h>
|
|
|
|
struct tee_device;
|
|
|
|
/**
|
|
* struct tee_shm - shared memory object
|
|
* @teedev: device used to allocate the object
|
|
* @ctx: context using the object, if NULL the context is gone
|
|
* @link link element
|
|
* @paddr: physical address of the shared memory
|
|
* @kaddr: virtual address of the shared memory
|
|
* @size: size of shared memory
|
|
* @refcount: reference counter
|
|
* @flags: defined by TEE_SHM_* in tee_drv.h
|
|
* @id: unique id of a shared memory object on this device
|
|
*/
|
|
struct tee_shm {
|
|
struct tee_device *teedev;
|
|
struct tee_context *ctx;
|
|
struct list_head link;
|
|
phys_addr_t paddr;
|
|
void *kaddr;
|
|
size_t size;
|
|
refcount_t refcount;
|
|
u32 flags;
|
|
int id;
|
|
};
|
|
|
|
struct tee_shm_pool_mgr;
|
|
|
|
/**
|
|
* struct tee_shm_pool_mgr_ops - shared memory pool manager operations
|
|
* @alloc: called when allocating shared memory
|
|
* @free: called when freeing shared memory
|
|
*/
|
|
struct tee_shm_pool_mgr_ops {
|
|
int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm,
|
|
size_t size);
|
|
void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm);
|
|
};
|
|
|
|
/**
|
|
* struct tee_shm_pool_mgr - shared memory manager
|
|
* @ops: operations
|
|
* @private_data: private data for the shared memory manager
|
|
*/
|
|
struct tee_shm_pool_mgr {
|
|
const struct tee_shm_pool_mgr_ops *ops;
|
|
void *private_data;
|
|
};
|
|
|
|
/**
|
|
* struct tee_shm_pool - shared memory pool
|
|
* @private_mgr: pool manager for shared memory only between kernel
|
|
* and secure world
|
|
* @dma_buf_mgr: pool manager for shared memory exported to user space
|
|
* @destroy: called when destroying the pool
|
|
* @private_data: private data for the pool
|
|
*/
|
|
struct tee_shm_pool {
|
|
struct tee_shm_pool_mgr private_mgr;
|
|
struct tee_shm_pool_mgr dma_buf_mgr;
|
|
void (*destroy)(struct tee_shm_pool *pool);
|
|
void *private_data;
|
|
};
|
|
|
|
#define TEE_DEVICE_FLAG_REGISTERED 0x1
|
|
#define TEE_MAX_DEV_NAME_LEN 32
|
|
|
|
/**
|
|
* struct tee_device - TEE Device representation
|
|
* @name: name of device
|
|
* @desc: description of device
|
|
* @id: unique id of device
|
|
* @flags: represented by TEE_DEVICE_FLAG_REGISTERED above
|
|
* @dev: embedded basic device structure
|
|
* @cdev: embedded cdev
|
|
* @num_users: number of active users of this device
|
|
* @c_no_user: completion used when unregistering the device
|
|
* @mutex: mutex protecting @num_users and @idr
|
|
* @idr: register of shared memory object allocated on this device
|
|
* @pool: shared memory pool
|
|
*/
|
|
struct tee_device {
|
|
char name[TEE_MAX_DEV_NAME_LEN];
|
|
const struct tee_desc *desc;
|
|
int id;
|
|
unsigned int flags;
|
|
|
|
struct device dev;
|
|
struct cdev cdev;
|
|
|
|
size_t num_users;
|
|
struct completion c_no_users;
|
|
struct mutex mutex; /* protects num_users and idr */
|
|
|
|
struct idr idr;
|
|
struct tee_shm_pool *pool;
|
|
};
|
|
|
|
int tee_shm_init(void);
|
|
|
|
int tee_shm_get_fd(struct tee_shm *shm);
|
|
|
|
bool tee_device_get(struct tee_device *teedev);
|
|
void tee_device_put(struct tee_device *teedev);
|
|
|
|
#endif /*TEE_PRIVATE_H*/
|