Commit 9b3a6da2 authored by zhangshuowen's avatar zhangshuowen Committed by zhangshuowen96
Browse files

driver:misc:sdma-dae: not expose sq_tail write and add validation of cq_head

kunpeng inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IAQL6P


CVE: NA

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

1. there is no need to expose sq_tail write to userspace, delete sq_tail
write api
2. cq_head should between cq_tail(hardware) and cq_head(software)
3. record open sdma device times of every process, clear authority infos
only after all opened file closed

Signed-off-by: default avatarzhangshuowen <zhangshuowen@hisilicon.com>
Fixes: 7d91f398 ("drivers: misc: sdma-dae: support sqe task send and execute")
parent bacc4778
Loading
Loading
Loading
Loading
+117 −10
Original line number Diff line number Diff line
@@ -38,6 +38,74 @@ struct pasid_info {
	u32 dst_pasid;
};

static struct hisi_sdma_pid_ref_hte *sdma_search_pid_ref(struct hisi_sdma_device *psdma_dev,
							 u32 pid)
{
	struct hisi_sdma_pid_ref_hte *entry = NULL;

	hash_for_each_possible(psdma_dev->sdma_pid_ref_ht, entry, node, pid) {
		if (entry->pid == pid)
			return entry;
	}

	return NULL;
}

static int sdma_add_pid_ref(struct hisi_sdma_device *psdma_dev, u32 pid)
{
	struct hisi_sdma_pid_ref_hte *entry = NULL;

	spin_lock(&psdma_dev->pid_lock);
	entry = sdma_search_pid_ref(psdma_dev, pid);
	if (!entry) {
		entry = kmalloc_node(sizeof(struct hisi_sdma_pid_ref_hte), GFP_KERNEL,
				     psdma_dev->node_idx);
		if (!entry) {
			spin_unlock(&psdma_dev->pid_lock);
			return -ENOMEM;
		}
		entry->pid = pid;
		entry->ref = 1;
		hash_add(psdma_dev->sdma_pid_ref_ht, &entry->node, entry->pid);
	} else {
		entry->ref++;
	}
	spin_unlock(&psdma_dev->pid_lock);

	return 0;
}

static void sdma_del_pid_ref(struct hisi_sdma_device *psdma_dev, u32 pid)
{
	struct hisi_sdma_pid_ref_hte *entry = NULL;

	spin_lock(&psdma_dev->pid_lock);
	entry = sdma_search_pid_ref(psdma_dev, pid);
	if (entry) {
		entry->ref--;
		if (entry->ref == 0) {
			hash_del(&entry->node);
			kfree(entry);
			sdma_free_authority_ht_with_pid(pid);
		}
	}
	spin_unlock(&psdma_dev->pid_lock);
}

void sdma_clear_pid_ref(struct hisi_sdma_device *psdma_dev)
{
	struct hisi_sdma_pid_ref_hte *entry = NULL;
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&psdma_dev->pid_lock);
	hash_for_each_safe(psdma_dev->sdma_pid_ref_ht, bkt, tmp, entry, node) {
		hash_del(&entry->node);
		kfree(entry);
	}
	spin_unlock(&psdma_dev->pid_lock);
}

static int __do_sdma_open(struct hisi_sdma_device *psdma_dev, struct file *file)
{
	struct file_open_data *data;
@@ -49,11 +117,17 @@ static int __do_sdma_open(struct hisi_sdma_device *psdma_dev, struct file *file)
	if (id < 0)
		return id;

	ret = sdma_add_pid_ref(psdma_dev, (u32)current->tgid);
	if (ret != 0) {
		dev_err(&psdma_dev->pdev->dev, "alloc pid_ref hash failed\n");
		goto free_ida;
	}

	dev_dbg(&psdma_dev->pdev->dev, "%s: ida alloc id = %d\n", __func__, id);
	data = kmalloc_node(sizeof(struct file_open_data), GFP_KERNEL, psdma_dev->node_idx);
	if (!data) {
		ret = -ENOMEM;
		goto free_ida;
		goto free_pid_ref_ht;
	}

	handle = iommu_sva_bind_device(&psdma_dev->pdev->dev, current->mm, NULL);
@@ -84,6 +158,8 @@ static int __do_sdma_open(struct hisi_sdma_device *psdma_dev, struct file *file)
	iommu_sva_unbind_device(handle);
free_privt_data:
	kfree(data);
free_pid_ref_ht:
	sdma_del_pid_ref(psdma_dev, current->tgid);
free_ida:
	ida_free(g_info.fd_ida, id);
	return ret;
@@ -176,7 +252,8 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	u32 alloc_chn_num_max, idx;
	int ret;

	list_node = kmalloc_node(sizeof(struct hisi_sdma_channel_list), GFP_KERNEL, pdev->node_idx);
	list_node = kmalloc_node(sizeof(struct hisi_sdma_channel_list), GFP_KERNEL,
				 pdev->node_idx);
	if (!list_node)
		return -ENOMEM;

@@ -511,7 +588,8 @@ static bool sdma_check_channel_permission(struct hisi_sdma_channel *pchannel, u3
	return true;
}

static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_task_info *task_info,
static int sdma_send_task_kernel(struct file_open_data *data,
				 struct hisi_sdma_task_info *task_info,
				 struct hisi_sdma_sqe_task *task_list)
{
	struct hisi_sdma_device *pdev = data->psdma_dev;
@@ -529,6 +607,10 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
		return -EPERM;
	}
	sq_tail = pchannel->sync_info_base->sq_tail;
	if (sq_tail >= HISI_SDMA_SQ_LENGTH) {
		dev_err(&pdev->pdev->dev, "sq_tail in share mem wrong, sq_tail = %u\n", sq_tail);
		return -EINVAL;
	}
	for (i = 0; i < task_info->task_cnt; i++) {
		if (task_info->req_cnt != 0) {
			/* not send/record tasks whose length == 0 */
@@ -635,6 +717,25 @@ static int ioctl_sdma_send_task(struct file *file, unsigned long arg)
	return ret;
}

/* register value should be between cq_head(software update) and cq_tail(hardware updated) */
static bool sdma_cq_head_validate(struct hisi_sdma_channel *pchan, u32 reg_value)
{
	u32 cq_tail;
	u32 cq_head;

	cq_head = sdma_channel_get_cq_head(pchan);
	cq_tail = sdma_channel_get_cq_tail(pchan);
	if (cq_tail > cq_head) {
		if (reg_value <= cq_tail && reg_value >= cq_head)
			return true;
	} else {
		if (reg_value <= cq_tail || reg_value >= cq_head)
			return true;
	}

	return false;
}

static int sdma_operation_reg(struct hisi_sdma_device *pdev, unsigned long arg,
			      u32 (*get_func)(struct hisi_sdma_channel *),
			      void (*set_func)(struct hisi_sdma_channel *, u32))
@@ -660,10 +761,16 @@ static int sdma_operation_reg(struct hisi_sdma_device *pdev, unsigned long arg,
				 sizeof(struct hisi_sdma_reg_info)))
			return -EFAULT;
	} else if (reg_info.type == HISI_SDMA_WRITE_REG) {
		if (!set_func)
			dev_err(dev, "write operation not supported\n");
		else
		if (set_func) {
			if (reg_info.reg_value == sdma_channel_get_cq_head(pchannel))
				return 0;
			if (sdma_cq_head_validate(pchannel, reg_info.reg_value)) {
				set_func(pchannel, reg_info.reg_value);
			} else {
				dev_err(dev, "cq_head value illegal!\n");
				return -EINVAL;
			}
		}
	}

	return 0;
