Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fc1d475 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: pil: Fix use-after-free bug when loading firmware images"

parents 40484060 dea78350
Loading
Loading
Loading
Loading
+36 −30
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ static int proxy_timeout_ms = -1;
module_param(proxy_timeout_ms, int, 0644);

static bool disable_timeouts;

static struct workqueue_struct *pil_wq;

/**
 * struct pil_mdt - Representation of <name>.mdt file in memory
 * @hdr: ELF32 header
@@ -886,20 +889,19 @@ struct pil_seg_data {
	int seg_id;
	struct pil_desc *desc;
	struct pil_seg *seg;
	struct completion load_done;
	struct work_struct load_seg_work;
	int retval;
};

static int pil_load_seg_thread_fn(void *data)
static void pil_load_seg_work_fn(struct work_struct *work)
{
	struct pil_seg_data *pil_seg_data = data;
	struct pil_seg_data *pil_seg_data = container_of(work,
							struct pil_seg_data,
							load_seg_work);
	struct pil_desc *desc = pil_seg_data->desc;
	struct pil_seg *seg = pil_seg_data->seg;

	pil_seg_data->retval = pil_load_seg(desc, seg);
	complete(&pil_seg_data->load_done);

	return 0;
}

static int pil_load_segs(struct pil_desc *desc)
@@ -908,7 +910,6 @@ static int pil_load_segs(struct pil_desc *desc)
	struct pil_priv *priv = desc->priv;
	struct pil_seg_data *pil_seg_data;
	struct pil_seg *seg;
	struct task_struct *task_load_seg;
	unsigned long *err_map;

	err_map = kcalloc(priv->num_segs, sizeof(*err_map), GFP_KERNEL);
@@ -923,23 +924,10 @@ static int pil_load_segs(struct pil_desc *desc)
		pil_seg_data[seg_id].seg_id = seg_id;
		pil_seg_data[seg_id].desc = desc;
		pil_seg_data[seg_id].seg = seg;
		init_completion(&pil_seg_data[seg_id].load_done);
		task_load_seg = kthread_run(pil_load_seg_thread_fn,
						&pil_seg_data[seg_id],
						"%s-%d", desc->name, seg_id);
		/*
		 * For error handling, do not block/kill other threads. Just
		 * set the error return code for this thread and call its
		 * completion. Errors can be handled while the threads are being
		 * collected.
		 */
		if (IS_ERR(task_load_seg)) {
			pil_seg_data[seg_id].retval = PTR_ERR(task_load_seg);
			complete(&pil_seg_data[seg_id].load_done);
			pil_err(desc,
			"Failed to spawn the thread for seg_id: %d, rc: %d\n",
			seg_id, pil_seg_data[seg_id].retval);
		}

		INIT_WORK(&pil_seg_data[seg_id].load_seg_work,
				pil_load_seg_work_fn);
		queue_work(pil_wq, &pil_seg_data[seg_id].load_seg_work);

		seg_id++;
	}
@@ -949,9 +937,7 @@ static int pil_load_segs(struct pil_desc *desc)
	/* Wait for the parallel loads to finish */
	seg_id = 0;
	list_for_each_entry(seg, &desc->priv->segs, list) {
		if (wait_for_completion_interruptible(
					&pil_seg_data[seg_id].load_done))
			pil_seg_data[seg_id].retval = -ERESTARTSYS;
		flush_work(&pil_seg_data[seg_id].load_seg_work);

		/* Don't exit if one of the thread fails. Wait for others to
		 * complete. Bitmap the return codes we get from the threads.
@@ -985,6 +971,7 @@ int pil_boot(struct pil_desc *desc)
{
	int ret;
	char fw_name[30];
	struct pil_seg *seg;
	const struct pil_mdt *mdt;
	const struct elf32_hdr *ehdr;
	const struct firmware *fw;
@@ -1095,9 +1082,21 @@ int pil_boot(struct pil_desc *desc)

	trace_pil_event("before_load_seg", desc);

	/**
	 * Fallback to serial loading of blobs if the
	 * workqueue creatation failed during module init.
	 */
	if (pil_wq) {
		ret = pil_load_segs(desc);
		if (ret)
			goto err_deinit_image;
	} else {
		list_for_each_entry(seg, &desc->priv->segs, list) {
			ret = pil_load_seg(desc, seg);
			if (ret)
				goto err_deinit_image;
		}
	}

	if (desc->subsys_vmid > 0) {
		trace_pil_event("before_reclaim_mem", desc);
@@ -1385,6 +1384,11 @@ static int __init msm_pil_init(void)
		pr_err("SMEM is not initialized.\n");
		return -EPROBE_DEFER;
	}

	pil_wq = alloc_workqueue("pil_workqueue", WQ_HIGHPRI | WQ_UNBOUND, 0);
	if (!pil_wq)
		pr_warn("pil: Defaulting to sequential firmware loading.\n");

out:
	return register_pm_notifier(&pil_pm_notifier);
}
@@ -1392,6 +1396,8 @@ subsys_initcall(msm_pil_init);

static void __exit msm_pil_exit(void)
{
	if (pil_wq)
		destroy_workqueue(pil_wq);
	unregister_pm_notifier(&pil_pm_notifier);
	if (pil_info_base)
		iounmap(pil_info_base);