Commit message: check zlib space to be within buffer #39
CVE: CVE-2018-7725
CWE: CWE-119
Vulnerability description: An issue was discovered in ZZIPlib 0.13.68. An invalid memory address dereference was discovered in zzip_disk_fread in mmapped.c. The vulnerability causes an application crash, which leads to denial of service.

Project: zziplib
File: memdisk.c

zzip_mem_entry_fopen(ZZIP_MEM_DISK * dir, ZZIP_MEM_ENTRY * entry)
{
    /* keep this in sync with zzip_disk_entry_fopen */
    ZZIP_DISK_FILE *file = malloc(sizeof(ZZIP_MEM_DISK_FILE));
    if (! file)
        return file;
    file->buffer = dir->disk->buffer;
    file->endbuf = dir->disk->endbuf;
    file->avail = zzip_mem_entry_usize(entry);

    if (! file->avail || zzip_mem_entry_data_stored(entry))
        { file->stored = zzip_mem_entry_to_data (entry); return file; }

    file->stored = 0;
    file->zlib.opaque = 0;
    file->zlib.zalloc = Z_NULL;
    file->zlib.zfree = Z_NULL;
    file->zlib.avail_in = zzip_mem_entry_csize(entry);
    file->zlib.next_in = zzip_mem_entry_to_data(entry);

    debug2("compressed size %i", (int) file->zlib.avail_in);
    if (file->zlib.next_in + file->zlib.avail_in >= file->endbuf)
         goto error;
    if (file->zlib.next_in < file->buffer)
         goto error;

    if (! zzip_mem_entry_data_deflated(entry) ||
        inflateInit2(&file->zlib, -MAX_WBITS) != Z_OK)
        { free (file); return 0; }

    return file;
error:
    errno = EBADMSG;
    return NULL;
}

Project: zziplib
File: memdisk.c

zzip_mem_entry_find_extra_block(ZZIP_MEM_ENTRY * entry, short datatype, zzip_size_t blocksize)
{
    int i = 2;
    while (1)
    {
        char* ext = (char*)( entry->zz_ext[i] );
        char* ext_end = ext + entry->zz_extlen[i];
        if (ext)
        {
	    /*
	     * Make sure that
	     * 1) the extra block header
	     * AND
	     * 2) the block we're looking for
	     * fit into the extra block!
	     */
            while (ext + zzip_extra_block_headerlength + blocksize <= ext_end)
            {
                if (datatype == zzip_extra_block_get_datatype(ext))
                {
                    if (blocksize <= zzip_extra_block_get_datasize(ext) + zzip_extra_block_headerlength)
                    {
                        return ((ZZIP_EXTRA_BLOCK*) ext);
                    }
                }
                /* skip to start of next extra_block */
                ___ zzip_size_t datasize = zzip_extra_block_get_datasize(ext);
                ext += zzip_extra_block_headerlength;
                ext += datasize;
                ____;
            }
        }
        if (! i)
            return 0;
        i--;
    }
}

Project: zziplib
File: mmapped.c

zzip_disk_entry_fopen(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry)
{
    /* keep this in sync with zzip_mem_entry_fopen */
    struct zzip_file_header *header =
        zzip_disk_entry_to_file_header(disk, entry);
    if (! header)
        return 0; /* EBADMSG */
    ___ ZZIP_DISK_FILE *file = malloc(sizeof(ZZIP_DISK_FILE));
    if (! file)
        return 0; /* ENOMEM */
    file->buffer = disk->buffer;
    file->endbuf = disk->endbuf;
    file->avail = zzip_file_header_usize(header);

    if (! file->avail || zzip_file_header_data_stored(header))
    { 
         file->stored = zzip_file_header_to_data (header);
         DBG2("stored size %i", (int) file->avail);
         if (file->stored + file->avail >= disk->endbuf)
             goto error;
         return file; 
    }

    file->stored = 0;
    file->zlib.opaque = 0;
    file->zlib.zalloc = Z_NULL;
    file->zlib.zfree = Z_NULL;
    file->zlib.avail_in = zzip_file_header_csize(header);
    file->zlib.next_in = zzip_file_header_to_data(header);

    DBG2("compressed size %i", (int) file->zlib.avail_in);
    if (file->zlib.next_in + file->zlib.avail_in >= disk->endbuf)
         goto error;
    if (file->zlib.next_in < disk->buffer)
         goto error;

    if (! zzip_file_header_data_deflated(header))
        goto error;
    if (inflateInit2(&file->zlib, -MAX_WBITS) != Z_OK)
        goto error;

    return file;
error:
    free (file);
    errno = EBADMSG;
    return 0; 
    ____;
}