@@ -682,7 +789,7 @@ static int ioctl_sdma_sq_tail_reg(struct file *file, unsigned long arg)
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;

	return sdma_operation_reg(pdev, arg, sdma_channel_get_sq_tail, sdma_channel_set_sq_tail);
	return sdma_operation_reg(pdev, arg, sdma_channel_get_sq_tail, NULL);
}

static int ioctl_sdma_cq_head_reg(struct file *file, unsigned long arg)
@@ -886,7 +993,7 @@ static int sdma_dev_release(struct inode *inode SDMA_UNUSED, struct file *file)
		iommu_sva_unbind_device(data->handle);

	sdma_hash_free_entry(data->ida);
	sdma_free_authority_ht_with_pid(pid);
	sdma_del_pid_ref(pdev, pid);
	ida_free(g_info.fd_ida, data->ida);

	kfree(file->private_data);
+10 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#define RW_R_R			0644
#define SDMA_IRQ_NUM_MAX	512
#define ALIGN_NUM		1
#define HISI_SDMA_HAL_HASH_BUCKETS_BITS 8

/**
 * struct hisi_sdma_channel - Information about one channel in the SDMA device
@@ -41,6 +42,12 @@ struct hisi_sdma_channel {
	u16 cnt_used;
};

struct hisi_sdma_pid_ref_hte {
	u32 pid;
	u32 ref;
	struct hlist_node node;
};

/**
 * struct hisi_sdma_device - SDMA device structure
 * @idx: SDMA device's ID
@@ -77,6 +84,8 @@ struct hisi_sdma_device {

	int irq_cnt;
	int irq[SDMA_IRQ_NUM_MAX];
	DECLARE_HASHTABLE(sdma_pid_ref_ht, HISI_SDMA_HAL_HASH_BUCKETS_BITS);
	spinlock_t pid_lock;
};

struct hisi_sdma_core_device {
@@ -92,6 +101,7 @@ struct hisi_sdma_global_info {
	struct ida *fd_ida;
};

void sdma_clear_pid_ref(struct hisi_sdma_device *psdma_dev);
int sdma_create_dbg_node(struct dentry *sdma_dbgfs_dir);
void sdma_cdev_init(struct cdev *cdev);
void sdma_info_sync_cdev(struct hisi_sdma_core_device *p, u32 *share_chns, struct ida *fd_ida);
+4 −0
Original line number Diff line number Diff line
@@ -420,6 +420,8 @@ static int sdma_device_probe(struct platform_device *pdev)

	psdma_dev->streamid = pdev->dev.iommu->fwspec->ids[0];
	spin_lock_init(&psdma_dev->channel_lock);
	hash_init(psdma_dev->sdma_pid_ref_ht);
	spin_lock_init(&psdma_dev->pid_lock);

	ret = sdma_device_add(psdma_dev);
	if (ret)
@@ -430,6 +432,7 @@ static int sdma_device_probe(struct platform_device *pdev)
	return 0;

sva_device_shutdown:
	sdma_clear_pid_ref(psdma_dev);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
deinit_device:
@@ -445,6 +448,7 @@ static int sdma_device_remove(struct platform_device *pdev)
	struct hisi_sdma_device *psdma_dev = dev_get_drvdata(&pdev->dev);

	sdma_device_delete(psdma_dev);
	sdma_clear_pid_ref(psdma_dev);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
	sdma_deinit_device_info(psdma_dev);