summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-04-01 11:53:17 +0100
committerAidan MacDonald <amachronic@protonmail.com>2022-04-09 15:20:57 +0100
commit32f1418c5ad1becd829b65628f9f6ec32b6dda79 (patch)
tree6f598f4927f4da64a79e5b8add3a4c0cb5134ca5 /apps
parent5aa0fc3b008e976a0d61a55814666229f2da47c8 (diff)
downloadrockbox-32f1418c5ad1becd829b65628f9f6ec32b6dda79.tar.gz
rockbox-32f1418c5ad1becd829b65628f9f6ec32b6dda79.zip
buffering: fix buffer overflows with bitmap loading
In some circumstances it was possible for a bitmap to overflow its buffer and overwrite the next handle. The easiest way to trigger it is with a highly compressed JPEG that is decoded to a large bitmap. Because the JPEG file size is used to determine how much to allocate this would cause an obvious buffer overflow when the JPEG is smaller than the decoded bitmap. Fix this by using the decoded bitmap size as the allocation size. Some overhead must be added to deal with JPEGs, but it will be freed once the image is loaded. A less obvious possibility is the fact that add_handle() will allow a handle to be added even if there's not enough space for the entire allocation. This is generally beneficial because it allows the first part of a file to be loaded while waiting for space to free up, but for bitmaps it is not valid because the whole image is loaded at once. Hence if there is not actually enough space in the buffer, the bitmap load can again overflow the actual free space and overwrite the next handle. The buffering code supports an H_ALLOCALL flag for allocations that need the free space available immediately, so use it for bitmaps to avoid that bug. load_image() had a sketchy-looking check for free space which stopped me from triggering the bug with simple tests, but since guessing the free space is obviously a bad idea when the caller *knows* how much free space there really is, remove that guess and let the caller tell load_image() the real deal. Change-Id: If62a58759705d83c16ee5b50f26bcbccc3f6c01f
Diffstat (limited to 'apps')
-rw-r--r--apps/buffering.c40
1 files changed, 23 insertions, 17 deletions
diff --git a/apps/buffering.c b/apps/buffering.c
index f80d73a4a8..db555d8805 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -848,8 +848,9 @@ static bool fill_buffer(void)
848 Return value is the total size (struct + data). */ 848 Return value is the total size (struct + data). */
849static int load_image(int fd, const char *path, 849static int load_image(int fd, const char *path,
850 struct bufopen_bitmap_data *data, 850 struct bufopen_bitmap_data *data,
851 size_t bufidx) 851 size_t bufidx, size_t max_size)
852{ 852{
853 (void)path;
853 int rc; 854 int rc;
854 struct bitmap *bmp = ringbuf_ptr(bufidx); 855 struct bitmap *bmp = ringbuf_ptr(bufidx);
855 struct dim *dim = data->dim; 856 struct dim *dim = data->dim;
@@ -863,25 +864,20 @@ static int load_image(int fd, const char *path,
863#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) 864#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)
864 bmp->maskdata = NULL; 865 bmp->maskdata = NULL;
865#endif 866#endif
866 int free = (int)MIN(buffer_len - bytes_used(), buffer_len - bufidx) 867 const int format = FORMAT_NATIVE | FORMAT_DITHER |
867 - sizeof(struct bitmap); 868 FORMAT_RESIZE | FORMAT_KEEP_ASPECT;
868
869#ifdef HAVE_JPEG 869#ifdef HAVE_JPEG
870 if (aa != NULL) { 870 if (aa != NULL) {
871 lseek(fd, aa->pos, SEEK_SET); 871 lseek(fd, aa->pos, SEEK_SET);
872 rc = clip_jpeg_fd(fd, aa->size, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| 872 rc = clip_jpeg_fd(fd, aa->size, bmp, (int)max_size, format, NULL);
873 FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
874 } 873 }
875 else if (strcmp(path + strlen(path) - 4, ".bmp")) 874 else if (strcmp(path + strlen(path) - 4, ".bmp"))
876 rc = read_jpeg_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| 875 rc = read_jpeg_fd(fd, bmp, (int)max_size, format, NULL);
877 FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
878 else 876 else
879#endif 877#endif
880 rc = read_bmp_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| 878 rc = read_bmp_fd(fd, bmp, (int)max_size, format, NULL);
881 FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
882 879
883 return rc + (rc > 0 ? sizeof(struct bitmap) : 0); 880 return rc + (rc > 0 ? sizeof(struct bitmap) : 0);
884 (void)path;
885} 881}
886#endif /* HAVE_ALBUMART */ 882#endif /* HAVE_ALBUMART */
887 883
@@ -967,11 +963,18 @@ int bufopen(const char *file, off_t offset, enum data_type type,
967 size_t size = 0; 963 size_t size = 0;
968#ifdef HAVE_ALBUMART 964#ifdef HAVE_ALBUMART
969 if (type == TYPE_BITMAP) { 965 if (type == TYPE_BITMAP) {
970 /* If albumart is embedded, the complete file is not buffered, 966 /* Bitmaps are resized to the requested dimensions when loaded,
971 * but only the jpeg part; filesize() would be wrong */ 967 * so the file size should not be used as it may be too large
968 * or too small */
972 struct bufopen_bitmap_data *aa = user_data; 969 struct bufopen_bitmap_data *aa = user_data;
973 if (aa->embedded_albumart) 970 size = BM_SIZE(aa->dim->width, aa->dim->height, FORMAT_NATIVE, false);
974 size = aa->embedded_albumart->size; 971 size += sizeof(struct bitmap);
972
973#ifdef HAVE_JPEG
974 /* JPEG loading requires extra memory
975 * TODO: don't add unncessary overhead for .bmp images! */
976 size += JPEG_DECODE_OVERHEAD;
977#endif
975 } 978 }
976#endif 979#endif
977 980
@@ -980,7 +983,10 @@ int bufopen(const char *file, off_t offset, enum data_type type,
980 983
981 unsigned int hflags = 0; 984 unsigned int hflags = 0;
982 if (type == TYPE_PACKET_AUDIO || type == TYPE_CODEC) 985 if (type == TYPE_PACKET_AUDIO || type == TYPE_CODEC)
983 hflags = H_CANWRAP; 986 hflags |= H_CANWRAP;
987 /* Bitmaps need their space allocated up front */
988 if (type == TYPE_BITMAP)
989 hflags |= H_ALLOCALL;
984 990
985 size_t adjusted_offset = offset; 991 size_t adjusted_offset = offset;
986 if (adjusted_offset > size) 992 if (adjusted_offset > size)
@@ -1031,7 +1037,7 @@ int bufopen(const char *file, off_t offset, enum data_type type,
1031#ifdef HAVE_ALBUMART 1037#ifdef HAVE_ALBUMART
1032 if (type == TYPE_BITMAP) { 1038 if (type == TYPE_BITMAP) {
1033 /* Bitmap file: we load the data instead of the file */ 1039 /* Bitmap file: we load the data instead of the file */
1034 int rc = load_image(fd, file, user_data, data); 1040 int rc = load_image(fd, file, user_data, data, padded_size);
1035 if (rc <= 0) { 1041 if (rc <= 0) {
1036 handle_id = ERR_FILE_ERROR; 1042 handle_id = ERR_FILE_ERROR;
1037 } else { 1043 } else {