Commit f81404c8 authored by Junxian Huang's avatar Junxian Huang Committed by Xinghai Cen
Browse files

RDMA/hns: Fix delay-destruction mechanism not processing kernel db

driver inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBQK42



----------------------------------------------------------------------

Kernel dbs are not processed by delayed-destruction mechanism.
This may lead to HW UAF described in the fixes commit. Expand
the hns_roce_umem_node to hns_roce_db_pg_node with kernel db
information. This struct is now used by both userspace and
kernel db pages.

Fixes: 431c875e ("RDMA/hns: Fix simultaneous reset and resource deregistration")
Signed-off-by: default avatarJunxian Huang <huangjunxian6@hisilicon.com>
parent ee4dd6d5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -339,7 +339,8 @@ static void free_cq_db(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
		hns_roce_db_unmap_user(uctx, &hr_cq->db,
				       hr_cq->delayed_destroy_flag);
	} else {
		hns_roce_free_db(hr_dev, &hr_cq->db);
		hns_roce_free_db(hr_dev, &hr_cq->db,
				 hr_cq->delayed_destroy_flag);
	}
}

+55 −30
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
{
	unsigned long page_addr = virt & PAGE_MASK;
	struct hns_roce_user_db_page *page;
	struct ib_umem *umem;
	unsigned int offset;
	int ret = 0;

@@ -29,31 +30,32 @@ int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,

	refcount_set(&page->refcount, 1);
	page->user_virt = page_addr;
	page->umem = ib_umem_get(context->ibucontext.device, page_addr,
				 PAGE_SIZE, 0);
	if (IS_ERR(page->umem)) {
		ret = PTR_ERR(page->umem);
	page->db_node = kvzalloc(sizeof(*page->db_node), GFP_KERNEL);
	if (!page->db_node) {
		ret = -ENOMEM;
		goto err_page;
	}
	page->umem_node = kvmalloc(sizeof(*page->umem_node), GFP_KERNEL);
	if (!page->umem_node) {
		ret = -ENOMEM;
		goto err_umem;

	umem = ib_umem_get(context->ibucontext.device, page_addr, PAGE_SIZE, 0);
	if (IS_ERR(umem)) {
		ret = PTR_ERR(umem);
		goto err_dbnode;
	}

	page->db_node->umem = umem;
	list_add(&page->list, &context->page_list);

found:
	offset = virt - page_addr;
	db->dma = sg_dma_address(page->umem->sg_head.sgl) + offset;
	db->virt_addr = sg_virt(page->umem->sg_head.sgl) + offset;
	db->dma = sg_dma_address(page->db_node->umem->sg_head.sgl) + offset;
	db->virt_addr = sg_virt(page->db_node->umem->sg_head.sgl) + offset;
	db->u.user_page = page;
	refcount_inc(&page->refcount);
	mutex_unlock(&context->page_mutex);
	return 0;

err_umem:
	ib_umem_release(page->umem);
err_dbnode:
	kvfree(page->db_node);
err_page:
	kvfree(page);
err_out:
@@ -67,20 +69,20 @@ void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
			    bool delayed_unmap_flag)
{
	struct hns_roce_dev *hr_dev = to_hr_dev(context->ibucontext.device);
	struct hns_roce_umem_node *umem_node = db->u.user_page->umem_node;
	struct hns_roce_db_pg_node *db_node = db->u.user_page->db_node;

	mutex_lock(&context->page_mutex);

	umem_node->delayed_unmap_flag |= delayed_unmap_flag;
	db_node->delayed_unmap_flag |= delayed_unmap_flag;

	refcount_dec(&db->u.user_page->refcount);
	if (refcount_dec_if_one(&db->u.user_page->refcount)) {
		list_del(&db->u.user_page->list);
		if (umem_node->delayed_unmap_flag) {
			hns_roce_add_unfree_umem(db->u.user_page, hr_dev);
		if (db_node->delayed_unmap_flag) {
			hns_roce_add_unfree_db(db_node, hr_dev);
		} else {
			ib_umem_release(db->u.user_page->umem);
			kvfree(umem_node);
			ib_umem_release(db_node->umem);
			kvfree(db_node);
		}
		kfree(db->u.user_page);
	}
@@ -92,6 +94,8 @@ static struct hns_roce_db_pgdir *hns_roce_alloc_db_pgdir(
					struct device *dma_device)
{
	struct hns_roce_db_pgdir *pgdir;
	dma_addr_t db_dma;
	u32 *page;

	pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);
	if (!pgdir)
@@ -101,14 +105,25 @@ static struct hns_roce_db_pgdir *hns_roce_alloc_db_pgdir(
			HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT);
	pgdir->bits[0] = pgdir->order0;
	pgdir->bits[1] = pgdir->order1;
	pgdir->page = dma_alloc_coherent(dma_device, PAGE_SIZE,
					 &pgdir->db_dma, GFP_KERNEL);
	if (!pgdir->page) {
	pgdir->db_node = kvzalloc(sizeof(*pgdir->db_node), GFP_KERNEL);
	if (!pgdir->db_node)
		goto err_node;

	page = dma_alloc_coherent(dma_device, PAGE_SIZE, &db_dma, GFP_KERNEL);
	if (!page)
		goto err_dma;

	pgdir->db_node->kdb.page = page;
	pgdir->db_node->kdb.db_dma = db_dma;

	return pgdir;

err_dma:
	kvfree(pgdir->db_node);
err_node:
	kfree(pgdir);
	return NULL;
	}

	return pgdir;
}

static int hns_roce_alloc_db_from_pgdir(struct hns_roce_db_pgdir *pgdir,
@@ -135,8 +150,8 @@ static int hns_roce_alloc_db_from_pgdir(struct hns_roce_db_pgdir *pgdir,

	db->u.pgdir	= pgdir;
	db->index	= i;
	db->db_record	= pgdir->page + db->index;
	db->dma		= pgdir->db_dma  + db->index * HNS_ROCE_DB_UNIT_SIZE;
	db->db_record	= pgdir->db_node->kdb.page + db->index;
	db->dma	= pgdir->db_node->kdb.db_dma + db->index * HNS_ROCE_DB_UNIT_SIZE;
	db->order	= order;

	return 0;
@@ -171,13 +186,17 @@ int hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
	return ret;
}

void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db)
void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
		      bool delayed_unmap_flag)
{
	struct hns_roce_db_pg_node *db_node = db->u.pgdir->db_node;
	unsigned long o;
	unsigned long i;

	mutex_lock(&hr_dev->pgdir_mutex);

	db_node->delayed_unmap_flag |= delayed_unmap_flag;

	o = db->order;
	i = db->index;

@@ -191,9 +210,15 @@ void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db)

	if (bitmap_full(db->u.pgdir->order1,
			HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT)) {
		dma_free_coherent(hr_dev->dev, PAGE_SIZE, db->u.pgdir->page,
				  db->u.pgdir->db_dma);
		list_del(&db->u.pgdir->list);
		if (db_node->delayed_unmap_flag) {
			hns_roce_add_unfree_db(db_node, hr_dev);
		} else {
			dma_free_coherent(hr_dev->dev, PAGE_SIZE,
					  db_node->kdb.page,
							  db_node->kdb.db_dma);
			kvfree(db_node);
		}
		kfree(db->u.pgdir);
	}

+19 −16
Original line number Diff line number Diff line
@@ -488,27 +488,29 @@ struct hns_roce_buf {
	unsigned int			page_shift;
};

struct hns_roce_db_pgdir {
struct hns_roce_db_pg_node {
	struct list_head list;
	DECLARE_BITMAP(order0, HNS_ROCE_DB_PER_PAGE);
	DECLARE_BITMAP(order1, HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT);
	unsigned long		*bits[HNS_ROCE_DB_TYPE_COUNT];
	struct ib_umem *umem;
	struct {
		u32 *page;
		dma_addr_t db_dma;
	} kdb;
	bool delayed_unmap_flag;
};

struct hns_roce_umem_node {
	struct ib_umem *umem;
struct hns_roce_db_pgdir {
	struct list_head	list;
	bool delayed_unmap_flag;
	DECLARE_BITMAP(order0, HNS_ROCE_DB_PER_PAGE);
	DECLARE_BITMAP(order1, HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT);
	unsigned long		*bits[HNS_ROCE_DB_TYPE_COUNT];
	struct hns_roce_db_pg_node *db_node;
};

struct hns_roce_user_db_page {
	struct list_head	list;
	struct ib_umem		*umem;
	unsigned long		user_virt;
	refcount_t		refcount;
	struct hns_roce_umem_node *umem_node;
	struct hns_roce_db_pg_node *db_node;
};

struct hns_roce_db {
@@ -1225,8 +1227,8 @@ struct hns_roce_dev {
	size_t notify_num;
	struct list_head mtr_unfree_list; /* list of unfree mtr on this dev */
	struct mutex mtr_unfree_list_mutex; /* protect mtr_unfree_list */
	struct list_head umem_unfree_list; /* list of unfree umem on this dev */
	struct mutex umem_unfree_list_mutex; /* protect umem_unfree_list */
	struct list_head db_unfree_list; /* list of unfree db on this dev */
	struct mutex db_unfree_list_mutex; /* protect db_unfree_list */

	void *dca_safe_buf;
	dma_addr_t dca_safe_page;
@@ -1520,7 +1522,8 @@ void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
			    bool delayed_unmap_flag);
int hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
		      int order);
void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db);
void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
		      bool delayed_unmap_flag);

void hns_roce_cq_completion(struct hns_roce_dev *hr_dev, u32 cqn);
void hns_roce_cq_event(struct hns_roce_dev *hr_dev, u32 cqn, int event_type);
@@ -1538,9 +1541,9 @@ int hns_roce_fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ib_qp);
int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp);
int hns_roce_fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ib_mr);
int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr);
void hns_roce_add_unfree_umem(struct hns_roce_user_db_page *user_page,
void hns_roce_add_unfree_db(struct hns_roce_db_pg_node *db_node,
			    struct hns_roce_dev *hr_dev);
void hns_roce_free_unfree_umem(struct hns_roce_dev *hr_dev);
void hns_roce_free_unfree_db(struct hns_roce_dev *hr_dev);
void hns_roce_add_unfree_mtr(struct hns_roce_dev *hr_dev,
			     struct hns_roce_mtr *mtr);
void hns_roce_free_unfree_mtr(struct hns_roce_dev *hr_dev);
+5 −5
Original line number Diff line number Diff line
@@ -1296,7 +1296,7 @@ static void hns_roce_teardown_hca(struct hns_roce_dev *hr_dev)
		hns_roce_cleanup_dca(hr_dev);

	hns_roce_cleanup_bitmap(hr_dev);
	mutex_destroy(&hr_dev->umem_unfree_list_mutex);
	mutex_destroy(&hr_dev->db_unfree_list_mutex);
	mutex_destroy(&hr_dev->mtr_unfree_list_mutex);
	mutex_destroy(&hr_dev->uctx_list_mutex);

@@ -1326,8 +1326,8 @@ static int hns_roce_setup_hca(struct hns_roce_dev *hr_dev)
	INIT_LIST_HEAD(&hr_dev->mtr_unfree_list);
	mutex_init(&hr_dev->mtr_unfree_list_mutex);

	INIT_LIST_HEAD(&hr_dev->umem_unfree_list);
	mutex_init(&hr_dev->umem_unfree_list_mutex);
	INIT_LIST_HEAD(&hr_dev->db_unfree_list);
	mutex_init(&hr_dev->db_unfree_list_mutex);

	if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_CQ_RECORD_DB ||
	    hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_RECORD_DB) {
@@ -1369,7 +1369,7 @@ static int hns_roce_setup_hca(struct hns_roce_dev *hr_dev)

err_uar_table_free:
	ida_destroy(&hr_dev->uar_ida.ida);
	mutex_destroy(&hr_dev->umem_unfree_list_mutex);
	mutex_destroy(&hr_dev->db_unfree_list_mutex);
	mutex_destroy(&hr_dev->mtr_unfree_list_mutex);
	mutex_destroy(&hr_dev->uctx_list_mutex);

@@ -1594,7 +1594,7 @@ void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup)

	if (hr_dev->hw->hw_exit)
		hr_dev->hw->hw_exit(hr_dev);
	hns_roce_free_unfree_umem(hr_dev);
	hns_roce_free_unfree_db(hr_dev);
	hns_roce_free_unfree_mtr(hr_dev);
	hns_roce_teardown_hca(hr_dev);
	hns_roce_cleanup_hem(hr_dev);