Project: zziplib
File: mmapped.c

zzip_disk_findfirst(ZZIP_DISK * disk)
{
    DBG1("findfirst");
    if (! disk)
    {
        DBG1("non arg");
        errno = EINVAL;
        return 0;
    }
    if (disk->buffer > disk->endbuf - sizeof(struct zzip_disk_trailer))
    {
        DBG1("not enough data for a disk trailer");
        errno = EBADMSG;
        return 0;
    }
    ___ zzip_byte_t *p = disk->endbuf - sizeof(struct zzip_disk_trailer);
    for (; p >= disk->buffer; p--)
    {
        zzip_byte_t *root;      /* (struct zzip_disk_entry*) */
	zzip_size_t rootsize;	/* Size of root central directory */

        if (zzip_disk_trailer_check_magic(p))
        {
            struct zzip_disk_trailer *trailer = (struct zzip_disk_trailer *) p;
            zzip_size_t rootseek = zzip_disk_trailer_get_rootseek(trailer);
	    rootsize = zzip_disk_trailer_get_rootsize(trailer);

            root = disk->buffer + rootseek;
            DBG2("disk rootseek at %lli", (long long)rootseek);
            if (root > p)
            {
                /* the first disk_entry is after the disk_trailer? can't be! */
                DBG2("have rootsize at %lli", (long long)rootsize);
                if (disk->buffer + rootsize > p)
                    continue;
                /* a common brokeness that can be fixed: we just assume the
                 * central directory was written directly before the trailer:*/
                root = p - rootsize;
            }
        } else if (zzip_disk64_trailer_check_magic(p))
        {
            struct zzip_disk64_trailer *trailer =
                (struct zzip_disk64_trailer *) p;
            if (sizeof(void *) < 8)
            {
                DBG1("disk64 trailer in non-largefile part");
                errno = EFBIG;
                return 0;
            }
            zzip_size_t rootseek = zzip_disk64_trailer_get_rootseek(trailer);
	    rootsize = zzip_disk64_trailer_get_rootsize(trailer);
            DBG2("disk64 rootseek at %lli", (long long)rootseek);
            root = disk->buffer + rootseek;
            if (root > p)
                continue;
        } else
        {
            continue;
        }

        DBG4("buffer %p root %p endbuf %p", disk->buffer, root, disk->endbuf);
        if (root < disk->buffer)
        {
            DBG1("root before buffer should be impossible");
            errno = EBADMSG;
            return 0;
        }
	if (root >= disk->endbuf || (root + rootsize) >= disk->endbuf)
	{
	    DBG1("root behind endbuf should be impossible");
	    errno = EBADMSG;
	    return 0;
	}
        if (zzip_disk_entry_check_magic(root))
        {
            DBG2("found the disk root %p", root);
            return (struct zzip_disk_entry *) root;
        }
    } ____;
    /* not found */
    errno = ENOENT;
    return 0;
}

Project: zziplib
File: memdisk.c

zzip_mem_disk_load(ZZIP_MEM_DISK * dir, ZZIP_DISK * disk)
{
    if (! dir || ! disk)
        { errno=EINVAL; return -1; }
    if (dir->list)
        zzip_mem_disk_unload(dir);
    ___ long count = 0;
    ___ struct zzip_disk_entry *entry = zzip_disk_findfirst(disk);
    if (!entry) goto error;
    for (; entry; entry = zzip_disk_findnext(disk, entry))
    {
        ZZIP_MEM_ENTRY *item = zzip_mem_entry_new(disk, entry);
        if (! item)
        {
            debug1("unable to load entry");
            goto error;
        }
        if (dir->last)
        {
            dir->last->zz_next = item;  /* chain last */
        } else
        {
            dir->list = item;
        }
        dir->last = item;       /* to earlier */
        count++;
    }
    ____;
    dir->disk = disk;
    return count;
    ____;
  error:
    zzip_mem_disk_unload(dir);
    return -1;
}