libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

commit 6182911f85814952db9bdd84307b9ab43acfd96f
parent b61f2f54a708661d4a659f6fb698f6bb627f3211
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 30 Jul 2026 00:01:52 +0200

fix unzip code insanity and possible huge-allocation bug, a bit

Diffstat:
Msrc/common/unzip.c | 561+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/common/unzip.h | 82++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/fuzz/fuzz_unzip.c | 12++++++------
Msrc/plugins/elf_extractor.c | 13++++++++-----
Msrc/plugins/rtf_extractor.c | 3+++
5 files changed, 343 insertions(+), 328 deletions(-)

diff --git a/src/common/unzip.c b/src/common/unzip.c @@ -1,6 +1,6 @@ /* This file is part of libextractor. - Copyright (C) 2004, 2008, 2012 Vidyut Samanta and Christian Grothoff + Copyright (C) 2004, 2008, 2012, 2026 Vidyut Samanta and Christian Grothoff libextractor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -62,6 +62,7 @@ #include "platform.h" #include <ctype.h> #include "extractor.h" +#include "le_architecture.h" #include "unzip.h" #define CASESENSITIVITY (0) @@ -85,24 +86,28 @@ struct FileFuncDefs { /** - * Callback for reading 'size' bytes from the ZIP archive into buf. + * Callback for reading @a size bytes from the ZIP archive into @a buf. + * Returns the number of bytes actually copied, which is less than + * @a size at the end of the archive. */ - uLong (*zread_file) (voidpf opaque, void*buf, uLong size); + size_t (*zread_file) (void *opaque, void *buf, size_t size); /** - * Callback to obtain the current read offset in the ZIP archive. + * Callback to obtain the current read offset in the ZIP archive, + * -1 on error. */ - long (*ztell_file) (voidpf opaque); + int64_t (*ztell_file) (void *opaque); /** * Callback for seeking to a different position in the ZIP archive. + * Returns 0 on success. */ - long (*zseek_file) (voidpf opaque, uLong offset, int origin); + int (*zseek_file) (void *opaque, int64_t offset, int whence); /** * Opaque argument to pass to all IO functions. */ - voidpf opaque; + void *opaque; }; @@ -146,19 +151,19 @@ struct GlobalInfo /** * total number of entries in - * the central dir on this disk + * the central dir on this disk 2 bytes */ - uLong number_entry; + uint16_t number_entry; /** - * size of the global comment of the zipfile + * size of the global comment of the zipfile 2 bytes */ - uLong size_comment; + uint16_t size_comment; /** * offset of the global comment in the zipfile */ - uLong offset_comment; + uint64_t offset_comment; }; @@ -171,7 +176,7 @@ struct UnzipFileInfoInternal /** * relative offset of local header 4 bytes */ - uLong offset_curfile; + uint32_t offset_curfile; }; @@ -195,47 +200,47 @@ struct FileInZipReadInfo /** * position in byte on the zipfile, for fseek */ - uLong pos_in_zipfile; + uint64_t pos_in_zipfile; /** * flag set if stream structure is initialised */ - uLong stream_initialised; + int stream_initialised; /** * offset of the local extra field */ - uLong offset_local_extrafield; + uint64_t offset_local_extrafield; /** - * size of the local extra field + * size of the local extra field 2 bytes */ - uInt size_local_extrafield; + uint16_t size_local_extrafield; /** * position in the local extra field in read */ - uLong pos_local_extrafield; + uint32_t pos_local_extrafield; /** - * crc32 of all data uncompressed so far + * crc32 of all data uncompressed so far 4 bytes */ - uLong crc32; + uint32_t crc32; /** - * crc32 we must obtain after decompress all + * crc32 we must obtain after decompress all 4 bytes */ - uLong crc32_wait; + uint32_t crc32_wait; /** - * number of bytes to be decompressed + * number of bytes to be decompressed 4 bytes */ - uLong rest_read_compressed; + uint32_t rest_read_compressed; /** - * number of bytes to be obtained after decomp + * number of bytes to be obtained after decomp 4 bytes */ - uLong rest_read_uncompressed; + uint32_t rest_read_uncompressed; /** * IO functions. @@ -243,14 +248,14 @@ struct FileInZipReadInfo struct FileFuncDefs z_filefunc; /** - * compression method (0==store) + * compression method (0==store) 2 bytes */ - uLong compression_method; + uint16_t compression_method; /** * byte before the zipfile, (>0 for sfx) */ - uLong byte_before_the_zipfile; + uint64_t byte_before_the_zipfile; }; @@ -273,38 +278,38 @@ struct EXTRACTOR_UnzipFile /** * byte before the zipfile, (>0 for sfx) */ - uLong byte_before_the_zipfile; + uint64_t byte_before_the_zipfile; /** * number of the current file in the zipfile */ - uLong num_file; + uint32_t num_file; /** * pos of the current file in the central dir */ - uLong pos_in_central_dir; + uint64_t pos_in_central_dir; /** * flag about the usability of the current file */ - uLong current_file_ok; + int current_file_ok; /** * position of the beginning of the central dir */ - uLong central_pos; + uint64_t central_pos; /** - * size of the central directory + * size of the central directory 4 bytes */ - uLong size_central_dir; + uint32_t size_central_dir; /** * offset of start of central directory with respect to the starting - * disk number + * disk number 4 bytes */ - uLong offset_central_dir; + uint32_t offset_central_dir; /** * public info about the current file in zip @@ -329,88 +334,54 @@ struct EXTRACTOR_UnzipFile /** - * Read a byte from a gz_stream; update next_in and avail_in. Return EOF - * for end of file. - * IN assertion: the stream s has been successfully opened for reading. + * Read a 16-bit little-endian field from the archive. + * + * The whole field is fetched with a single #ZREAD() and only then + * byte-swapped, so a truncated archive can be recognised (the read is + * short) instead of being papered over one byte at a time. Note that + * failure is reported as #EXTRACTOR_UNZIP_ERRNO and never as + * #EXTRACTOR_UNZIP_EOF, which is numerically #EXTRACTOR_UNZIP_OK and so + * would leave @a val untouched behind a successful-looking return. * * @param ffd functions for performing IO operations - * @param pi where to store the byte that was read - * @return #EXTRACTOR_UNZIP_OK on success, or #EXTRACTOR_UNZIP_EOF + * @param[out] val set to the value that was read, untouched on error + * @return #EXTRACTOR_UNZIP_OK on success, #EXTRACTOR_UNZIP_ERRNO if the + * field could not be read in full */ static int -read_byte_from_ffd (const struct FileFuncDefs *ffd, - int *pi) +read_uint16_from_ffd (const struct FileFuncDefs *ffd, + uint16_t *val) { - unsigned char c; + uint16_t raw; - if (1 != ZREAD (*ffd, &c, 1)) - return EXTRACTOR_UNZIP_EOF; - *pi = (int) c; + if (sizeof (raw) != ZREAD (*ffd, &raw, sizeof (raw))) + return EXTRACTOR_UNZIP_ERRNO; + *val = LE_le16toh (raw); return EXTRACTOR_UNZIP_OK; } /** - * Read a short (2 bytes) from a gz_stream; update next_in and avail_in. Return EOF - * for end of file. - * IN assertion: the stream s has been successfully opened for reading. + * Read a 32-bit little-endian field from the archive. * - * @param ffd functions for performing IO operations - * @param pi where to store the short that was read - * @return #EXTRACTOR_UNZIP_OK on success, or #EXTRACTOR_UNZIP_EOF - */ -static int -read_short_from_ffd (const struct FileFuncDefs *ffd, - uLong *pX) -{ - uLong x; - int i; - int err; - - *pX = 0; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x = (uLong) i; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x += ((uLong) i) << 8; - *pX = x; - return err; -} - - -/** - * Read a 'long' (4 bytes) from a gz_stream; update next_in and avail_in. Return EOF - * for end of file. - * IN assertion: the stream s has been successfully opened for reading. + * See read_uint16_from_ffd() for why this reads the field in one go and + * why it must not return #EXTRACTOR_UNZIP_EOF. * * @param ffd functions for performing IO operations - * @param pi where to store the long that was read - * @return #EXTRACTOR_UNZIP_OK on success, or #EXTRACTOR_UNZIP_EOF + * @param[out] val set to the value that was read, untouched on error + * @return #EXTRACTOR_UNZIP_OK on success, #EXTRACTOR_UNZIP_ERRNO if the + * field could not be read in full */ static int -read_long_from_ffd (const struct FileFuncDefs *ffd, - uLong *pX) +read_uint32_from_ffd (const struct FileFuncDefs *ffd, + uint32_t *val) { - uLong x; - int i; - int err; + uint32_t raw; - *pX = 0; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x = (uLong) i; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x += ((uLong) i) << 8; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x += ((uLong) i) << 16; - if (EXTRACTOR_UNZIP_OK != (err = read_byte_from_ffd (ffd, &i))) - return err; - x += ((uLong) i) << 24; - *pX = x; - return err; + if (sizeof (raw) != ZREAD (*ffd, &raw, sizeof (raw))) + return EXTRACTOR_UNZIP_ERRNO; + *val = LE_le32toh (raw); + return EXTRACTOR_UNZIP_OK; } @@ -438,15 +409,17 @@ read_long_from_ffd (const struct FileFuncDefs *ffd, * @return 0 if names are equal */ static int -EXTRACTOR_common_unzip_string_file_name_compare (const char*fileName1, - const char*fileName2, +EXTRACTOR_common_unzip_string_file_name_compare (const char *fileName1, + const char *fileName2, int iCaseSensitivity) { if (0 == iCaseSensitivity) iCaseSensitivity = CASESENSITIVITYDEFAULTVALUE; if (1 == iCaseSensitivity) - return strcmp (fileName1, fileName2); - return strcasecmp (fileName1, fileName2); + return strcmp (fileName1, + fileName2); + return strcasecmp (fileName1, + fileName2); } @@ -459,49 +432,50 @@ EXTRACTOR_common_unzip_string_file_name_compare (const char*fileName1, * Locate the central directory in the ZIP file. * * @param ffd IO functions - * @return offset of central directory, 0 on error + * @return offset of central directory, -1 on error */ -static uLong +static int64_t locate_central_directory (const struct FileFuncDefs *ffd) { unsigned char buf[BUFREADCOMMENT + 4]; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack = 0xffff; /* maximum size of global comment */ + int64_t file_size; + int64_t back_read; + int64_t max_back = 0xffff; /* maximum size of global comment */ if (0 != ZSEEK (*ffd, 0, SEEK_END)) - return 0; - uSizeFile = ZTELL (*ffd); - if (uMaxBack > uSizeFile) - uMaxBack = uSizeFile; - uBackRead = 4; - while (uBackRead < uMaxBack) + return -1; + if (0 > (file_size = ZTELL (*ffd))) + return -1; + if (max_back > file_size) + max_back = file_size; + back_read = 4; + while (back_read < max_back) { - uLong uReadSize; - uLong uReadPos; + size_t read_size; + int64_t read_pos; int i; - if (uBackRead + BUFREADCOMMENT > uMaxBack) - uBackRead = uMaxBack; + if (back_read + BUFREADCOMMENT > max_back) + back_read = max_back; else - uBackRead += BUFREADCOMMENT; - uReadPos = uSizeFile - uBackRead; - uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos)) - ? (BUFREADCOMMENT + 4) - : (uSizeFile - uReadPos); - if (0 != ZSEEK (*ffd, uReadPos, SEEK_SET)) + back_read += BUFREADCOMMENT; + read_pos = file_size - back_read; + read_size = (size_t) (((BUFREADCOMMENT + 4) < (file_size - read_pos)) + ? (BUFREADCOMMENT + 4) + : (file_size - read_pos)); + if (0 != ZSEEK (*ffd, read_pos, SEEK_SET)) break; - if (ZREAD (*ffd, buf, uReadSize) != uReadSize) + if (ZREAD (*ffd, buf, read_size) != read_size) break; - i = (int) uReadSize - 3; + i = (int) read_size - 3; while (i-- > 0) if ( (0x50 == (*(buf + i))) && (0x4b == (*(buf + i + 1))) && (0x05 == (*(buf + i + 2))) && (0x06 == (*(buf + i + 3))) ) - return uReadPos + i; + return read_pos + i; } - return 0; + return -1; } @@ -513,18 +487,18 @@ locate_central_directory (const struct FileFuncDefs *ffd) * @param ptm where to write time in readable format */ static void -dos_date_to_tmu_date (uLong ulDosDate, +dos_date_to_tmu_date (uint32_t ulDosDate, struct EXTRACTOR_UnzipDateTimeInfo*ptm) { - uLong uDate; - - uDate = (uLong) (ulDosDate >> 16); - ptm->tm_mday = (uInt) (uDate & 0x1f); - ptm->tm_mon = (uInt) ((((uDate) & 0x1E0) / 0x20) - 1); - ptm->tm_year = (uInt) (((uDate & 0x0FE00) / 0x0200) + 1980); - ptm->tm_hour = (uInt) ((ulDosDate & 0xF800) / 0x800); - ptm->tm_min = (uInt) ((ulDosDate & 0x7E0) / 0x20); - ptm->tm_sec = (uInt) (2 * (ulDosDate & 0x1f)); + uint32_t uDate; + + uDate = ulDosDate >> 16; + ptm->tm_mday = (unsigned int) (uDate & 0x1f); + ptm->tm_mon = (unsigned int) ((((uDate) & 0x1E0) / 0x20) - 1); + ptm->tm_year = (unsigned int) (((uDate & 0x0FE00) / 0x0200) + 1980); + ptm->tm_hour = (unsigned int) ((ulDosDate & 0xF800) / 0x800); + ptm->tm_min = (unsigned int) ((ulDosDate & 0x7E0) / 0x20); + ptm->tm_sec = (unsigned int) (2 * (ulDosDate & 0x1f)); } @@ -548,85 +522,86 @@ get_current_file_info (struct EXTRACTOR_UnzipFile *file, struct EXTRACTOR_UnzipFileInfo *pfile_info, struct UnzipFileInfoInternal *pfile_info_internal, char *szFileName, - uLong fileNameBufferSize, + size_t fileNameBufferSize, void *extraField, - uLong extraFieldBufferSize, + size_t extraFieldBufferSize, char *szComment, - uLong commentBufferSize) + size_t commentBufferSize) { struct EXTRACTOR_UnzipFileInfo file_info; struct UnzipFileInfoInternal file_info_internal; - uLong uMagic; - long lSeek; + uint32_t uMagic; + int64_t lSeek; if (NULL == file) return EXTRACTOR_UNZIP_PARAMERROR; if (0 != ZSEEK (file->z_filefunc, - file->pos_in_central_dir + file->byte_before_the_zipfile, + (int64_t) (file->pos_in_central_dir + + file->byte_before_the_zipfile), SEEK_SET)) return EXTRACTOR_UNZIP_ERRNO; /* we check the magic */ if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, &uMagic)) + read_uint32_from_ffd (&file->z_filefunc, &uMagic)) return EXTRACTOR_UNZIP_ERRNO; if (0x02014b50 != uMagic) return EXTRACTOR_UNZIP_BADZIPFILE; if ( (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.version)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.version)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.version_needed)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.version_needed)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.flag)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.flag)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.compression_method)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.compression_method)) || (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info.dosDate)) ) + read_uint32_from_ffd (&file->z_filefunc, + &file_info.dosDate)) ) return EXTRACTOR_UNZIP_ERRNO; dos_date_to_tmu_date (file_info.dosDate, &file_info.tmu_date); if ( (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info.crc)) || + read_uint32_from_ffd (&file->z_filefunc, + &file_info.crc)) || (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info.compressed_size)) || + read_uint32_from_ffd (&file->z_filefunc, + &file_info.compressed_size)) || (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info.uncompressed_size)) || + read_uint32_from_ffd (&file->z_filefunc, + &file_info.uncompressed_size)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.size_filename)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.size_filename)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.size_file_extra)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.size_file_extra)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.size_file_comment)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.size_file_comment)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.disk_num_start)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.disk_num_start)) || (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &file_info.internal_fa)) || + read_uint16_from_ffd (&file->z_filefunc, + &file_info.internal_fa)) || (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info.external_fa)) || + read_uint32_from_ffd (&file->z_filefunc, + &file_info.external_fa)) || (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &file_info_internal.offset_curfile)) ) + read_uint32_from_ffd (&file->z_filefunc, + &file_info_internal.offset_curfile)) ) return EXTRACTOR_UNZIP_ERRNO; lSeek = file_info.size_filename; if (NULL != szFileName) { - uLong uSizeRead; + size_t uSizeRead; if (file_info.size_filename < fileNameBufferSize) { @@ -652,12 +627,12 @@ get_current_file_info (struct EXTRACTOR_UnzipFile *file, szFileName, uSizeRead)) return EXTRACTOR_UNZIP_ERRNO; - lSeek -= uSizeRead; + lSeek -= (int64_t) uSizeRead; } if (NULL != extraField) { - uLong uSizeRead; + size_t uSizeRead; if (file_info.size_file_extra<extraFieldBufferSize) uSizeRead = file_info.size_file_extra; @@ -680,7 +655,7 @@ get_current_file_info (struct EXTRACTOR_UnzipFile *file, extraField, uSizeRead)) ) return EXTRACTOR_UNZIP_ERRNO; - lSeek += file_info.size_file_extra - uSizeRead; + lSeek += (int64_t) file_info.size_file_extra - (int64_t) uSizeRead; } else { @@ -689,7 +664,7 @@ get_current_file_info (struct EXTRACTOR_UnzipFile *file, if (NULL != szComment) { - uLong uSizeRead; + size_t uSizeRead; if (file_info.size_file_comment < commentBufferSize) { @@ -726,7 +701,7 @@ get_current_file_info (struct EXTRACTOR_UnzipFile *file, szComment, uSizeRead)) ) return EXTRACTOR_UNZIP_ERRNO; - lSeek += file_info.size_file_comment - uSizeRead; + lSeek += (int64_t) file_info.size_file_comment - (int64_t) uSizeRead; } else { @@ -778,20 +753,21 @@ unzip_open_using_ffd (struct FileFuncDefs *ffd) { struct EXTRACTOR_UnzipFile us; struct EXTRACTOR_UnzipFile *file; - uLong central_pos; - uLong uL; - uLong number_disk; /* number of the current dist, used for + int64_t central_pos; + int64_t offset_comment; + uint32_t uL; + uint16_t number_disk; /* number of the current dist, used for spanning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number of the disk with central dir, used + uint16_t number_disk_with_CD; /* number of the disk with central dir, used for spanning ZIP, unsupported, always 0*/ - uLong number_entry_CD; /* total number of entries in + uint16_t number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ memset (&us, 0, sizeof(us)); us.z_filefunc = *ffd; central_pos = locate_central_directory (&us.z_filefunc); - if (0 == central_pos) + if (0 > central_pos) return NULL; if (0 != ZSEEK (us.z_filefunc, central_pos, SEEK_SET)) @@ -799,27 +775,27 @@ unzip_open_using_ffd (struct FileFuncDefs *ffd) /* the signature, already checked */ if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&us.z_filefunc, &uL)) + read_uint32_from_ffd (&us.z_filefunc, &uL)) return NULL; /* number of this disk */ if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&us.z_filefunc, &number_disk)) + read_uint16_from_ffd (&us.z_filefunc, &number_disk)) return NULL; /* number of the disk with the start of the central directory */ if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&us.z_filefunc, &number_disk_with_CD)) + read_uint16_from_ffd (&us.z_filefunc, &number_disk_with_CD)) return NULL; /* total number of entries in the central dir on this disk */ if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&us.z_filefunc, &us.gi.number_entry)) + read_uint16_from_ffd (&us.z_filefunc, &us.gi.number_entry)) return NULL; /* total number of entries in the central dir */ if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&us.z_filefunc, &number_entry_CD)) + read_uint16_from_ffd (&us.z_filefunc, &number_entry_CD)) return NULL; if ( (number_entry_CD != us.gi.number_entry) || @@ -829,26 +805,29 @@ unzip_open_using_ffd (struct FileFuncDefs *ffd) /* size of the central directory */ if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&us.z_filefunc, &us.size_central_dir)) + read_uint32_from_ffd (&us.z_filefunc, &us.size_central_dir)) return NULL; /* offset of start of central directory with respect to the starting disk number */ if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&us.z_filefunc, &us.offset_central_dir)) + read_uint32_from_ffd (&us.z_filefunc, &us.offset_central_dir)) return NULL; /* zipfile comment length */ if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&us.z_filefunc, &us.gi.size_comment)) + read_uint16_from_ffd (&us.z_filefunc, &us.gi.size_comment)) + return NULL; + if (0 > (offset_comment = ZTELL (us.z_filefunc))) return NULL; - us.gi.offset_comment = ZTELL (us.z_filefunc); - if ((central_pos < us.offset_central_dir + us.size_central_dir)) + us.gi.offset_comment = (uint64_t) offset_comment; + if (central_pos < (int64_t) us.offset_central_dir + us.size_central_dir) return NULL; - us.byte_before_the_zipfile = central_pos - - (us.offset_central_dir + us.size_central_dir); - us.central_pos = central_pos; + us.byte_before_the_zipfile = (uint64_t) central_pos + - ((uint64_t) us.offset_central_dir + + us.size_central_dir); + us.central_pos = (uint64_t) central_pos; us.pfile_in_zip_read = NULL; us.encrypted = 0; @@ -929,7 +908,7 @@ EXTRACTOR_common_unzip_get_global_comment (struct EXTRACTOR_UnzipFile *file, comment_len = file->gi.size_comment + 1; if (0 != ZSEEK (file->z_filefunc, - file->gi.offset_comment, + (int64_t) file->gi.offset_comment, SEEK_SET)) return EXTRACTOR_UNZIP_ERRNO; if (comment_len - 1 != @@ -961,16 +940,21 @@ EXTRACTOR_common_unzip_get_current_file_info ( struct EXTRACTOR_UnzipFile *file, struct EXTRACTOR_UnzipFileInfo *pfile_info, char *szFileName, - uLong fileNameBufferSize, + size_t fileNameBufferSize, void *extraField, - uLong extraFieldBufferSize, + size_t extraFieldBufferSize, char *szComment, - uLong commentBufferSize) + size_t commentBufferSize) { - return get_current_file_info (file, pfile_info, NULL, - szFileName, fileNameBufferSize, - extraField, extraFieldBufferSize, - szComment, commentBufferSize); + return get_current_file_info (file, + pfile_info, + NULL, + szFileName, + fileNameBufferSize, + extraField, + extraFieldBufferSize, + szComment, + commentBufferSize); } @@ -992,7 +976,7 @@ EXTRACTOR_common_unzip_go_to_next_file (struct EXTRACTOR_UnzipFile *file) return EXTRACTOR_UNZIP_END_OF_LIST_OF_FILE; if (file->num_file + 1 == file->gi.number_entry) return EXTRACTOR_UNZIP_END_OF_LIST_OF_FILE; - file->pos_in_central_dir += SIZECENTRALDIRITEM + file->pos_in_central_dir += (uint64_t) SIZECENTRALDIRITEM + file->cur_file_info.size_filename + file->cur_file_info.size_file_extra + file->cur_file_info.size_file_comment; @@ -1028,8 +1012,8 @@ EXTRACTOR_common_unzip_go_find_local_file (struct EXTRACTOR_UnzipFile *file, */ struct EXTRACTOR_UnzipFileInfo cur_file_infoSaved; struct UnzipFileInfoInternal cur_file_info_internalSaved; - uLong num_fileSaved; - uLong pos_in_central_dirSaved; + uint32_t num_fileSaved; + uint64_t pos_in_central_dirSaved; if (NULL == file) return EXTRACTOR_UNZIP_PARAMERROR; @@ -1093,7 +1077,7 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, size_t len) { int err = EXTRACTOR_UNZIP_OK; - uInt iRead = 0; + size_t iRead = 0; struct FileInZipReadInfo *pfile_in_zip_read_info; if (NULL == file) @@ -1123,8 +1107,8 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, return EXTRACTOR_UNZIP_EOF; if (0 != ZSEEK (pfile_in_zip_read_info->z_filefunc, - pfile_in_zip_read_info->pos_in_zipfile - + pfile_in_zip_read_info->byte_before_the_zipfile, + (int64_t) (pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile), SEEK_SET)) return EXTRACTOR_UNZIP_ERRNO; if (ZREAD (pfile_in_zip_read_info->z_filefunc, @@ -1133,7 +1117,7 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, return EXTRACTOR_UNZIP_ERRNO; pfile_in_zip_read_info->pos_in_zipfile += uReadThis; - pfile_in_zip_read_info->rest_read_compressed -= uReadThis; + pfile_in_zip_read_info->rest_read_compressed -= (uint32_t) uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef *) pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt) uReadThis; @@ -1145,7 +1129,7 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, if ( (0 == pfile_in_zip_read_info->stream.avail_in) && (0 == pfile_in_zip_read_info->rest_read_compressed) ) - return (0 == iRead) ? EXTRACTOR_UNZIP_EOF : iRead; + return (0 == iRead) ? EXTRACTOR_UNZIP_EOF : (ssize_t) iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) @@ -1155,11 +1139,11 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, memcpy (pfile_in_zip_read_info->stream.next_out, pfile_in_zip_read_info->stream.next_in, uDoCopy); - pfile_in_zip_read_info->crc32 = crc32 (pfile_in_zip_read_info->crc32, - pfile_in_zip_read_info->stream. - next_out, - uDoCopy); - pfile_in_zip_read_info->rest_read_uncompressed -= uDoCopy; + pfile_in_zip_read_info->crc32 = + (uint32_t) crc32 (pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed -= (uint32_t) uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; @@ -1189,23 +1173,23 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, uOutThis = uTotalOutAfter - uTotalOutBefore; pfile_in_zip_read_info->crc32 = - crc32 (pfile_in_zip_read_info->crc32, bufBefore, - (uInt) (uOutThis)); + (uint32_t) crc32 (pfile_in_zip_read_info->crc32, bufBefore, + (uInt) (uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; + (uint32_t) uOutThis; - iRead += (uInt) (uTotalOutAfter - uTotalOutBefore); + iRead += (size_t) (uTotalOutAfter - uTotalOutBefore); if (Z_STREAM_END == err) - return (0 == iRead) ? EXTRACTOR_UNZIP_EOF : iRead; + return (0 == iRead) ? EXTRACTOR_UNZIP_EOF : (ssize_t) iRead; if (Z_OK != err) break; } } if (Z_OK == err) - return iRead; + return (ssize_t) iRead; return err; } @@ -1224,79 +1208,81 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, */ static int parse_current_file_coherency_header (struct EXTRACTOR_UnzipFile *file, - uInt *piSizeVar, - uLong *poffset_local_extrafield, - uInt *psize_local_extrafield) + uint32_t *piSizeVar, + uint64_t *poffset_local_extrafield, + uint16_t *psize_local_extrafield) { - uLong uMagic; - uLong uData; - uLong uFlags; - uLong size_filename; - uLong size_extra_field; + uint32_t uMagic; + uint16_t u16; + uint32_t u32; + uint16_t uFlags; + uint16_t size_filename; + uint16_t size_extra_field; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (0 != ZSEEK (file->z_filefunc, - file->cur_file_info_internal.offset_curfile - + file->byte_before_the_zipfile, + (int64_t) (file->cur_file_info_internal.offset_curfile + + file->byte_before_the_zipfile), SEEK_SET)) return EXTRACTOR_UNZIP_ERRNO; if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &uMagic)) + read_uint32_from_ffd (&file->z_filefunc, + &uMagic)) return EXTRACTOR_UNZIP_ERRNO; if (0x04034b50 != uMagic) return EXTRACTOR_UNZIP_BADZIPFILE; if ( (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, &uData)) || + read_uint16_from_ffd (&file->z_filefunc, &u16)) || /* version */ (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, &uFlags)) ) + read_uint16_from_ffd (&file->z_filefunc, &uFlags)) ) return EXTRACTOR_UNZIP_ERRNO; - if (EXTRACTOR_UNZIP_OK != read_short_from_ffd (&file->z_filefunc, &uData)) + if (EXTRACTOR_UNZIP_OK != read_uint16_from_ffd (&file->z_filefunc, &u16)) return EXTRACTOR_UNZIP_ERRNO; - if (uData != file->cur_file_info.compression_method) + if (u16 != file->cur_file_info.compression_method) return EXTRACTOR_UNZIP_BADZIPFILE; if ( (0 != file->cur_file_info.compression_method) && (Z_DEFLATED != file->cur_file_info.compression_method) ) return EXTRACTOR_UNZIP_BADZIPFILE; if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, &uData)) /* date/time */ + read_uint32_from_ffd (&file->z_filefunc, &u32)) /* date/time */ return EXTRACTOR_UNZIP_ERRNO; if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, &uData)) /* crc */ + read_uint32_from_ffd (&file->z_filefunc, &u32)) /* crc */ return EXTRACTOR_UNZIP_ERRNO; - if ( (uData != file->cur_file_info.crc) && + if ( (u32 != file->cur_file_info.crc) && (0 == (uFlags & 8)) ) return EXTRACTOR_UNZIP_BADZIPFILE; if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, &uData)) /* size compr */ + read_uint32_from_ffd (&file->z_filefunc, &u32)) /* size compr */ return EXTRACTOR_UNZIP_ERRNO; - if ( (uData != file->cur_file_info.compressed_size) && + if ( (u32 != file->cur_file_info.compressed_size) && (0 == (uFlags & 8)) ) return EXTRACTOR_UNZIP_BADZIPFILE; if (EXTRACTOR_UNZIP_OK != - read_long_from_ffd (&file->z_filefunc, - &uData)) /* size uncompr */ + read_uint32_from_ffd (&file->z_filefunc, + &u32)) /* size uncompr */ return EXTRACTOR_UNZIP_ERRNO; - if ( (uData != file->cur_file_info.uncompressed_size) && + if ( (u32 != file->cur_file_info.uncompressed_size) && (0 == (uFlags & 8))) return EXTRACTOR_UNZIP_BADZIPFILE; if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, &size_filename)) + read_uint16_from_ffd (&file->z_filefunc, &size_filename)) return EXTRACTOR_UNZIP_ERRNO; if (size_filename != file->cur_file_info.size_filename) return EXTRACTOR_UNZIP_BADZIPFILE; - *piSizeVar += (uInt) size_filename; + *piSizeVar += size_filename; if (EXTRACTOR_UNZIP_OK != - read_short_from_ffd (&file->z_filefunc, - &size_extra_field)) + read_uint16_from_ffd (&file->z_filefunc, + &size_extra_field)) return EXTRACTOR_UNZIP_ERRNO; - *poffset_local_extrafield = file->cur_file_info_internal.offset_curfile + *poffset_local_extrafield = (uint64_t) file->cur_file_info_internal. + offset_curfile + SIZEZIPLOCALHEADER + size_filename; - *psize_local_extrafield = (uInt) size_extra_field; - *piSizeVar += (uInt) size_extra_field; + *psize_local_extrafield = size_extra_field; + *piSizeVar += size_extra_field; return EXTRACTOR_UNZIP_OK; } @@ -1312,10 +1298,10 @@ int EXTRACTOR_common_unzip_open_current_file (struct EXTRACTOR_UnzipFile *file) { int err; - uInt iSizeVar; + uint32_t iSizeVar; struct FileInZipReadInfo *pfile_in_zip_read_info; - uLong offset_local_extrafield; /* offset of the local extra field */ - uInt size_local_extrafield; /* size of the local extra field */ + uint64_t offset_local_extrafield; /* offset of the local extra field */ + uint16_t size_local_extrafield; /* size of the local extra field */ if (NULL == file) return EXTRACTOR_UNZIP_PARAMERROR; @@ -1329,8 +1315,8 @@ EXTRACTOR_common_unzip_open_current_file (struct EXTRACTOR_UnzipFile *file) &offset_local_extrafield, &size_local_extrafield)) return EXTRACTOR_UNZIP_BADZIPFILE; - if (NULL == (pfile_in_zip_read_info = malloc (sizeof(struct - FileInZipReadInfo)))) + if (NULL == (pfile_in_zip_read_info + = malloc (sizeof(struct FileInZipReadInfo)))) return EXTRACTOR_UNZIP_INTERNALERROR; if (NULL == (pfile_in_zip_read_info->read_buffer = malloc (UNZ_BUFSIZE))) { @@ -1402,15 +1388,15 @@ EXTRACTOR_common_unzip_open_current_file (struct EXTRACTOR_UnzipFile *file) * @param size number of bytes desired * @return number of bytes copied to buf */ -static uLong -ec_read_file_func (voidpf opaque, - void*buf, - uLong size) +static size_t +ec_read_file_func (void *opaque, + void *buf, + size_t size) { struct EXTRACTOR_ExtractContext *ec = opaque; void *ptr; ssize_t ret; - uLong done; + size_t done; done = 0; while (done < size) @@ -1420,8 +1406,15 @@ ec_read_file_func (voidpf opaque, size - done); if (ret <= 0) return done; - memcpy (buf + done, ptr, ret); - done += ret; + /* the contract says a read never returns more than was asked for, + but this is the memcpy() that would run off the end of @a buf if + some data source ever got that wrong */ + if ((size_t) ret > size - done) + ret = (ssize_t) (size - done); + memcpy ((char *) buf + done, + ptr, + (size_t) ret); + done += (size_t) ret; } return done; } @@ -1433,12 +1426,14 @@ ec_read_file_func (voidpf opaque, * @param opaque the 'struct EXTRACTOR_ExtractContext' * @return current offset in file, -1 on error */ -static long -ec_tell_file_func (voidpf opaque) +static int64_t +ec_tell_file_func (void *opaque) { struct EXTRACTOR_ExtractContext *ec = opaque; - return ec->seek (ec->cls, 0, SEEK_CUR); + return ec->seek (ec->cls, + 0, + SEEK_CUR); } @@ -1447,17 +1442,17 @@ ec_tell_file_func (voidpf opaque) * * @param opaque the 'struct EXTRACTOR_ExtractContext' * @param offset where to seek - * @param origin relative to where should we seek + * @param whence relative to where should we seek * @return #EXTRACTOR_UNZIP_OK on success */ -static long -ec_seek_file_func (voidpf opaque, - uLong offset, - int origin) +static int +ec_seek_file_func (void *opaque, + int64_t offset, + int whence) { struct EXTRACTOR_ExtractContext *ec = opaque; - if (-1 == ec->seek (ec->cls, offset, origin)) + if (-1 == ec->seek (ec->cls, offset, whence)) return EXTRACTOR_UNZIP_INTERNALERROR; return EXTRACTOR_UNZIP_OK; } diff --git a/src/common/unzip.h b/src/common/unzip.h @@ -1,6 +1,6 @@ /* This file is part of libextractor. - Copyright (C) 2008, 2012 Christian Grothoff (and other contributing authors) + Copyright (C) 2008, 2012, 2026 Christian Grothoff (and other contributing authors) libextractor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -29,7 +29,10 @@ #ifndef LE_COMMON_UNZIP_H #define LE_COMMON_UNZIP_H -#include <zlib.h> +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> +#include <zlib.h> /* for Z_ERRNO */ /** * Operation was successful. @@ -48,6 +51,13 @@ /** * Reached end of the file (NOTE: same as OK!) + * + * Because this is numerically #EXTRACTOR_UNZIP_OK, it can only ever be + * used where "nothing left to read" is a *success*, i.e. as the 0 return + * of #EXTRACTOR_common_unzip_read_current_file(). It must never be + * returned to signal a short or failed read: the caller's + * `EXTRACTOR_UNZIP_OK != err` test cannot tell the two apart and would + * go on to use data that was never written. */ #define EXTRACTOR_UNZIP_EOF (0) @@ -85,109 +95,113 @@ struct EXTRACTOR_UnzipDateTimeInfo /** * seconds after the minute - [0,59] */ - uInt tm_sec; + unsigned int tm_sec; /** * minutes after the hour - [0,59] */ - uInt tm_min; + unsigned int tm_min; /** * hours since midnight - [0,23] */ - uInt tm_hour; + unsigned int tm_hour; /** * day of the month - [1,31] */ - uInt tm_mday; + unsigned int tm_mday; /** * months since January - [0,11] */ - uInt tm_mon; + unsigned int tm_mon; /** * years - [1980..2044] */ - uInt tm_year; + unsigned int tm_year; }; /** - * Information about a file in the zipfile + * Information about a file in the zipfile. + * + * Every member mirrors a field of the ZIP central directory header and + * has exactly that field's width, so a value that the format cannot + * represent cannot be reported here either. */ struct EXTRACTOR_UnzipFileInfo { /** * version made by 2 bytes */ - uLong version; + uint16_t version; /** * version needed to extract 2 bytes */ - uLong version_needed; + uint16_t version_needed; /** * general purpose bit flag 2 bytes */ - uLong flag; + uint16_t flag; /** * compression method 2 bytes */ - uLong compression_method; + uint16_t compression_method; /** * last mod file date in Dos fmt 4 bytes */ - uLong dosDate; + uint32_t dosDate; /** * crc-32 4 bytes */ - uLong crc; + uint32_t crc; /** * compressed size 4 bytes */ - uLong compressed_size; + uint32_t compressed_size; /** * uncompressed size 4 bytes */ - uLong uncompressed_size; + uint32_t uncompressed_size; /** * filename length 2 bytes */ - uLong size_filename; + uint16_t size_filename; /** * extra field length 2 bytes */ - uLong size_file_extra; + uint16_t size_file_extra; /** * file comment length 2 bytes */ - uLong size_file_comment; + uint16_t size_file_comment; /** * disk number start 2 bytes */ - uLong disk_num_start; + uint16_t disk_num_start; /** * internal file attributes 2 bytes */ - uLong internal_fa; + uint16_t internal_fa; /** * external file attributes 4 bytes */ - uLong external_fa; + uint32_t external_fa; /** * Time and date of last modification. @@ -284,22 +298,22 @@ EXTRACTOR_common_unzip_go_find_local_file (struct EXTRACTOR_UnzipFile *file, * @return EXTRACTOR_UNZIP_OK if there is no problem. */ int -EXTRACTOR_common_unzip_get_current_file_info (struct EXTRACTOR_UnzipFile *file, - struct EXTRACTOR_UnzipFileInfo * - pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize); +EXTRACTOR_common_unzip_get_current_file_info ( + struct EXTRACTOR_UnzipFile *file, + struct EXTRACTOR_UnzipFileInfo *pfile_info, + char *szFileName, + size_t fileNameBufferSize, + void *extraField, + size_t extraFieldBufferSize, + char *szComment, + size_t commentBufferSize); /** * Open for reading data the current file in the zipfile. * * @param file zipfile to manipulate - * @return EXTRACTOR_UNZIP_OK on success + * @return #EXTRACTOR_UNZIP_OK on success */ int EXTRACTOR_common_unzip_open_current_file (struct EXTRACTOR_UnzipFile *file); @@ -324,7 +338,7 @@ EXTRACTOR_common_unzip_read_current_file (struct EXTRACTOR_UnzipFile *file, /** * Close the file in zip opened with EXTRACTOR_common_unzip_open_current_file. * - * @return EXTRACTOR_UNZIP_CRCERROR if all the file was read but the CRC is not good + * @return #EXTRACTOR_UNZIP_CRCERROR if all the file was read but the CRC is not good */ int EXTRACTOR_common_unzip_close_current_file (struct EXTRACTOR_UnzipFile *file); diff --git a/src/fuzz/fuzz_unzip.c b/src/fuzz/fuzz_unzip.c @@ -104,11 +104,11 @@ probe_current_file_info (struct EXTRACTOR_UnzipFile *uf, EXTRACTOR_common_unzip_get_current_file_info (uf, &fi, name, - (uLong) fi.size_filename, + (size_t) fi.size_filename, extra, - (uLong) fi.size_file_extra, + (size_t) fi.size_file_extra, comment, - (uLong) + (size_t) fi.size_file_comment)) { free (name); @@ -128,7 +128,7 @@ probe_current_file_info (struct EXTRACTOR_UnzipFile *uf, (void) EXTRACTOR_common_unzip_get_current_file_info (uf, &fi, name, - (uLong) small, + (size_t) small, NULL, 0, NULL, 0); free (name); @@ -141,7 +141,7 @@ probe_current_file_info (struct EXTRACTOR_UnzipFile *uf, NULL, 0, NULL, 0, comment, - (uLong) small); + (size_t) small); free (comment); small = (0 == fi.size_file_extra) ? 0 @@ -150,7 +150,7 @@ probe_current_file_info (struct EXTRACTOR_UnzipFile *uf, (void) EXTRACTOR_common_unzip_get_current_file_info (uf, &fi, NULL, 0, - extra, (uLong) small, + extra, (size_t) small, NULL, 0); free (extra); } diff --git a/src/plugins/elf_extractor.c b/src/plugins/elf_extractor.c @@ -82,7 +82,7 @@ typedef struct &(p)->e_shentsize, \ &(p)->e_shnum, \ &(p)->e_shstrndx -static char *ELF_HEADER_SPECS[] = { +static const char *ELF_HEADER_SPECS[] = { "hhwwwwwhhhhhh", "HHWWWWWHHHHHH", }; @@ -121,7 +121,7 @@ typedef struct &(p)->e_shentsize, \ &(p)->e_shnum, \ &(p)->e_shstrndx -static char *ELF64_HEADER_SPECS[] = { +static const char *ELF64_HEADER_SPECS[] = { "hhwxxxwhhhhhh", "HHWXXXWHHHHHH", }; @@ -153,7 +153,7 @@ typedef struct &(p)->sh_info, \ &(p)->sh_addralign, \ &(p)->sh_entsize -static char *ELF_SECTION_SPECS[] = { +static const char *ELF_SECTION_SPECS[] = { "wwwwwwwwww", "WWWWWWWWWW", }; @@ -179,7 +179,7 @@ typedef struct &(p)->p_memsz, \ &(p)->p_flags, \ &(p)->p_align -static char *ELF_PHDR_SPECS[] = { +static const char *ELF_PHDR_SPECS[] = { "wwwwwwww", "WWWWWWWW", }; @@ -197,7 +197,7 @@ typedef struct #define ELF_DYN_FIELDS(p) \ & (p)->d_tag, \ &(p)->d_un -static char *ELF_DYN_SPECS[] = { +static const char *ELF_DYN_SPECS[] = { "ww", "WW", }; @@ -505,6 +505,9 @@ readStringTable (struct EXTRACTOR_ExtractContext *ec, * @param ec extraction context provided to the plugin */ void +EXTRACTOR_elf_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_elf_extract_method (struct EXTRACTOR_ExtractContext *ec) { Elf32_Ehdr ehdr; diff --git a/src/plugins/rtf_extractor.c b/src/plugins/rtf_extractor.c @@ -1951,6 +1951,9 @@ cleanup (struct RtfContext *rc) * @param ec extraction context provided to the plugin */ void +EXTRACTOR_rtf_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_rtf_extract_method (struct EXTRACTOR_ExtractContext *ec) { struct RtfContext *rc;