summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRishabh Bhatnagar <rishabhb@codeaurora.org>2018-08-31 11:43:31 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-09-12 03:31:00 -0400
commit422b3db2a5036add39a82425b1dd9fb6c96481e8 (patch)
treee9d435631e8f0dcf969d10eea5528e61389c8883
parent6712cc9c22117a8af9f3df272b4a44fd2e4201cd (diff)
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. Fixes: 0e742e9275 ("firmware: provide infrastructure to make fw caching optional") [mcgrof: broken since feature introduction on v4.8] Cc: stable@vger.kernel.org # v4.8+ Signed-off-by: Vikram Mulukutla <markivx@codeaurora.org> Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/base/firmware_loader/main.c30
1 files changed, 18 insertions, 12 deletions
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 0943e7065e0e..b3c0498ee433 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -209,21 +209,24 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name)
209static int alloc_lookup_fw_priv(const char *fw_name, 209static int alloc_lookup_fw_priv(const char *fw_name,
210 struct firmware_cache *fwc, 210 struct firmware_cache *fwc,
211 struct fw_priv **fw_priv, void *dbuf, 211 struct fw_priv **fw_priv, void *dbuf,
212 size_t size) 212 size_t size, enum fw_opt opt_flags)
213{ 213{
214 struct fw_priv *tmp; 214 struct fw_priv *tmp;
215 215
216 spin_lock(&fwc->lock); 216 spin_lock(&fwc->lock);
217 tmp = __lookup_fw_priv(fw_name); 217 if (!(opt_flags & FW_OPT_NOCACHE)) {
218 if (tmp) { 218 tmp = __lookup_fw_priv(fw_name);
219 kref_get(&tmp->ref); 219 if (tmp) {
220 spin_unlock(&fwc->lock); 220 kref_get(&tmp->ref);
221 *fw_priv = tmp; 221 spin_unlock(&fwc->lock);
222 pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n"); 222 *fw_priv = tmp;
223 return 1; 223 pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n");
224 return 1;
225 }
224 } 226 }
227
225 tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size); 228 tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
226 if (tmp) 229 if (tmp && !(opt_flags & FW_OPT_NOCACHE))
227 list_add(&tmp->list, &fwc->head); 230 list_add(&tmp->list, &fwc->head);
228 spin_unlock(&fwc->lock); 231 spin_unlock(&fwc->lock);
229 232
@@ -493,7 +496,8 @@ int assign_fw(struct firmware *fw, struct device *device,
493 */ 496 */
494static int 497static int
495_request_firmware_prepare(struct firmware **firmware_p, const char *name, 498_request_firmware_prepare(struct firmware **firmware_p, const char *name,
496 struct device *device, void *dbuf, size_t size) 499 struct device *device, void *dbuf, size_t size,
500 enum fw_opt opt_flags)
497{ 501{
498 struct firmware *firmware; 502 struct firmware *firmware;
499 struct fw_priv *fw_priv; 503 struct fw_priv *fw_priv;
@@ -511,7 +515,8 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
511 return 0; /* assigned */ 515 return 0; /* assigned */
512 } 516 }
513 517
514 ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size); 518 ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
519 opt_flags);
515 520
516 /* 521 /*
517 * bind with 'priv' now to avoid warning in failure path 522 * bind with 'priv' now to avoid warning in failure path
@@ -571,7 +576,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
571 goto out; 576 goto out;
572 } 577 }
573 578
574 ret = _request_firmware_prepare(&fw, name, device, buf, size); 579 ret = _request_firmware_prepare(&fw, name, device, buf, size,
580 opt_flags);
575 if (ret <= 0) /* error or already assigned */ 581 if (ret <= 0) /* error or already assigned */
576 goto out; 582 goto out;
577 583