+15 −15
Original line number Diff line number Diff line
@@ -1289,27 +1289,27 @@ void hns_roce_free_unfree_mtr(struct hns_roce_dev *hr_dev)
	mutex_unlock(&hr_dev->mtr_unfree_list_mutex);
}

void hns_roce_add_unfree_umem(struct hns_roce_user_db_page *user_page,
void hns_roce_add_unfree_db(struct hns_roce_db_pg_node *db_node,
			    struct hns_roce_dev *hr_dev)
{
	struct hns_roce_umem_node *pos = user_page->umem_node;

	pos->umem = user_page->umem;

	mutex_lock(&hr_dev->umem_unfree_list_mutex);
	list_add_tail(&pos->list, &hr_dev->umem_unfree_list);
	mutex_unlock(&hr_dev->umem_unfree_list_mutex);
	mutex_lock(&hr_dev->db_unfree_list_mutex);
	list_add_tail(&db_node->list, &hr_dev->db_unfree_list);
	mutex_unlock(&hr_dev->db_unfree_list_mutex);
}

void hns_roce_free_unfree_umem(struct hns_roce_dev *hr_dev)
void hns_roce_free_unfree_db(struct hns_roce_dev *hr_dev)
{
	struct hns_roce_umem_node *pos, *next;
	struct hns_roce_db_pg_node *pos, *next;

	mutex_lock(&hr_dev->umem_unfree_list_mutex);
	list_for_each_entry_safe(pos, next, &hr_dev->umem_unfree_list, list) {
	mutex_lock(&hr_dev->db_unfree_list_mutex);
	list_for_each_entry_safe(pos, next, &hr_dev->db_unfree_list, list) {
		list_del(&pos->list);
		if (pos->umem)
			ib_umem_release(pos->umem);
		else
			dma_free_coherent(hr_dev->dev, PAGE_SIZE,
					  pos->kdb.page, pos->kdb.db_dma);
		kvfree(pos);
	}
	mutex_unlock(&hr_dev->umem_unfree_list_mutex);
	mutex_unlock(&hr_dev->db_unfree_list_mutex);
}
Loading