From ec1d707cc5a19bf9f95a18291cd317e8ad96eb25 Mon Sep 17 00:00:00 2001 From: Maximilian Heise <7600641+maxheise@users.noreply.github.com> Date: Thu, 21 May 2026 10:07:12 +0200 Subject: [PATCH] epub: avoid a fixed-size buffer for the zip entry filename In extract_one_file() the current zip entry's filename is read into a fixed 512-byte buffer: gpointer currentfilename = g_malloc0(512); unzGetCurrentFileInfo64(..., currentfilename, 512, ...); The entry name is read by unzGetCurrentFileInfo64() from the current ZIP entry; its length (file_info.size_filename) is the entry's file-name-length field, a 16-bit value in the ZIP format, so it is whatever the archive declares, nothing here bounds it, and that call's return value is not checked. minizip copies at most the supplied buffer size and only NUL-terminates the name when it fits within that size, so a zip entry name of 512 bytes or more leaves the buffer truncated and without a terminator. The following uses of currentfilename (g_strrstr, the g_string_append_printf, the ".html" check) then read past the 512-byte allocation. The initial g_malloc0 zero-fill does not help once a name that long fills the whole buffer. Read the actual name length first: call unzGetCurrentFileInfo64() once with a NULL name buffer to populate file_info.size_filename, then allocate size_filename + 1 so the terminator always fits, and check the return code of both calls. Because the ZIP file-name-length field is 16-bit, size_filename can reach 65535 bytes. A name that long is not a usable extraction path, so reject the entry when size_filename exceeds PATH_MAX (from , the system's maximum path length) instead of allocating for and processing it. This bounds the allocation to a sane path length and stops an absurd entry name before it reaches the path-building code. The three new error paths close the entry opened by unzOpenCurrentFile(): the function's normal cleanup runs only at the out: label, which these early returns cannot reach because gfilepath is not yet initialised there (g_string_free() on it would read an indeterminate pointer). Each early path therefore closes the entry by hand and frees currentfilename only where it has already been allocated. For an ordinary short name the result is the same NUL-terminated string as before. The separate fixed 512-byte buffer used to read the file contents further down is unrelated and left unchanged. --- backend/epub/epub-document.c | 41 +++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/backend/epub/epub-document.c b/backend/epub/epub-document.c index 57d626c4..96112db6 100644 --- a/backend/epub/epub-document.c +++ b/backend/epub/epub-document.c @@ -41,6 +41,9 @@ /*For strcasestr(),strstr()*/ #include +/* For PATH_MAX */ +#include + typedef enum _xmlParseReturnType { XML_ATTRIBUTE, @@ -670,7 +673,6 @@ extract_one_file(EpubDocument* epub_document, GFile *tmp_gfile, GError ** error) GFile * outfile ; gsize writesize = 0; GString * gfilepath ; - unz_file_info64 info ; gchar* directory; GString* dir_create; GFileOutputStream * outstream ; @@ -682,8 +684,41 @@ extract_one_file(EpubDocument* epub_document, GFile *tmp_gfile, GError ** error) gboolean result = TRUE; - gpointer currentfilename = g_malloc0(512); - unzGetCurrentFileInfo64(epub_document->epubDocument,&info,currentfilename,512,NULL,0,NULL,0) ; + unz_file_info64 file_info; + int uret; + + uret = unzGetCurrentFileInfo64 (epub_document->epubDocument, + &file_info, NULL, 0, + NULL, 0, NULL, 0); + if (uret != UNZ_OK) { + g_warning ("cannot read zip entry info"); + unzCloseCurrentFile (epub_document->epubDocument); + return FALSE; + } + + /* PATH_MAX comes from : the system's maximum path + * length. The zip file-name-length field is a 16-bit value, so + * it can reach 65535 bytes, far longer than any path this code + * can usefully extract to. Reject such an entry rather than + * allocate for and process it. */ + if (file_info.size_filename > PATH_MAX) { + g_warning ("zip entry name too long (%lu bytes)", + (unsigned long) file_info.size_filename); + unzCloseCurrentFile (epub_document->epubDocument); + return FALSE; + } + + gpointer currentfilename = g_malloc0 (file_info.size_filename + 1); + uret = unzGetCurrentFileInfo64 (epub_document->epubDocument, + &file_info, currentfilename, + file_info.size_filename + 1, + NULL, 0, NULL, 0); + if (uret != UNZ_OK) { + g_free (currentfilename); + g_warning ("cannot read zip entry filename"); + unzCloseCurrentFile (epub_document->epubDocument); + return FALSE; + } directory = g_strrstr(currentfilename,"/") ; if ( directory != NULL )