Add functionality for loading firmware files provided by the vendor
to be flashed into the device's internal flash memory. The firmware
consists of several components, such as the firmware executable itself,
chip-specific customizations, and configuration files.
The firmware file contains at least a flash utility, which is executed
on the device side, and one or more flashable components. Each component
has its own specific properties, such as the address where it should be
loaded during flashing, one or more destination flash pages, and
the flashing method that should be used.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
v3:
* minor fixes requested by Przemek
* reworked component loading using sscanf (thx Przemek)
v2:
* added additional includes
* removed empty line
* '*(dst+len)' -> '*(dst + len)'
* 'Santity' -> 'Sanity'
* fixed smatch warning about uninitialized 'rc'
---
drivers/dpll/zl3073x/Makefile | 2 +-
drivers/dpll/zl3073x/fw.c | 427 ++++++++++++++++++++++++++++++++++
drivers/dpll/zl3073x/fw.h | 52 +++++
3 files changed, 480 insertions(+), 1 deletion(-)
create mode 100644 drivers/dpll/zl3073x/fw.c
create mode 100644 drivers/dpll/zl3073x/fw.h
diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile
index 9894513f67dd3..84e22aae57e5f 100644
--- a/drivers/dpll/zl3073x/Makefile
+++ b/drivers/dpll/zl3073x/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ZL3073X) += zl3073x.o
-zl3073x-objs := core.o devlink.o dpll.o flash.o prop.o
+zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o prop.o
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
zl3073x_i2c-objs := i2c.o
diff --git a/drivers/dpll/zl3073x/fw.c b/drivers/dpll/zl3073x/fw.c
new file mode 100644
index 0000000000000..1d7bfcfb8cc39
--- /dev/null
+++ b/drivers/dpll/zl3073x/fw.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/netlink.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "core.h"
+#include "flash.h"
+#include "fw.h"
+
+#define ZL3073X_FW_ERR_PFX "FW load failed: "
+#define ZL3073X_FW_ERR_MSG(_zldev, _extack, _msg, ...) \
+ do { \
+ dev_err((_zldev)->dev, ZL3073X_FW_ERR_PFX _msg "\n", \
+ ## __VA_ARGS__); \
+ NL_SET_ERR_MSG_FMT_MOD((_extack), \
+ ZL3073X_FW_ERR_PFX _msg, \
+ ## __VA_ARGS__); \
+ } while (0)
+
+enum zl3073x_flash_type {
+ ZL3073X_FLASH_TYPE_NONE = 0,
+ ZL3073X_FLASH_TYPE_SECTORS,
+ ZL3073X_FLASH_TYPE_PAGE,
+ ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
+};
+
+struct zl3073x_fw_component_info {
+ const char *name;
+ size_t max_size;
+ enum zl3073x_flash_type flash_type;
+ u32 load_addr;
+ u32 dest_page;
+ u32 copy_page;
+};
+
+static const struct zl3073x_fw_component_info component_info[] = {
+ [ZL_FW_COMPONENT_UTIL] = {
+ .name = "utility",
+ .max_size = 0x2300,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_NONE,
+ },
+ [ZL_FW_COMPONENT_FW1] = {
+ .name = "firmware1",
+ .max_size = 0x35000,
+ .load_addr = 0x20002000,
+ .flash_type = ZL3073X_FLASH_TYPE_SECTORS,
+ .dest_page = 0x020,
+ },
+ [ZL_FW_COMPONENT_FW2] = {
+ .name = "firmware2",
+ .max_size = 0x0040,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
+ .dest_page = 0x3e0,
+ .copy_page = 0x000,
+ },
+ [ZL_FW_COMPONENT_FW3] = {
+ .name = "firmware3",
+ .max_size = 0x0248,
+ .load_addr = 0x20000400,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
+ .dest_page = 0x3e4,
+ .copy_page = 0x004,
+ },
+ [ZL_FW_COMPONENT_CFG0] = {
+ .name = "config0",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x3d0,
+ },
+ [ZL_FW_COMPONENT_CFG1] = {
+ .name = "config1",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x3c0,
+ },
+ [ZL_FW_COMPONENT_CFG2] = {
+ .name = "config2",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x3b0,
+ },
+ [ZL_FW_COMPONENT_CFG3] = {
+ .name = "config3",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x3a0,
+ },
+ [ZL_FW_COMPONENT_CFG4] = {
+ .name = "config4",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x390,
+ },
+ [ZL_FW_COMPONENT_CFG5] = {
+ .name = "config5",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x380,
+ },
+ [ZL_FW_COMPONENT_CFG6] = {
+ .name = "config6",
+ .max_size = 0x1000,
+ .load_addr = 0x20000000,
+ .flash_type = ZL3073X_FLASH_TYPE_PAGE,
+ .dest_page = 0x370,
+ },
+};
+
+/* Sanity check */
+static_assert(ARRAY_SIZE(component_info) == ZL_FW_NUM_COMPONENTS);
+
+/**
+ * zl3073x_fw_component_alloc - Alloc structure to hold firmware component
+ * @size: size of buffer to store data
+ *
+ * Return: pointer to allocated component structure or NULL on error.
+ */
+static struct zl3073x_fw_component *
+zl3073x_fw_component_alloc(size_t size)
+{
+ struct zl3073x_fw_component *comp;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return NULL;
+
+ comp->size = size;
+ comp->data = kzalloc(size, GFP_KERNEL);
+ if (!comp->data) {
+ kfree(comp);
+ return NULL;
+ }
+
+ return comp;
+}
+
+/**
+ * zl3073x_fw_component_free - Free allocated component structure
+ * @comp: pointer to allocated component
+ */
+static void
+zl3073x_fw_component_free(struct zl3073x_fw_component *comp)
+{
+ if (comp)
+ kfree(comp->data);
+
+ kfree(comp);
+}
+
+/**
+ * zl3073x_fw_component_id_get - Get ID for firmware component name
+ * @name: input firmware component name
+ *
+ * Return:
+ * - ZL3073X_FW_COMPONENT_* ID for known component name
+ * - ZL3073X_FW_COMPONENT_INVALID if the given name is unknown
+ */
+static enum zl3073x_fw_component_id
+zl3073x_fw_component_id_get(const char *name)
+{
+ enum zl3073x_fw_component_id id;
+
+ for (id = 0; id < ZL_FW_NUM_COMPONENTS; id++)
+ if (!strcasecmp(name, component_info[id].name))
+ return id;
+
+ return ZL_FW_COMPONENT_INVALID;
+}
+
+/**
+ * zl3073x_fw_component_load - Load component from firmware source
+ * @zldev: zl3073x device structure
+ * @pcomp: pointer to loaded component
+ * @psrc: data pointer to load component from
+ * @psize: remaining bytes in buffer
+ * @extack: netlink extack pointer to report errors
+ *
+ * The function allocates single firmware component and loads the data from
+ * the buffer specified by @psrc and @psize. Pointer to allocated component
+ * is stored in output @pcomp. Source data pointer @psrc and remaining bytes
+ * @psize are updated accordingly.
+ *
+ * Return:
+ * * 1 when component was allocated and loaded
+ * * 0 when there is no component to load
+ * * <0 on error
+ */
+static ssize_t
+zl3073x_fw_component_load(struct zl3073x_dev *zldev,
+ struct zl3073x_fw_component **pcomp,
+ const char **psrc, size_t *psize,
+ struct netlink_ext_ack *extack)
+{
+ const struct zl3073x_fw_component_info *info;
+ struct zl3073x_fw_component *comp = NULL;
+ struct device *dev = zldev->dev;
+ enum zl3073x_fw_component_id id;
+ char buf[32], name[16];
+ u32 count, size, *dest;
+ int pos, rc;
+
+ /* Fetch image name and size from input */
+ strscpy(buf, *psrc, min(sizeof(buf), *psize));
+ rc = sscanf(buf, "%15s %u %n", name, &count, &pos);
+ if (!rc) {
+ /* No more data */
+ return 0;
+ } else if (rc == 1) {
+ ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size");
+ return -EINVAL;
+ }
+ *psrc += pos;
+ *psize -= pos;
+
+ dev_dbg(dev, "Firmware component '%s' found\n", name);
+
+ id = zl3073x_fw_component_id_get(name);
+ if (id == ZL_FW_COMPONENT_INVALID) {
+ ZL3073X_FW_ERR_MSG(zldev, extack, "unknown component type '%s'",
+ name);
+ return -EINVAL;
+ }
+
+ info = &component_info[id];
+ size = count * sizeof(u32); /* get size in bytes */
+
+ /* Check image size validity */
+ if (size > component_info[id].max_size) {
+ ZL3073X_FW_ERR_MSG(zldev, extack,
+ "[%s] component is too big (%u bytes)\n",
+ info->name, size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Indicated component image size: %u bytes\n", size);
+
+ /* Alloc component */
+ comp = zl3073x_fw_component_alloc(size);
+ if (!comp) {
+ ZL3073X_FW_ERR_MSG(zldev, extack, "failed to alloc memory");
+ return -ENOMEM;
+ }
+ comp->id = id;
+
+ /* Load component data from firmware source */
+ for (dest = comp->data; count; count--, dest++) {
+ strscpy(buf, *psrc, min(sizeof(buf), *psize));
+ rc = sscanf(buf, "%x %n", dest, &pos);
+ if (!rc)
+ goto err_data;
+
+ *psrc += pos;
+ *psize -= pos;
+ }
+
+ *pcomp = comp;
+
+ return 1;
+
+err_data:
+ ZL3073X_FW_ERR_MSG(zldev, extack, "[%s] invalid or missing data",
+ info->name);
+
+ zl3073x_fw_component_free(comp);
+
+ return -ENODATA;
+}
+
+/**
+ * zl3073x_fw_free - Free allocated firmware
+ * @fw: firmware pointer
+ *
+ * The function frees existing firmware allocated by @zl3073x_fw_load.
+ */
+void zl3073x_fw_free(struct zl3073x_fw *fw)
+{
+ size_t i;
+
+ if (!fw)
+ return;
+
+ for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++)
+ zl3073x_fw_component_free(fw->component[i]);
+
+ kfree(fw);
+}
+
+/**
+ * zl3073x_fw_load - Load all components from source
+ * @zldev: zl3073x device structure
+ * @data: source buffer pointer
+ * @size: size of source buffer
+ * @extack: netlink extack pointer to report errors
+ *
+ * The functions allocate firmware structure and loads all components from
+ * the given buffer specified by @data and @size.
+ *
+ * Return: pointer to firmware on success, error pointer on error
+ */
+struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
+ size_t size, struct netlink_ext_ack *extack)
+{
+ struct zl3073x_fw_component *comp;
+ enum zl3073x_fw_component_id id;
+ struct zl3073x_fw *fw;
+ ssize_t rc;
+
+ /* Allocate firmware structure */
+ fw = kzalloc(sizeof(*fw), GFP_KERNEL);
+ if (!fw)
+ return ERR_PTR(-ENOMEM);
+
+ do {
+ /* Load single component */
+ rc = zl3073x_fw_component_load(zldev, &comp, &data, &size,
+ extack);
+ if (rc <= 0)
+ /* Everything was read or error occurred */
+ break;
+
+ id = comp->id;
+
+ /* Report error if the given component is present twice
+ * or more.
+ */
+ if (fw->component[id]) {
+ ZL3073X_FW_ERR_MSG(zldev, extack,
+ "duplicate component '%s' detected",
+ component_info[id].name);
+ zl3073x_fw_component_free(comp);
+ rc = -EINVAL;
+ break;
+ }
+
+ fw->component[id] = comp;
+ } while (true);
+
+ if (rc) {
+ /* Free allocated firmware in case of error */
+ zl3073x_fw_free(fw);
+ return ERR_PTR(rc);
+ }
+
+ return fw;
+}
+
+/**
+ * zl3073x_flash_bundle_flash - Flash all components
+ * @zldev: zl3073x device structure
+ * @components: pointer to components array
+ * @extack: netlink extack pointer to report errors
+ *
+ * Returns 0 in case of success or negative number otherwise.
+ */
+static int
+zl3073x_fw_component_flash(struct zl3073x_dev *zldev,
+ struct zl3073x_fw_component *comp,
+ struct netlink_ext_ack *extack)
+{
+ const struct zl3073x_fw_component_info *info;
+ int rc;
+
+ info = &component_info[comp->id];
+
+ switch (info->flash_type) {
+ case ZL3073X_FLASH_TYPE_NONE:
+ /* Non-flashable component - used for utility */
+ return 0;
+ case ZL3073X_FLASH_TYPE_SECTORS:
+ rc = zl3073x_flash_sectors(zldev, info->name, info->dest_page,
+ info->load_addr, comp->data,
+ comp->size, extack);
+ break;
+ case ZL3073X_FLASH_TYPE_PAGE:
+ rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
+ info->load_addr, comp->data, comp->size,
+ extack);
+ break;
+ case ZL3073X_FLASH_TYPE_PAGE_AND_COPY:
+ rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
+ info->load_addr, comp->data, comp->size,
+ extack);
+ if (!rc)
+ rc = zl3073x_flash_page_copy(zldev, info->name,
+ info->dest_page,
+ info->copy_page, extack);
+ break;
+ }
+ if (rc)
+ ZL3073X_FW_ERR_MSG(zldev, extack,
+ "Failed to flash component '%s'",
+ info->name);
+
+ return rc;
+}
+
+int zl3073x_fw_flash(struct zl3073x_dev *zldev, struct zl3073x_fw *zlfw,
+ struct netlink_ext_ack *extack)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++) {
+ if (!zlfw->component[i])
+ continue; /* Component is not present */
+
+ rc = zl3073x_fw_component_flash(zldev, zlfw->component[i],
+ extack);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/dpll/zl3073x/fw.h b/drivers/dpll/zl3073x/fw.h
new file mode 100644
index 0000000000000..fcaa89ab075e1
--- /dev/null
+++ b/drivers/dpll/zl3073x/fw.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ZL3073X_FW_H
+#define _ZL3073X_FW_H
+
+/*
+ * enum zl3073x_fw_component_id - Identifiers for possible flash components
+ */
+enum zl3073x_fw_component_id {
+ ZL_FW_COMPONENT_INVALID = -1,
+ ZL_FW_COMPONENT_UTIL = 0,
+ ZL_FW_COMPONENT_FW1,
+ ZL_FW_COMPONENT_FW2,
+ ZL_FW_COMPONENT_FW3,
+ ZL_FW_COMPONENT_CFG0,
+ ZL_FW_COMPONENT_CFG1,
+ ZL_FW_COMPONENT_CFG2,
+ ZL_FW_COMPONENT_CFG3,
+ ZL_FW_COMPONENT_CFG4,
+ ZL_FW_COMPONENT_CFG5,
+ ZL_FW_COMPONENT_CFG6,
+ ZL_FW_NUM_COMPONENTS
+};
+
+/**
+ * struct zl3073x_fw_component - Firmware component
+ * @id: Flash component ID
+ * @size: Size of the buffer
+ * @data: Pointer to buffer with component data
+ */
+struct zl3073x_fw_component {
+ enum zl3073x_fw_component_id id;
+ size_t size;
+ void *data;
+};
+
+/**
+ * struct zl3073x_fw - Firmware bundle
+ * @component: firmware components array
+ */
+struct zl3073x_fw {
+ struct zl3073x_fw_component *component[ZL_FW_NUM_COMPONENTS];
+};
+
+struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
+ size_t size, struct netlink_ext_ack *extack);
+void zl3073x_fw_free(struct zl3073x_fw *fw);
+
+int zl3073x_fw_flash(struct zl3073x_dev *zldev, struct zl3073x_fw *zlfw,
+ struct netlink_ext_ack *extack);
+
+#endif /* _ZL3073X_FW_H */
--
2.49.1
Hi Ivan, kernel test robot noticed the following build warnings: url: https://github.com/intel-lab-lkp/linux/commits/Ivan-Vecera/dpll-zl3073x-Add-functions-to-access-hardware-registers/20250814-014831 base: net-next/main patch link: https://lore.kernel.org/r/20250813174408.1146717-4-ivecera%40redhat.com patch subject: [PATCH net-next v3 3/5] dpll: zl3073x: Add firmware loading functionality config: xtensa-randconfig-r073-20250819 (https://download.01.org/0day-ci/archive/20250820/202508200929.zEY4ejFt-lkp@intel.com/config) compiler: xtensa-linux-gcc (GCC) 9.5.0 If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> | Closes: https://lore.kernel.org/r/202508200929.zEY4ejFt-lkp@intel.com/ smatch warnings: drivers/dpll/zl3073x/fw.c:239 zl3073x_fw_component_load() warn: potential user controlled sizeof overflow 'count * 4' '0-u32max * 4' vim +239 drivers/dpll/zl3073x/fw.c cd5cfd9ddd76800 Ivan Vecera 2025-08-13 202 static ssize_t cd5cfd9ddd76800 Ivan Vecera 2025-08-13 203 zl3073x_fw_component_load(struct zl3073x_dev *zldev, cd5cfd9ddd76800 Ivan Vecera 2025-08-13 204 struct zl3073x_fw_component **pcomp, cd5cfd9ddd76800 Ivan Vecera 2025-08-13 205 const char **psrc, size_t *psize, cd5cfd9ddd76800 Ivan Vecera 2025-08-13 206 struct netlink_ext_ack *extack) cd5cfd9ddd76800 Ivan Vecera 2025-08-13 207 { cd5cfd9ddd76800 Ivan Vecera 2025-08-13 208 const struct zl3073x_fw_component_info *info; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 209 struct zl3073x_fw_component *comp = NULL; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 210 struct device *dev = zldev->dev; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 211 enum zl3073x_fw_component_id id; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 212 char buf[32], name[16]; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 213 u32 count, size, *dest; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 214 int pos, rc; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 215 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 216 /* Fetch image name and size from input */ cd5cfd9ddd76800 Ivan Vecera 2025-08-13 217 strscpy(buf, *psrc, min(sizeof(buf), *psize)); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 218 rc = sscanf(buf, "%15s %u %n", name, &count, &pos); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 219 if (!rc) { cd5cfd9ddd76800 Ivan Vecera 2025-08-13 220 /* No more data */ cd5cfd9ddd76800 Ivan Vecera 2025-08-13 221 return 0; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 222 } else if (rc == 1) { cd5cfd9ddd76800 Ivan Vecera 2025-08-13 223 ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size"); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 224 return -EINVAL; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 225 } cd5cfd9ddd76800 Ivan Vecera 2025-08-13 226 *psrc += pos; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 227 *psize -= pos; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 228 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 229 dev_dbg(dev, "Firmware component '%s' found\n", name); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 230 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 231 id = zl3073x_fw_component_id_get(name); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 232 if (id == ZL_FW_COMPONENT_INVALID) { cd5cfd9ddd76800 Ivan Vecera 2025-08-13 233 ZL3073X_FW_ERR_MSG(zldev, extack, "unknown component type '%s'", cd5cfd9ddd76800 Ivan Vecera 2025-08-13 234 name); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 235 return -EINVAL; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 236 } cd5cfd9ddd76800 Ivan Vecera 2025-08-13 237 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 238 info = &component_info[id]; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 @239 size = count * sizeof(u32); /* get size in bytes */ This is an integer overflow. Imagine count is 0x80000001. That means size is 4. cd5cfd9ddd76800 Ivan Vecera 2025-08-13 240 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 241 /* Check image size validity */ cd5cfd9ddd76800 Ivan Vecera 2025-08-13 242 if (size > component_info[id].max_size) { size is valid. cd5cfd9ddd76800 Ivan Vecera 2025-08-13 243 ZL3073X_FW_ERR_MSG(zldev, extack, cd5cfd9ddd76800 Ivan Vecera 2025-08-13 244 "[%s] component is too big (%u bytes)\n", cd5cfd9ddd76800 Ivan Vecera 2025-08-13 245 info->name, size); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 246 return -EINVAL; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 247 } cd5cfd9ddd76800 Ivan Vecera 2025-08-13 248 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 249 dev_dbg(dev, "Indicated component image size: %u bytes\n", size); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 250 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 251 /* Alloc component */ cd5cfd9ddd76800 Ivan Vecera 2025-08-13 252 comp = zl3073x_fw_component_alloc(size); The allocation succeeds. cd5cfd9ddd76800 Ivan Vecera 2025-08-13 253 if (!comp) { cd5cfd9ddd76800 Ivan Vecera 2025-08-13 254 ZL3073X_FW_ERR_MSG(zldev, extack, "failed to alloc memory"); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 255 return -ENOMEM; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 256 } cd5cfd9ddd76800 Ivan Vecera 2025-08-13 257 comp->id = id; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 258 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 259 /* Load component data from firmware source */ cd5cfd9ddd76800 Ivan Vecera 2025-08-13 260 for (dest = comp->data; count; count--, dest++) { But count is invalid so so we will loop 134 million times. cd5cfd9ddd76800 Ivan Vecera 2025-08-13 261 strscpy(buf, *psrc, min(sizeof(buf), *psize)); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 262 rc = sscanf(buf, "%x %n", dest, &pos); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 263 if (!rc) cd5cfd9ddd76800 Ivan Vecera 2025-08-13 264 goto err_data; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 265 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 266 *psrc += pos; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 267 *psize -= pos; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 268 } cd5cfd9ddd76800 Ivan Vecera 2025-08-13 269 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 270 *pcomp = comp; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 271 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 272 return 1; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 273 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 274 err_data: cd5cfd9ddd76800 Ivan Vecera 2025-08-13 275 ZL3073X_FW_ERR_MSG(zldev, extack, "[%s] invalid or missing data", cd5cfd9ddd76800 Ivan Vecera 2025-08-13 276 info->name); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 277 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 278 zl3073x_fw_component_free(comp); cd5cfd9ddd76800 Ivan Vecera 2025-08-13 279 cd5cfd9ddd76800 Ivan Vecera 2025-08-13 280 return -ENODATA; cd5cfd9ddd76800 Ivan Vecera 2025-08-13 281 } -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On 20. 08. 25 8:40 dop., Dan Carpenter wrote: > Hi Ivan, > > kernel test robot noticed the following build warnings: > > url: https://github.com/intel-lab-lkp/linux/commits/Ivan-Vecera/dpll-zl3073x-Add-functions-to-access-hardware-registers/20250814-014831 > base: net-next/main > patch link: https://lore.kernel.org/r/20250813174408.1146717-4-ivecera%40redhat.com > patch subject: [PATCH net-next v3 3/5] dpll: zl3073x: Add firmware loading functionality > config: xtensa-randconfig-r073-20250819 (https://download.01.org/0day-ci/archive/20250820/202508200929.zEY4ejFt-lkp@intel.com/config) > compiler: xtensa-linux-gcc (GCC) 9.5.0 > > If you fix the issue in a separate patch/commit (i.e. not just a new version of > the same patch/commit), kindly add following tags > | Reported-by: kernel test robot <lkp@intel.com> > | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> > | Closes: https://lore.kernel.org/r/202508200929.zEY4ejFt-lkp@intel.com/ > > smatch warnings: > drivers/dpll/zl3073x/fw.c:239 zl3073x_fw_component_load() warn: potential user controlled sizeof overflow 'count * 4' '0-u32max * 4' > > vim +239 drivers/dpll/zl3073x/fw.c > > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 202 static ssize_t > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 203 zl3073x_fw_component_load(struct zl3073x_dev *zldev, > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 204 struct zl3073x_fw_component **pcomp, > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 205 const char **psrc, size_t *psize, > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 206 struct netlink_ext_ack *extack) > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 207 { > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 208 const struct zl3073x_fw_component_info *info; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 209 struct zl3073x_fw_component *comp = NULL; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 210 struct device *dev = zldev->dev; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 211 enum zl3073x_fw_component_id id; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 212 char buf[32], name[16]; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 213 u32 count, size, *dest; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 214 int pos, rc; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 215 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 216 /* Fetch image name and size from input */ > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 217 strscpy(buf, *psrc, min(sizeof(buf), *psize)); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 218 rc = sscanf(buf, "%15s %u %n", name, &count, &pos); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 219 if (!rc) { > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 220 /* No more data */ > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 221 return 0; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 222 } else if (rc == 1) { > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 223 ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size"); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 224 return -EINVAL; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 225 } > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 226 *psrc += pos; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 227 *psize -= pos; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 228 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 229 dev_dbg(dev, "Firmware component '%s' found\n", name); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 230 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 231 id = zl3073x_fw_component_id_get(name); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 232 if (id == ZL_FW_COMPONENT_INVALID) { > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 233 ZL3073X_FW_ERR_MSG(zldev, extack, "unknown component type '%s'", > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 234 name); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 235 return -EINVAL; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 236 } > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 237 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 238 info = &component_info[id]; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 @239 size = count * sizeof(u32); /* get size in bytes */ > > This is an integer overflow. Imagine count is 0x80000001. That means > size is 4. Will fix this in the next version. Thanks, Ivan > > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 240 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 241 /* Check image size validity */ > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 242 if (size > component_info[id].max_size) { > > size is valid. > > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 243 ZL3073X_FW_ERR_MSG(zldev, extack, > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 244 "[%s] component is too big (%u bytes)\n", > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 245 info->name, size); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 246 return -EINVAL; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 247 } > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 248 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 249 dev_dbg(dev, "Indicated component image size: %u bytes\n", size); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 250 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 251 /* Alloc component */ > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 252 comp = zl3073x_fw_component_alloc(size); > > The allocation succeeds. > > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 253 if (!comp) { > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 254 ZL3073X_FW_ERR_MSG(zldev, extack, "failed to alloc memory"); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 255 return -ENOMEM; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 256 } > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 257 comp->id = id; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 258 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 259 /* Load component data from firmware source */ > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 260 for (dest = comp->data; count; count--, dest++) { > > But count is invalid so so we will loop 134 million times. > > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 261 strscpy(buf, *psrc, min(sizeof(buf), *psize)); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 262 rc = sscanf(buf, "%x %n", dest, &pos); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 263 if (!rc) > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 264 goto err_data; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 265 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 266 *psrc += pos; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 267 *psize -= pos; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 268 } > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 269 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 270 *pcomp = comp; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 271 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 272 return 1; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 273 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 274 err_data: > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 275 ZL3073X_FW_ERR_MSG(zldev, extack, "[%s] invalid or missing data", > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 276 info->name); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 277 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 278 zl3073x_fw_component_free(comp); > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 279 > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 280 return -ENODATA; > cd5cfd9ddd76800 Ivan Vecera 2025-08-13 281 } >
On Wed, 13 Aug 2025 19:44:06 +0200 Ivan Vecera wrote: > +#define ZL3073X_FW_ERR_MSG(_zldev, _extack, _msg, ...) \ > + do { \ > + dev_err((_zldev)->dev, ZL3073X_FW_ERR_PFX _msg "\n", \ > + ## __VA_ARGS__); \ > + NL_SET_ERR_MSG_FMT_MOD((_extack), \ > + ZL3073X_FW_ERR_PFX _msg, \ > + ## __VA_ARGS__); \ > + } while (0) Please don't duplicate the messages to the logs. If devlink error reporting doesn't work it needs to be fixed in the core. > +static ssize_t > +zl3073x_fw_component_load(struct zl3073x_dev *zldev, > + struct zl3073x_fw_component **pcomp, > + const char **psrc, size_t *psize, > + struct netlink_ext_ack *extack) > +{ > + const struct zl3073x_fw_component_info *info; > + struct zl3073x_fw_component *comp = NULL; > + struct device *dev = zldev->dev; > + enum zl3073x_fw_component_id id; > + char buf[32], name[16]; > + u32 count, size, *dest; > + int pos, rc; > + > + /* Fetch image name and size from input */ > + strscpy(buf, *psrc, min(sizeof(buf), *psize)); > + rc = sscanf(buf, "%15s %u %n", name, &count, &pos); > + if (!rc) { > + /* No more data */ > + return 0; > + } else if (rc == 1) { > + ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size"); > + return -EINVAL; > + } > + *psrc += pos; > + *psize -= pos; what if pos > *psize ? I think the parsing needs more care.
Hi Kuba, sorry for the late response... (I was on PTO last 2 weeks). On 19. 08. 25 4:22 dop., Jakub Kicinski wrote: > On Wed, 13 Aug 2025 19:44:06 +0200 Ivan Vecera wrote: >> +#define ZL3073X_FW_ERR_MSG(_zldev, _extack, _msg, ...) \ >> + do { \ >> + dev_err((_zldev)->dev, ZL3073X_FW_ERR_PFX _msg "\n", \ >> + ## __VA_ARGS__); \ >> + NL_SET_ERR_MSG_FMT_MOD((_extack), \ >> + ZL3073X_FW_ERR_PFX _msg, \ >> + ## __VA_ARGS__); \ >> + } while (0) > > Please don't duplicate the messages to the logs. > If devlink error reporting doesn't work it needs to be fixed > in the core. OK, will fix this. >> +static ssize_t >> +zl3073x_fw_component_load(struct zl3073x_dev *zldev, >> + struct zl3073x_fw_component **pcomp, >> + const char **psrc, size_t *psize, >> + struct netlink_ext_ack *extack) >> +{ >> + const struct zl3073x_fw_component_info *info; >> + struct zl3073x_fw_component *comp = NULL; >> + struct device *dev = zldev->dev; >> + enum zl3073x_fw_component_id id; >> + char buf[32], name[16]; >> + u32 count, size, *dest; >> + int pos, rc; >> + >> + /* Fetch image name and size from input */ >> + strscpy(buf, *psrc, min(sizeof(buf), *psize)); >> + rc = sscanf(buf, "%15s %u %n", name, &count, &pos); >> + if (!rc) { >> + /* No more data */ >> + return 0; >> + } else if (rc == 1) { >> + ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size"); >> + return -EINVAL; >> + } >> + *psrc += pos; >> + *psize -= pos; > > what if pos > *psize ? I think the parsing needs more care. This should not happen. strscpy copies min(32, *psize) from the source to buf and sscanf parses buf and fills pos by index from the buf. The pos cannot be greater than *psize...or did I miss something? Thanks, Ivan
On Fri, 29 Aug 2025 12:39:30 +0200 Ivan Vecera wrote: > >> + strscpy(buf, *psrc, min(sizeof(buf), *psize)); > >> + rc = sscanf(buf, "%15s %u %n", name, &count, &pos); > >> + if (!rc) { > >> + /* No more data */ > >> + return 0; > >> + } else if (rc == 1) { > >> + ZL3073X_FW_ERR_MSG(zldev, extack, "invalid component size"); > >> + return -EINVAL; > >> + } > >> + *psrc += pos; > >> + *psize -= pos; > > > > what if pos > *psize ? I think the parsing needs more care. > > This should not happen. strscpy copies min(32, *psize) from the source > to buf and sscanf parses buf and fills pos by index from the buf. > The pos cannot be greater than *psize...or did I miss something? Glancing at it now, I think I was concerned that *psize will go negative / wrap. So potentially leading to over-read of psrc, rather than overflow of buf. But I could well be wrong..
© 2016 - 2025 Red Hat, Inc.