| From stable+bounces-172480-greg=kroah.com@vger.kernel.org Fri Aug 22 19:20:15 2025 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 22 Aug 2025 13:20:05 -0400 |
| Subject: soc: qcom: mdt_loader: Ensure we don't read past the ELF header |
| To: stable@vger.kernel.org |
| Cc: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>, Doug Anderson <dianders@chromium.org>, Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>, Bjorn Andersson <andersson@kernel.org>, Sasha Levin <sashal@kernel.org> |
| Message-ID: <20250822172005.1328408-2-sashal@kernel.org> |
| |
| From: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com> |
| |
| [ Upstream commit 9f9967fed9d066ed3dae9372b45ffa4f6fccfeef ] |
| |
| When the MDT loader is used in remoteproc, the ELF header is sanitized |
| beforehand, but that's not necessary the case for other clients. |
| |
| Validate the size of the firmware buffer to ensure that we don't read |
| past the end as we iterate over the header. e_phentsize and e_shentsize |
| are validated as well, to ensure that the assumptions about step size in |
| the traversal are valid. |
| |
| Fixes: 2aad40d911ee ("remoteproc: Move qcom_mdt_loader into drivers/soc/qcom") |
| Cc: stable@vger.kernel.org |
| Reported-by: Doug Anderson <dianders@chromium.org> |
| Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com> |
| Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> |
| Link: https://lore.kernel.org/r/20250610-mdt-loader-validation-and-fixes-v2-1-f7073e9ab899@oss.qualcomm.com |
| Signed-off-by: Bjorn Andersson <andersson@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/soc/qcom/mdt_loader.c | 43 ++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 43 insertions(+) |
| |
| --- a/drivers/soc/qcom/mdt_loader.c |
| +++ b/drivers/soc/qcom/mdt_loader.c |
| @@ -17,6 +17,37 @@ |
| #include <linux/slab.h> |
| #include <linux/soc/qcom/mdt_loader.h> |
| |
| +static bool mdt_header_valid(const struct firmware *fw) |
| +{ |
| + const struct elf32_hdr *ehdr; |
| + size_t phend; |
| + size_t shend; |
| + |
| + if (fw->size < sizeof(*ehdr)) |
| + return false; |
| + |
| + ehdr = (struct elf32_hdr *)fw->data; |
| + |
| + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) |
| + return false; |
| + |
| + if (ehdr->e_phentsize != sizeof(struct elf32_phdr)) |
| + return -EINVAL; |
| + |
| + phend = size_add(size_mul(sizeof(struct elf32_phdr), ehdr->e_phnum), ehdr->e_phoff); |
| + if (phend > fw->size) |
| + return false; |
| + |
| + if (ehdr->e_shentsize != sizeof(struct elf32_shdr)) |
| + return -EINVAL; |
| + |
| + shend = size_add(size_mul(sizeof(struct elf32_shdr), ehdr->e_shnum), ehdr->e_shoff); |
| + if (shend > fw->size) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| static bool mdt_phdr_valid(const struct elf32_phdr *phdr) |
| { |
| if (phdr->p_type != PT_LOAD) |
| @@ -84,6 +115,9 @@ ssize_t qcom_mdt_get_size(const struct f |
| phys_addr_t max_addr = 0; |
| int i; |
| |
| + if (!mdt_header_valid(fw)) |
| + return -EINVAL; |
| + |
| ehdr = (struct elf32_hdr *)fw->data; |
| phdrs = (struct elf32_phdr *)(ehdr + 1); |
| |
| @@ -136,6 +170,9 @@ void *qcom_mdt_read_metadata(const struc |
| ssize_t ret; |
| void *data; |
| |
| + if (!mdt_header_valid(fw)) |
| + return ERR_PTR(-EINVAL); |
| + |
| ehdr = (struct elf32_hdr *)fw->data; |
| phdrs = (struct elf32_phdr *)(ehdr + 1); |
| |
| @@ -216,6 +253,9 @@ int qcom_mdt_pas_init(struct device *dev |
| int ret; |
| int i; |
| |
| + if (!mdt_header_valid(fw)) |
| + return -EINVAL; |
| + |
| ehdr = (struct elf32_hdr *)fw->data; |
| phdrs = (struct elf32_phdr *)(ehdr + 1); |
| |
| @@ -304,6 +344,9 @@ static int __qcom_mdt_load(struct device |
| if (!fw || !mem_region || !mem_phys || !mem_size) |
| return -EINVAL; |
| |
| + if (!mdt_header_valid(fw)) |
| + return -EINVAL; |
| + |
| is_split = qcom_mdt_bins_are_split(fw, fw_name); |
| ehdr = (struct elf32_hdr *)fw->data; |
| phdrs = (struct elf32_phdr *)(ehdr + 1); |