FAILED: patch "[PATCH] firmware: Fix security issue with request_firmware_into_buf()" failed to apply to 4.9-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 4.9-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
Quoted 1 line
From
422b3db2a5036add39a82425b1dd9fb6c96481e8Mon Sep 17 00:00:00 2001
From: Rishabh Bhatnagar <[email protected]>
Date: Fri, 31 Aug 2018 08:43:31 -0700
Subject: [PATCH] firmware: Fix security issue with request_firmware_into_buf()
When calling request_firmware_into_buf() with the FW_OPT_NOCACHE flag it is expected that firmware is loaded into buffer from memory. But inside alloc_lookup_fw_priv every new firmware that is loaded is added to the firmware cache (fwc) list head. So if any driver requests a firmware that is already loaded the code iterates over the above mentioned list and it can end up giving a pointer to other device driver's firmware buffer.
Also the existing copy may either be modified by drivers, remote processors or even freed. This causes a potential security issue with batched requests when using request_firmware_into_buf.
Fix alloc_lookup_fw_priv to not add to the fwc head list if FW_OPT_NOCACHE is set, and also don't do the lookup in the list.
drivers/base/firmware_loader/main.c +18 -12
@@ -209,21 +209,24 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name)static int alloc_lookup_fw_priv (const char *fw_name,struct firmware_cache *fwc,struct fw_priv **fw_priv,void *dbuf, -size_t size) +size_t size,enum fw_opt opt_flags) {struct fw_priv *tmp;spin_lock (&fwc->lock); - tmp =__lookup_fw_priv (fw_name); -if (tmp) { -kref_get (&tmp->ref); -spin_unlock (&fwc->lock); - *fw_priv = tmp; -pr_debug ("batched request - sharing the same struct fw_priv and lookup for multiple requests\n" ); -return 1 ; +if (!(opt_flags &FW_OPT_NOCACHE )) { + tmp =__lookup_fw_priv (fw_name); +if (tmp) { +kref_get (&tmp->ref); +spin_unlock (&fwc->lock); + *fw_priv = tmp; +pr_debug ("batched request - sharing the same struct fw_priv and lookup for multiple requests\n" ); +return 1 ; + } } + tmp =__allocate_fw_priv (fw_name, fwc, dbuf, size); -if (tmp) +if (tmp && !(opt_flags &FW_OPT_NOCACHE ))list_add (&tmp->list, &fwc->head);spin_unlock (&fwc->lock);
@@ -493,7 +496,8 @@ int assign_fw(struct firmware *fw, struct device *device,*/ static int _request_firmware_prepare (struct firmware **firmware_p,const char *name, -struct device *device,void *dbuf,size_t size) +struct device *device,void *dbuf,size_t size, +enum fw_opt opt_flags) {struct firmware *firmware;struct fw_priv *fw_priv;
@@ -511,7 +515,8 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,return 0 ;/* assigned */ } - ret =alloc_lookup_fw_priv (name, &fw_cache, &fw_priv, dbuf, size); + ret =alloc_lookup_fw_priv (name, &fw_cache, &fw_priv, dbuf, size, + opt_flags);/* * bind with 'priv' now to avoid warning in failure path
@@ -571,7 +576,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - ret =_request_firmware_prepare (&fw, name, device, buf, size); + ret =_request_firmware_prepare (&fw, name, device, buf, size, + opt_flags);if (ret <=0 )/* error or already assigned */ goto out;