The sys calls SYS_FLEN and SYS_READ were being used in a way that did not allow for file sizes > 2^31. Fixing this required changing the signatures of the functions smh_flen and smh_read to allow file sizes up to 2^32 - 2 and also being able to return errors that could be tested for.
This issue was found by Smatch. Signed-off-by: Andrew Goodbody <[email protected]> --- Changes in v2: - Fixup smh_flen and smh_read to allow for > 2^31 byte files - Link to v1: https://lore.kernel.org/r/[email protected] --- common/spl/spl_semihosting.c | 13 +++++++------ drivers/serial/serial_semihosting.c | 6 ++++-- fs/semihostingfs.c | 13 +++++++------ include/semihosting.h | 13 +++++++------ lib/semihosting.c | 25 +++++++++++++++++++------ 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/common/spl/spl_semihosting.c b/common/spl/spl_semihosting.c index f36863fe48d25859ee9af67a67140893b6e9e262..fb578a274d0d937b2ed5bd13e505bcefd4491799 100644 --- a/common/spl/spl_semihosting.c +++ b/common/spl/spl_semihosting.c @@ -13,13 +13,14 @@ static ulong smh_fit_read(struct spl_load_info *load, ulong file_offset, ulong size, void *buf) { long fd = *(long *)load->priv; - ulong ret; + long ret; + size_t bytes_read; if (smh_seek(fd, file_offset)) return 0; - ret = smh_read(fd, buf, size); - return ret < 0 ? 0 : ret; + ret = smh_read(fd, buf, size, &bytes_read); + return ret < 0 ? 0 : bytes_read; } static int spl_smh_load_image(struct spl_image_info *spl_image, @@ -27,7 +28,8 @@ static int spl_smh_load_image(struct spl_image_info *spl_image, { const char *filename = CONFIG_SPL_FS_LOAD_PAYLOAD_NAME; int ret; - long fd, len; + long fd; + size_t len; struct spl_load_info load; fd = smh_open(filename, MODE_READ | MODE_BINARY); @@ -36,12 +38,11 @@ static int spl_smh_load_image(struct spl_image_info *spl_image, return fd; } - ret = smh_flen(fd); + ret = smh_flen(fd, &len); if (ret < 0) { log_debug("could not get length of image: %d\n", ret); goto out; } - len = ret; spl_load_init(&load, smh_fit_read, &fd, 1); ret = spl_load(spl_image, bootdev, &load, len, 0); diff --git a/drivers/serial/serial_semihosting.c b/drivers/serial/serial_semihosting.c index 56a5ec72428afdd97eccad739a46189380ba3e76..017b1f8eda84f5c25e09be7e970d0ebfdbf2ace0 100644 --- a/drivers/serial/serial_semihosting.c +++ b/drivers/serial/serial_semihosting.c @@ -25,11 +25,12 @@ static int smh_serial_getc(struct udevice *dev) { char ch = 0; struct smh_serial_priv *priv = dev_get_priv(dev); + size_t bytes_read; if (priv->infd < 0) return smh_getc(); - smh_read(priv->infd, &ch, sizeof(ch)); + smh_read(priv->infd, &ch, sizeof(ch), &bytes_read); return ch; } @@ -140,11 +141,12 @@ static void smh_serial_setbrg(void) static int smh_serial_getc(void) { char ch = 0; + size_t bytes_read; if (infd < 0) return smh_getc(); - smh_read(infd, &ch, sizeof(ch)); + smh_read(infd, &ch, sizeof(ch), &bytes_read); return ch; } diff --git a/fs/semihostingfs.c b/fs/semihostingfs.c index 77e39ca407e4d240a1fd573497c5b6b908816454..71b6634b0660e32cc63f7fc60ec1af8d3248433a 100644 --- a/fs/semihostingfs.c +++ b/fs/semihostingfs.c @@ -23,7 +23,8 @@ int smh_fs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info) static int smh_fs_read_at(const char *filename, loff_t pos, void *buffer, loff_t maxsize, loff_t *actread) { - long fd, size, ret; + long fd, ret; + size_t size; fd = smh_open(filename, MODE_READ | MODE_BINARY); if (fd < 0) @@ -34,19 +35,19 @@ static int smh_fs_read_at(const char *filename, loff_t pos, void *buffer, return ret; } if (!maxsize) { - size = smh_flen(fd); + ret = smh_flen(fd, &size); if (ret < 0) { smh_close(fd); - return size; + return ret; } maxsize = size; } - size = smh_read(fd, buffer, maxsize); + ret = smh_read(fd, buffer, maxsize, &size); smh_close(fd); - if (size < 0) - return size; + if (ret < 0) + return ret; *actread = size; return 0; diff --git a/include/semihosting.h b/include/semihosting.h index 4e844cbad87bb1ae6bb365f87f3e7a8aeea445f4..60535199b9532ce16620aea3ca1ea741c5b73766 100644 --- a/include/semihosting.h +++ b/include/semihosting.h @@ -95,12 +95,12 @@ long smh_open(const char *fname, enum smh_open_mode mode); * @fd: A file descriptor returned from smh_open() * @memp: Pointer to a buffer of memory of at least @len bytes * @len: The number of bytes to read + * @read_len: Pointer which will be updated with actual bytes read on success, + * with 0 indicating %EOF * - * Return: - * * The number of bytes read on success, with 0 indicating %EOF - * * A negative error on failure + * Return: 0 on success or negative error on failure */ -long smh_read(long fd, void *memp, size_t len); +long smh_read(long fd, void *memp, size_t len, size_t *read_len); /** * smh_write() - Write data to a file @@ -124,10 +124,11 @@ long smh_close(long fd); /** * smh_flen() - Get the length of a file * @fd: A file descriptor returned from smh_open() + * @size: Pointer which will be updated with length of file on success * - * Return: The length of the file, in bytes, or a negative error on failure + * Return: 0 on success or negative error on failure */ -long smh_flen(long fd); +int smh_flen(long fd, size_t *size); /** * smh_seek() - Seek to a position in a file diff --git a/lib/semihosting.c b/lib/semihosting.c index 9be5bffd30124777c4f8110065e6cca5640312ac..a0f42d41e4a7ad95023c684f6824bfc677ad25cd 100644 --- a/lib/semihosting.c +++ b/lib/semihosting.c @@ -93,9 +93,9 @@ struct smh_rdwr_s { size_t len; }; -long smh_read(long fd, void *memp, size_t len) +long smh_read(long fd, void *memp, size_t len, size_t *read_len) { - long ret; + size_t ret; struct smh_rdwr_s read; debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); @@ -105,9 +105,20 @@ long smh_read(long fd, void *memp, size_t len) read.len = len; ret = smh_trap(SYSREAD, &read); - if (ret < 0) + if (!ret) { + /* Success */ + *read_len = len; + return 0; + } else if (ret == len) { + /* EOF */ + *read_len = 0; + return 0; + } else { + /* Partial success */ + *read_len = len - ret; return smh_errno(); - return len - ret; + } + } long smh_write(long fd, const void *memp, size_t len, ulong *written) @@ -140,7 +151,7 @@ long smh_close(long fd) return 0; } -long smh_flen(long fd) +int smh_flen(long fd, size_t *size) { long ret; @@ -149,7 +160,9 @@ long smh_flen(long fd) ret = smh_trap(SYSFLEN, &fd); if (ret == -1) return smh_errno(); - return ret; + + *size = (size_t)ret; + return 0; } long smh_seek(long fd, long pos) --- base-commit: da47ddebd16a7e1047da8537fbf01558d2a89fcf change-id: 20251002-fs_semihosting-85d697fbfcad Best regards, -- Andrew Goodbody <[email protected]>

