ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/libclamav/png.c

1432 lines
46 KiB

/*
* Copyright 1995-2007 by Alexander Lehmann <lehmann@usa.net>,
* Andreas Dilger <adilger@enel.ucalgary.ca>,
* Glenn Randers-Pehrson <randeg@alum.rpi.edu>,
* Greg Roelofs <newt@pobox.com>,
* John Bowler <jbowler@acm.org>,
* Tom Lane <tgl@sss.pgh.pa.us>
* Copyright (C) 2011 Sourcefire, Inc.
* Maintainer: Tomasz Kojm <tkojm@clamav.net>
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <zlib.h>
#include "clamav.h"
#include "others.h"
#include "png.h"
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define BS 32000 /* size of read block for CRC calculation (and zlib) */
/* Mark's macros to extract big-endian short and long ints: */
#define SH(p) ((ush)(uch)((p)[1]) | ((ush)(uch)((p)[0]) << 8))
#define LG(p) ((ulg)(SH((p)+2)) | ((ulg)(SH(p)) << 16))
#define isASCIIalpha(x) (ascii_alpha_table[x] & 0x1)
#define ANCILLARY(chunkID) ((chunkID)[0] & 0x20)
#define PRIVATE(chunkID) ((chunkID)[1] & 0x20)
#define RESERVED(chunkID) ((chunkID)[2] & 0x20)
#define SAFECOPY(chunkID) ((chunkID)[3] & 0x20)
#define CRITICAL(chunkID) (!ANCILLARY(chunkID))
#define PUBLIC(chunkID) (!PRIVATE(chunkID))
/* what the PNG, MNG and JNG magic numbers should be */
static const uch good_PNG_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
static const uch good_MNG_magic[8] = {138, 77, 78, 71, 13, 10, 26, 10};
static const uch good_JNG_magic[8] = {139, 74, 78, 71, 13, 10, 26, 10};
/* GRR FIXME: could merge all three of these into single table (bit fields) */
/* GRR 20061203: for "isalpha()" that works even on EBCDIC machines */
static const uch ascii_alpha_table[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
/* GRR 20070707: list of forbidden characters in various keywords */
static const uch latin1_keyword_forbidden[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
/* GRR 20070707: list of discouraged (control) characters in tEXt/zTXt text */
static const uch latin1_text_discouraged[256] = {
1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
/* PNG stuff */
static const char *png_type[] = { /* IHDR, tRNS, BASI, summary */
"grayscale",
"INVALID",
"RGB",
"palette",
"grayscale+alpha",
"INVALID",
"RGB+alpha"
};
#define CRCCOMPL(c) c
#define CRCINIT (0)
#define update_crc crc32
static ulg getlong(fmap_t *map, unsigned int *offset, const char *where)
{
ulg res = 0;
int j;
for (j = 0; j < 4; ++j) {
unsigned char c;
if(fmap_readn(map, &c, *offset, sizeof(c)) != sizeof(c)) {
cli_dbgmsg("PNG: EOF(?) while reading %s\n", where);
return 0;
}
(*offset)++;
res <<= 8;
res |= c & 0xff;
}
return res;
}
static int keywordlen(uch *buf, int maxsize)
{
int j = 0;
while (j < maxsize && buf[j])
++j;
return j;
}
static const char *getmonth(int m)
{
static const char *month[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
return (m < 1 || m > 12)? "INVALID" : month[m-1];
}
/* GRR 20061203: now EBCDIC-safe */
static int check_chunk_name(char *chunk_name)
{
if (isASCIIalpha((int)chunk_name[0]) && isASCIIalpha((int)chunk_name[1]) &&
isASCIIalpha((int)chunk_name[2]) && isASCIIalpha((int)chunk_name[3]))
return 0;
cli_dbgmsg("PNG: invalid chunk name\n");
return CL_EPARSE; /* usually means we've "jumped the tracks": bail! */
}
/* GRR 20050724 */
/* caller must do return CL_EPARSE based on return value (0 == OK) */
/* keyword_name is "keyword" for most chunks, but it can instead be "name" or
* "identifier" or whatever makes sense for the chunk in question */
static int check_keyword(uch *buffer, int maxsize, int *pKeylen)
{
int j, prev_space = 0;
int keylen = keywordlen(buffer, maxsize);
if (pKeylen)
*pKeylen = keylen;
if (keylen == 0) {
cli_dbgmsg("PNG: zero length keyword\n");
return 1;
}
if (keylen > 79) {
cli_dbgmsg("PNG: keyword is longer than 79 characters\n");
return 2;
}
if (buffer[0] == ' ') {
cli_dbgmsg("PNG: keyword has leading space(s)\n");
return 3;
}
if (buffer[keylen - 1] == ' ') {
cli_dbgmsg("PNG: keyword has trailing space(s)\n");
return 4;
}
for (j = 0; j < keylen; ++j) {
if (buffer[j] == ' ') {
if (prev_space) {
cli_dbgmsg("PNG: keyword has consecutive spaces\n");
return 5;
}
prev_space = 1;
} else {
prev_space = 0;
}
}
for (j = 0; j < keylen; ++j) {
if (latin1_keyword_forbidden[buffer[j]]) { /* [0,31] || [127,160] */
cli_dbgmsg("PNG: keyword has control character(s)\n");
return 6;
}
}
return 0;
}
/* GRR 20070707 */
/* caller must do return CL_EPARSE based on return value (0 == OK) */
static int check_text(uch *buffer, int maxsize)
{
int j;
for (j = 0; j < maxsize; ++j) {
if (buffer[j] == 0) {
cli_dbgmsg("PNG: text contains NULL character(s)\n");
return 1;
} else if (latin1_text_discouraged[buffer[j]]) {
cli_dbgmsg("PNG: text has control character(s)\n");
return 1;
}
}
return 0;
}
/* GRR 20061203 (used only for sCAL) */
static int check_ascii_float(uch *buffer, int len)
{
uch *qq = buffer, *bufEnd = buffer + len;
int have_sign = 0, have_integer = 0, have_dot = 0, have_fraction = 0;
int have_E = 0, have_Esign = 0, have_exponent = 0, in_digits = 0;
int have_nonzero = 0;
int rc = 0;
for (qq = buffer; qq < bufEnd && !rc; ++qq) {
switch (*qq) {
case '+':
case '-':
if (qq == buffer) {
have_sign = 1;
in_digits = 0;
} else if (have_E && !have_Esign) {
have_Esign = 1;
in_digits = 0;
} else {
cli_dbgmsg("PNG: invalid sign character\n");
rc = 1;
}
break;
case '.':
if (!have_dot && !have_E) {
have_dot = 1;
in_digits = 0;
} else {
cli_dbgmsg("PNG: invalid decimal point\n");
rc = 2;
}
break;
case 'e':
case 'E':
if (have_integer || have_fraction) {
have_E = 1;
in_digits = 0;
} else {
cli_dbgmsg("PNG: invalid exponent before mantissa\n");
rc = 3;
}
break;
default:
if (*qq < '0' || *qq > '9') {
cli_dbgmsg("PNG: invalid character\n");
rc = 4;
} else if (in_digits) {
/* still in digits: do nothing except check for non-zero digits */
if (!have_exponent && *qq != '0')
have_nonzero = 1;
} else if (!have_integer && !have_dot) {
have_integer = 1;
in_digits = 1;
if (*qq != '0')
have_nonzero = 1;
} else if (have_dot && !have_fraction) {
have_fraction = 1;
in_digits = 1;
if (*qq != '0')
have_nonzero = 1;
} else if (have_E && !have_exponent) {
have_exponent = 1;
in_digits = 1;
} else {
/* is this case possible? */
cli_dbgmsg("PNG: invalid digits\n");
rc = 5;
}
break;
}
}
/* must have either integer part or fractional part; all else is optional */
if (rc == 0 && !have_integer && !have_fraction) {
cli_dbgmsg("PNG: missing mantissa\n");
rc = 6;
}
/* non-exponent part must be non-zero (=> must have seen a non-zero digit) */
if (rc == 0 && !have_nonzero) {
cli_dbgmsg("PNG: invalid zero value(s)\n");
rc = 7;
}
return rc;
}
int cli_parsepng(cli_ctx *ctx)
{
long sz;
uch magic[8];
char chunkid[5] = {'\0', '\0', '\0', '\0', '\0'};
int toread;
int c;
int have_IHDR = 0, have_IEND = 0;
int have_PLTE = 0;
int have_IDAT = 0, have_JDAT = 0, last_is_IDAT = 0, last_is_JDAT = 0;
int have_bKGD = 0, have_cHRM = 0, have_gAMA = 0, have_hIST = 0, have_iCCP = 0;
int have_oFFs = 0, have_pCAL = 0, have_pHYs = 0, have_sBIT = 0, have_sCAL = 0;
int have_sRGB = 0, have_sTER = 0, have_tIME = 0, have_tRNS = 0;
ulg zhead = 1; /* 0x10000 indicates both zlib header bytes read */
ulg crc, filecrc;
long num_chunks = 0L;
long w = 0L, h = 0L;
int bitdepth = 0, sampledepth = 0, lace = 0, nplte = 0;
unsigned int ityp = 1;
uch buffer[BS];
int first_idat = 1; /* flag: is this the first IDAT chunk? */
int zlib_error = 0; /* reset in IHDR section; used for IDAT */
int check_zlib = 1; /* validate zlib stream (just IDATs for now) */
unsigned zlib_windowbits = 15;
uch outbuf[BS];
z_stream zstrm;
unsigned int offset = 0;
fmap_t *map = *ctx->fmap;
cli_dbgmsg("in cli_parsepng()\n");
if(fmap_readn(map, magic, offset, 8) != 8)
return CL_SUCCESS; /* Ignore */
if(memcmp(magic, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8))
return CL_SUCCESS; /* Not a PNG file */
offset += 8;
/*-------------------- BEGINNING OF IMMENSE WHILE-LOOP --------------------*/
while(fmap_readn(map, &c, offset, sizeof(c)) == sizeof(c)) {
if (have_IEND) {
cli_dbgmsg("PNG: additional data after END chunk\n");
return CL_EPARSE;
}
sz = getlong(map, &offset, "chunk length");
if (sz < 0 || sz > 0x7fffffff) { /* FIXME: convert to ulg, lose "< 0" */
cli_dbgmsg("PNG: invalid chunk length (too large)\n");
return CL_EPARSE;
}
if(fmap_readn(map, chunkid, offset, 4) != 4) {
cli_dbgmsg("PNG: EOF while reading chunk type\n");
return CL_EPARSE;
}
offset += 4;
/* GRR: add 4-character EBCDIC conversion here (chunkid) */
chunkid[4] = '\0';
++num_chunks;
if (check_chunk_name(chunkid) != 0)
return CL_EPARSE;
if (!have_IHDR && strcmp(chunkid,"IHDR")!=0)
{
cli_dbgmsg("PNG: first chunk must be IHDR\n");
return CL_EPARSE;
}
crc = update_crc(CRCINIT, (uch *)chunkid, 4);
toread = (sz > BS)? BS:sz;
if(toread && fmap_readn(map, buffer, offset, toread) != toread) {
cli_dbgmsg("PNG: EOF while reading data\n");
return CL_EPARSE;
}
offset += toread;
crc = update_crc(crc, (uch *)buffer, toread);
/*------*
| IHDR |
*------*/
if (strcmp(chunkid, "IHDR") == 0) {
if (have_IHDR) {
cli_dbgmsg("PNG: multiple IHDR not allowed\n");
return CL_EPARSE;
} else if (sz != 13) {
cli_dbgmsg("PNG: invalid IHDR length\n");
return CL_EPARSE;
} else {
int compr, filt;
w = LG(buffer);
h = LG(buffer+4);
if (w <= 0 || h <= 0 || w > 2147483647 || h > 2147483647) {
cli_dbgmsg("PNG: invalid image dimensions\n");
return CL_EPARSE;
}
bitdepth = sampledepth = (uch)buffer[8];
ityp = (uch)buffer[9];
if (ityp == 1 || ityp == 5 || ityp > sizeof(png_type)/sizeof(char*)) {
cli_dbgmsg("PNG: invalid image type (%d)\n", ityp);
return CL_EPARSE;
}
switch (sampledepth) {
case 1:
case 2:
case 4:
if (ityp == 2 || ityp == 4 || ityp == 6) { /* RGB or GA or RGBA */
cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
return CL_EPARSE;
}
break;
case 8:
break;
case 16:
if (ityp == 3) { /* palette */
cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
return CL_EPARSE;
}
break;
default:
cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
return CL_EPARSE;
}
compr = (uch)buffer[10];
if (compr > 127) {
cli_dbgmsg("PNG: private (invalid?) compression method (%d)\n", compr);
return CL_EPARSE;
} else if (compr > 0) {
cli_dbgmsg("PNG: invalid compression method (%d)\n", compr);
return CL_EPARSE;
}
filt = (uch)buffer[11];
if (filt > 127) {
cli_dbgmsg("PNG: private (invalid?) filter method (%d)\n", filt);
return CL_EPARSE;
} else if (filt > 0)
{
cli_dbgmsg("PNG: invalid filter method (%d)\n", filt);
return CL_EPARSE;
}
lace = (uch)buffer[12];
if (lace > 127) {
cli_dbgmsg("PNG: private (invalid?) interlace method (%d)\n", lace);
return CL_EPARSE;
} else if (lace > 1) {
cli_dbgmsg("PNG: invalid interlace method (%d)\n", lace);
return CL_EPARSE;
}
switch (ityp) {
case 2:
bitdepth = sampledepth * 3; /* RGB */
break;
case 4:
bitdepth = sampledepth * 2; /* gray+alpha */
break;
case 6:
bitdepth = sampledepth * 4; /* RGBA */
break;
}
}
have_IHDR = 1;
last_is_IDAT = last_is_JDAT = 0;
first_idat = 1; /* flag: next IDAT will be the first in this subimage */
zlib_error = 0; /* flag: no zlib errors yet in this file */
/* GRR 20000304: data dump not yet compatible with interlaced images: */
/*================================================*
* PNG chunks (with the exception of IHDR, above) *
*================================================*/
/*------*
| PLTE |
*------*/
} else if (strcmp(chunkid, "PLTE") == 0) {
if (have_PLTE) {
cli_dbgmsg("PNG: multiple PLTE not allowed\n");
return CL_EPARSE;
} else if (ityp != 3 && ityp != 2 && ityp != 6) {
cli_dbgmsg("PNG: PLTE not allowed in %s image\n", png_type[ityp]);
return CL_EPARSE;
} else if (have_IDAT) {
cli_dbgmsg("PNG: PLTE must precede IDAT\n");
return CL_EPARSE;
} else if (have_bKGD) {
cli_dbgmsg("PNG: PLTE must precede bKGD\n");
return CL_EPARSE;
} else if (sz > 768 || sz % 3 != 0) {
cli_dbgmsg("PNG: invalid number of PLTE entries (%g)\n", (double)sz / 3);
return CL_EPARSE;
} else {
nplte = sz / 3;
if (((bitdepth == 1 && nplte > 2) ||
(bitdepth == 2 && nplte > 4) || (bitdepth == 4 && nplte > 16)))
{
cli_dbgmsg("PNG: invalid number of PLTE entries (%d) for %d-bit image\n", nplte, bitdepth);
return CL_EPARSE;
}
}
if (ityp == 1) /* for MNG and tRNS */
ityp = 3;
have_PLTE = 1;
last_is_IDAT = last_is_JDAT = 0;
} else if (strcmp(chunkid, "IDAT") == 0) {
/* GRR FIXME: need to check for consecutive IDATs within MNG segments */
if (have_IDAT && !last_is_IDAT) {
cli_dbgmsg("PNG: IDAT chunks must be consecutive\n");
return CL_EPARSE;
} else if (ityp == 3 && !have_PLTE) {
cli_dbgmsg("PNG: IDAT must follow PLTE in %s image\n", png_type[ityp]);
return CL_EPARSE;
}
/* We just want to check that we have read at least the minimum (10)
* IDAT bytes possible, but avoid any overflow for short ints. We
* must also take into account that 0-length IDAT chunks are legal.
*/
if (have_IDAT <= 0)
have_IDAT = (sz > 0)? sz : -1; /* -1 as marker for IDAT(s), no data */
else if (have_IDAT < 10)
have_IDAT += (sz > 10)? 10 : sz; /* FIXME? could cap at 10 always */
/* Dump the zlib header from the first two bytes. */
if (zhead < 0x10000 && sz > 0) {
zhead = (zhead << 8) + buffer[0];
if (sz > 1 && zhead < 0x10000)
zhead = (zhead << 8) + buffer[1];
if (zhead >= 0x10000) {
/* formerly print_zlibheader(zhead & 0xffff); */
/* See the code in zlib deflate.c that writes out the header when
s->status is INIT_STATE. In fact this code is based on the zlib
specification in RFC 1950 (ftp://ds.internic.net/rfc/rfc1950.txt),
with the implicit assumption that the zlib header *is* written (it
always should be inside a valid PNG file). The variable names are
taken, verbatim, from the RFC. */
unsigned int CINFO = (zhead & 0xf000) >> 12;
unsigned int CM = (zhead & 0xf00) >> 8;
zlib_windowbits = CINFO + 8;
if((zhead & 0xffff) % 31) {
cli_dbgmsg("PNG: compression header fails checksum\n");
return CL_EPARSE;
} else if (CM != 8) {
cli_dbgmsg("PNG: non-deflate compression method (%d)\n", CM);
return CL_EPARSE;
}
}
}
if (check_zlib && !zlib_error) {
static uch *p; /* always points to next filter byte */
static int cur_y, cur_pass, cur_xoff, cur_yoff, cur_xskip, cur_yskip;
static long cur_width, cur_linebytes;
static long numfilt, numfilt_this_block, numfilt_total, numfilt_pass[7];
uch *eod;
int err=Z_OK;
zstrm.next_in = buffer;
zstrm.avail_in = toread;
/* initialize zlib and bit/byte/line variables if not already done */
if (first_idat) {
zstrm.next_out = p = outbuf;
zstrm.avail_out = BS;
zstrm.zalloc = (alloc_func)Z_NULL;
zstrm.zfree = (free_func)Z_NULL;
zstrm.opaque = (voidpf)Z_NULL;
if ((err = inflateInit2(&zstrm, zlib_windowbits)) != Z_OK) {
cli_dbgmsg("PNG: zlib: can't initialize (error = %d)\n", err);
return CL_EUNPACK;
}
cur_y = 0;
cur_pass = 1; /* interlace pass: 1 through 7 */
cur_xoff = cur_yoff = 0;
cur_xskip = cur_yskip = lace? 8 : 1;
cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip; /* round up */
cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1; /* round, fltr */
numfilt = 0L;
first_idat = 0;
if (lace) { /* loop through passes to calculate total filters */
int passm1, yskip=0, yoff=0, xoff=0;
for (passm1 = 0; passm1 < 7; ++passm1) {
switch (passm1) { /* (see table below for full summary) */
case 0: yskip = 8; yoff = 0; xoff = 0; break;
case 1: yskip = 8; yoff = 0; xoff = 4; break;
case 2: yskip = 8; yoff = 4; xoff = 0; break;
case 3: yskip = 4; yoff = 0; xoff = 2; break;
case 4: yskip = 4; yoff = 2; xoff = 0; break;
case 5: yskip = 2; yoff = 0; xoff = 1; break;
case 6: yskip = 2; yoff = 1; xoff = 0; break;
}
/* effective height is reduced if odd pass: subtract yoff (but
* if effective width of pass is 0 => no rows and no filters) */
numfilt_pass[passm1] =
(w <= xoff)? 0 : (h - yoff + yskip - 1) / yskip;
if (passm1 > 0) /* now make it cumulative */
numfilt_pass[passm1] += numfilt_pass[passm1 - 1];
}
} else {
numfilt_pass[0] = h; /* if non-interlaced */
numfilt_pass[1] = numfilt_pass[2] = numfilt_pass[3] = h;
numfilt_pass[4] = numfilt_pass[5] = numfilt_pass[6] = h;
}
numfilt_total = numfilt_pass[6];
}
numfilt_this_block = 0L;
while (err != Z_STREAM_END && zstrm.avail_in > 0) {
/* know zstrm.avail_out > 0: get some image/filter data */
err = inflate(&zstrm, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END) {
cli_dbgmsg("PNG: zlib: inflate error\n");
inflateEnd(&zstrm);
return CL_EPARSE;
}
/* now have uncompressed, filtered image data in outbuf */
eod = outbuf + BS - zstrm.avail_out;
while (p < eod) {
if (cur_linebytes) { /* GRP 20000727: bugfix */
int filttype = p[0];
if (filttype > 127) {
if (lace > 1)
break; /* assume it's due to unknown interlace method */
if (numfilt_this_block == 0) {
/* warn only on first one per block; don't break */
cli_dbgmsg("PNG: private (invalid?) row-filter type (%d)\n", filttype);
inflateEnd(&zstrm);
return CL_EPARSE;
}
} else if (filttype > 4) {
if (lace <= 1) {
cli_dbgmsg("PNG: invalid row-filter type (%d)\n", filttype);
inflateEnd(&zstrm);
return CL_EPARSE;
} /* else assume it's due to unknown interlace method */
break;
}
++numfilt;
p += cur_linebytes;
}
cur_y += cur_yskip;
if (lace) {
while (cur_y >= h) { /* may loop if very short image */
/*
pass xskip yskip xoff yoff
1 8 8 0 0
2 8 8 4 0
3 4 8 0 4
4 4 4 2 0
5 2 4 0 2
6 2 2 1 0
7 1 2 0 1
*/
++cur_pass;
if (cur_pass & 1) { /* beginning an odd pass */
cur_yoff = cur_xoff;
cur_xoff = 0;
cur_xskip >>= 1;
} else { /* beginning an even pass */
if (cur_pass == 2)
cur_xoff = 4;
else {
cur_xoff = cur_yoff >> 1;
cur_yskip >>= 1;
}
cur_yoff = 0;
}
cur_y = cur_yoff;
/* effective width is reduced if even pass: subtract cur_xoff */
cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip;
cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1;
if (cur_linebytes == 1) /* just the filter byte? no can do */
cur_linebytes = 0; /* GRP 20000727: added fix */
}
} else if (cur_y >= h) {
inflateEnd(&zstrm);
if(eod - p > 0) {
cli_dbgmsg("PNG: %li bytes remaining in buffer before inflateEnd()", eod-p);
return CL_EPARSE;
}
err = Z_STREAM_END;
zlib_error = 1;
}
}
p -= (eod - outbuf); /* wrap p back into outbuf region */
zstrm.next_out = outbuf;
zstrm.avail_out = BS;
/* get more input (waiting until buffer empties is not necessary best
* zlib strategy, but simpler than shifting leftover data around) */
if (zstrm.avail_in == 0 && sz > toread) {
int data_read;
sz -= toread;
toread = (sz > BS)? BS:sz;
if((data_read = fmap_readn(map, buffer, offset, toread)) != toread) {
cli_dbgmsg("PNG: EOF while reading %s data\n", chunkid);
return CL_EPARSE;
}
offset += toread;
crc = update_crc(crc, buffer, toread);
zstrm.next_in = buffer;
zstrm.avail_in = toread;
}
}
}
last_is_IDAT = 1;
last_is_JDAT = 0;
/*------*
| IEND |
*------*/
} else if (strcmp(chunkid, "IEND") == 0) {
if (have_IEND) {
cli_dbgmsg("PNG: multiple IEND not allowed\n");
return CL_EPARSE;
} else if (sz != 0) {
cli_dbgmsg("PNG: invalid IEND length\n");
return CL_EPARSE;
} else if (have_IDAT <= 0) {
cli_dbgmsg("PNG: no IDAT chunks\n");
return CL_EPARSE;
} else if (have_IDAT < 10) {
cli_dbgmsg("PNG: not enough IDAT data\n");
return CL_EPARSE;
}
have_IEND = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| bKGD |
*------*/
} else if (strcmp(chunkid, "bKGD") == 0) {
if (have_bKGD) {
cli_dbgmsg("PNG: multiple bKGD not allowed\n");
return CL_EPARSE;
} else if ((have_IDAT || have_JDAT)) {
cli_dbgmsg("PNG: bKGD must precede IDAT\n");
return CL_EPARSE;
}
switch (ityp) {
case 0:
case 4:
if (sz != 2) {
cli_dbgmsg("PNG: invalid bKGD length\n");
return CL_EPARSE;
}
break;
case 1: /* MNG top-level chunk (default values): "as if 16-bit RGBA" */
case 2:
case 6:
if (sz != 6) {
cli_dbgmsg("PNG: invalid bKGD length\n");
return CL_EPARSE;
}
break;
case 3:
if (sz != 1) {
cli_dbgmsg("PNG: invalid bKGD length\n");
return CL_EPARSE;
} else if (buffer[0] >= nplte) {
cli_dbgmsg("PNG: bKGD index falls outside PLTE\n");
return CL_EPARSE;
}
break;
}
have_bKGD = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| cHRM |
*------*/
} else if (strcmp(chunkid, "cHRM") == 0) {
if (have_cHRM) {
cli_dbgmsg("PNG: multiple cHRM not allowed\n");
return CL_EPARSE;
} else if (have_PLTE) {
cli_dbgmsg("PNG: cHRM must precede PLTE\n");
return CL_EPARSE;
} else if ((have_IDAT || have_JDAT)) {
cli_dbgmsg("PNG: cHRM must precede IDAT\n");
return CL_EPARSE;
} else if (sz != 32) {
cli_dbgmsg("PNG: invalid cHRM length\n");
return CL_EPARSE;
} else {
double wx, wy, rx, ry, gx, gy, bx, by;
wx = (double)LG(buffer)/100000;
wy = (double)LG(buffer+4)/100000;
rx = (double)LG(buffer+8)/100000;
ry = (double)LG(buffer+12)/100000;
gx = (double)LG(buffer+16)/100000;
gy = (double)LG(buffer+20)/100000;
bx = (double)LG(buffer+24)/100000;
by = (double)LG(buffer+28)/100000;
if (wx < 0 || wx > 0.8 || wy < 0 || wy > 0.8 || wx + wy > 1.0) {
cli_dbgmsg("PNG: invalid cHRM white point\n");
return CL_EPARSE;
} else if (rx < 0 || rx > 0.8 || ry < 0 || ry > 0.8 || rx + ry > 1.0) {
cli_dbgmsg("PNG: invalid cHRM red point\n");
return CL_EPARSE;
} else if (gx < 0 || gx > 0.8 || gy < 0 || gy > 0.8 || gx + gy > 1.0) {
cli_dbgmsg("PNG: invalid cHRM green point\n");
return CL_EPARSE;
} else if (bx < 0 || bx > 0.8 || by < 0 || by > 0.8 || bx + by > 1.0) {
cli_dbgmsg("PNG: invalid cHRM blue point\n");
return CL_EPARSE;
}
}
have_cHRM = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| fRAc |
*------*/
} else if (strcmp(chunkid, "fRAc") == 0) {
last_is_IDAT = last_is_JDAT = 0;
/*------*
| gAMA |
*------*/
} else if (strcmp(chunkid, "gAMA") == 0) {
if (have_gAMA) {
cli_dbgmsg("PNG: multiple gAMA not allowed\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: gAMA must precede IDAT\n");
return CL_EPARSE;
} else if (have_PLTE) {
cli_dbgmsg("PNG: gAMA must precede PLTE\n");
return CL_EPARSE;
} else if (sz != 4) {
cli_dbgmsg("PNG: invalid gAMA length\n");
return CL_EPARSE;
} else if (LG(buffer) == 0) {
cli_dbgmsg("PNG: invalid gAMA value (0.0000)\n");
return CL_EPARSE;
}
have_gAMA = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| gIFg |
*------*/
} else if (strcmp(chunkid, "gIFg") == 0) {
if (sz != 4) {
cli_dbgmsg("PNG: invalid gIFg length\n");
return CL_EPARSE;
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| gIFt |
*------*/
} else if (strcmp(chunkid, "gIFt") == 0) {
if (sz < 24) {
cli_dbgmsg("PNG: invalid gIFt length\n");
return CL_EPARSE;
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| gIFx |
*------*/
} else if (strcmp(chunkid, "gIFx") == 0) {
if (sz < 11) {
cli_dbgmsg("PNG: invalid gIFx length\n");
return CL_EPARSE;
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| hIST |
*------*/
} else if (strcmp(chunkid, "hIST") == 0) {
if (have_hIST) {
cli_dbgmsg("PNG: multiple hIST not allowed\n");
return CL_EPARSE;
} else if (!have_PLTE) {
cli_dbgmsg("PNG: hIST must follow PLTE\n");
return CL_EPARSE;
} else if (have_IDAT) {
cli_dbgmsg("PNG: hIST must precede IDAT\n");
return CL_EPARSE;
} else if (sz != nplte * 2) {
cli_dbgmsg("PNG: invalid number of hIST entries (%g)\n", (double)sz / 2);
return CL_EPARSE;
}
have_hIST = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| iCCP |
*------*/
} else if (strcmp(chunkid, "iCCP") == 0) {
int name_len;
if (have_iCCP) {
cli_dbgmsg("PNG: multiple iCCP not allowed\n");
return CL_EPARSE;
} else if (have_sRGB) {
cli_dbgmsg("PNG: iCCP not allowed with sRGB\n");
return CL_EPARSE;
} else if (have_PLTE) {
cli_dbgmsg("PNG: iCCP must precede PLTE\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: iCCP must precede IDAT\n");
return CL_EPARSE;
} else if (check_keyword(buffer, toread, &name_len)) {
return CL_EPARSE;
} else {
int remainder = toread - name_len - 3;
uch compr = buffer[name_len+1];
if (remainder < 0) {
cli_dbgmsg("PNG: invalid iCCP length\n");
return CL_EPARSE;
} else if (buffer[name_len] != 0) {
cli_dbgmsg("PNG: missing NULL after iCCP profile name\n");
return CL_EPARSE;
} else if (compr > 0 && compr < 128) {
cli_dbgmsg("PNG: invalid iCCP compression method (%d)\n", compr);
return CL_EPARSE;
} else if (compr >= 128) {
return CL_EPARSE;
}
}
have_iCCP = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| iTXt |
*------*/
} else if (strcmp(chunkid, "iTXt") == 0) {
int keylen;
if (check_keyword(buffer, toread, &keylen))
return CL_EPARSE;
else {
int compressed = 0, compr = 0;
if(keylen + 1 >= BS)
return CL_EPARSE;
compressed = buffer[keylen+1];
if (compressed < 0 || compressed > 1) {
cli_dbgmsg("PNG: invalid iTXt compression flag (%d)\n", compressed);
return CL_EPARSE;
} else if ((compr = (uch)buffer[keylen+2]) > 127) {
cli_dbgmsg("PNG: private (invalid?) iTXt compression method (%d)\n", compr);
return CL_EPARSE;
} else if (compr > 0) {
cli_dbgmsg("PNG: invalid iTXt compression method (%d)\n", compr);
return CL_EPARSE;
}
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| oFFs |
*------*/
} else if (strcmp(chunkid, "oFFs") == 0) {
if (have_oFFs) {
cli_dbgmsg("PNG: multiple oFFs not allowed\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: oFFs must precede IDAT\n");
return CL_EPARSE;
} else if (sz != 9) {
cli_dbgmsg("PNG: invalid oFFs length\n");
return CL_EPARSE;
} else if (buffer[8] > 1) {
cli_dbgmsg("PNG: invalid oFFs unit specifier (%u)\n", buffer[8]);
return CL_EPARSE;
}
have_oFFs = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| pCAL |
*------*/
} else if (strcmp(chunkid, "pCAL") == 0) {
if (have_pCAL) {
cli_dbgmsg("PNG: multiple pCAL not allowed\n");
return CL_EPARSE;
} else if (have_IDAT) {
cli_dbgmsg("PNG: pCAL must precede IDAT\n");
return CL_EPARSE;
}
have_pCAL = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| pHYs |
*------*/
} else if (strcmp(chunkid, "pHYs") == 0) {
if (have_pHYs) {
cli_dbgmsg("PNG: multiple pHYs not allowed\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: pHYS must precede DAT\n");
return CL_EPARSE;
} else if (sz != 9) {
cli_dbgmsg("PNG: invalid pHYS length\n");
return CL_EPARSE;
} else if (buffer[8] > 1) {
cli_dbgmsg("PNG: invalid pHYs unit specifier (%u)\n", buffer[8]);
return CL_EPARSE;
}
have_pHYs = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| sBIT |
*------*/
} else if (strcmp(chunkid, "sBIT") == 0) {
int maxbits = (ityp == 3)? 8 : sampledepth;
if (have_sBIT) {
cli_dbgmsg("PNG: multiple sBIT not allowed\n");
return CL_EPARSE;
} else if (have_PLTE) {
cli_dbgmsg("PNG: sBIT must precede PLTE\n");
return CL_EPARSE;
} else if (have_IDAT) {
cli_dbgmsg("PNG: sBIT must precede IDAT\n");
return CL_EPARSE;
}
switch (ityp) {
case 0:
if (sz != 1) {
cli_dbgmsg("PNG: invalid sBIT length\n");
return CL_EPARSE;
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
cli_dbgmsg("PNG: sBIT grey bits invalid for sample image\n");
return CL_EPARSE;
}
break;
case 2:
case 3:
if (sz != 3) {
cli_dbgmsg("PNG: invalid sBIT length\n");
return CL_EPARSE;
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
cli_dbgmsg("PNG: sBIT red bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
cli_dbgmsg("PNG: sBIT green bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[2] == 0 || buffer[2] > maxbits) {
cli_dbgmsg("PNG: sBIT blue bits invalid for sample image\n");
return CL_EPARSE;
}
break;
case 4:
if (sz != 2) {
cli_dbgmsg("PNG: invalid length\n");
return CL_EPARSE;
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
cli_dbgmsg("PNG: grey bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
cli_dbgmsg("PNG: alpha bits invalid for sample image\n");
return CL_EPARSE;
}
break;
case 6:
if (sz != 4) {
cli_dbgmsg("PNG: invalid sBIT length\n");
return CL_EPARSE;
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
cli_dbgmsg("PNG: red bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
cli_dbgmsg("PNG: green bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[2] == 0 || buffer[2] > maxbits) {
cli_dbgmsg("PNG: blue bits invalid for sample image\n");
return CL_EPARSE;
} else if (buffer[3] == 0 || buffer[3] > maxbits) {
cli_dbgmsg("PNG: alpha bits invalid for sample image\n");
return CL_EPARSE;
}
break;
}
have_sBIT = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| sCAL |
*------*/
} else if (strcmp(chunkid, "sCAL") == 0) {
int unittype = buffer[0];
uch *pPixwidth = buffer+1, *pPixheight=NULL;
if (have_sCAL) {
cli_dbgmsg("PNG: multiple sCAL not allowed\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: sCAL must precede IDAT\n");
return CL_EPARSE;
} else if (sz < 4) {
cli_dbgmsg("PNG: invalid sCAL length\n");
return CL_EPARSE;
} else if (unittype < 1 || unittype > 2) {
cli_dbgmsg("PNG: invalid sCAL unit specifier (%d)\n", unittype);
return CL_EPARSE;
} else {
uch *qq;
for (qq = pPixwidth; qq < buffer+sz; ++qq) {
if (*qq == 0)
break;
}
if (qq == buffer+sz) {
cli_dbgmsg("PNG: missing sCAL null separator\n");
return CL_EPARSE;
} else {
pPixheight = qq + 1;
if (pPixheight == buffer+sz || *pPixheight == 0) {
cli_dbgmsg("PNG: missing sCAL pixel height\n");
return CL_EPARSE;
}
}
for (qq = pPixheight; qq < buffer+sz; ++qq) {
if (*qq == 0)
break;
}
if (qq != buffer+sz) {
cli_dbgmsg("PNG: extra sCAL null separator\n");
return CL_EPARSE;
}
if (*pPixwidth == '-' || *pPixheight == '-') {
cli_dbgmsg("PNG: invalid negative sCAL value(s)\n");
return CL_EPARSE;
} else if (check_ascii_float(pPixwidth, pPixheight-pPixwidth-1) ||
check_ascii_float(pPixheight, buffer+sz-pPixheight))
{
return CL_EPARSE;
}
}
have_sCAL = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| sPLT |
*------*/
} else if (strcmp(chunkid, "sPLT") == 0) {
int name_len;
if (have_IDAT) {
cli_dbgmsg("PNG: sPLT must precede IDAT\n");
return CL_EPARSE;
} else if (check_keyword(buffer, toread, &name_len)) {
return CL_EPARSE;
} else {
uch bps = buffer[name_len+1];
int remainder = toread - name_len - 2;
int bytes = (bps >> 3);
int entry_sz = 4*bytes + 2;
if (remainder < 0) {
cli_dbgmsg("PNG: invalid sPLT length\n");
return CL_EPARSE;
} else if (buffer[name_len] != 0) {
cli_dbgmsg("PNG: missing NULL after sPLT palette name\n");
return CL_EPARSE;
} else if (bps != 8 && bps != 16) {
cli_dbgmsg("PNG: invalid sPLT sample depth\n");
return CL_EPARSE;
} else if (remainder % entry_sz != 0) {
cli_dbgmsg("PNG: invalid number of sPLT entries\n");
return CL_EPARSE;
}
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| sRGB |
*------*/
} else if (strcmp(chunkid, "sRGB") == 0) {
if (have_sRGB) {
cli_dbgmsg("PNG: multiple sRGB not allowed\n");
return CL_EPARSE;
} else if (have_iCCP) {
cli_dbgmsg("PNG: sRGB not allowed with iCCP\n");
return CL_EPARSE;
} else if (have_PLTE) {
cli_dbgmsg("PNG: sRGB must precede PLTE\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: sRGB must precede IDAT\n");
return CL_EPARSE;
} else if (sz != 1) {
cli_dbgmsg("PNG: invalid sRGB length\n");
return CL_EPARSE;
} else if (buffer[0] > 3) {
cli_dbgmsg("PNG: sRGB invalid rendering intent\n");
return CL_EPARSE;
}
have_sRGB = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| sTER |
*------*/
} else if (strcmp(chunkid, "sTER") == 0) {
if (have_sTER) {
cli_dbgmsg("PNG: multiple sTER not allowed\n");
return CL_EPARSE;
} else if (have_IDAT || have_JDAT) {
cli_dbgmsg("PNG: sTER must precede IDAT\n");
return CL_EPARSE;
} else if (sz != 1) {
cli_dbgmsg("PNG: invalid sTER length\n");
return CL_EPARSE;
} else if (buffer[0] > 1) {
cli_dbgmsg("PNG: invalid sTER layout mode\n");
return CL_EPARSE;
}
have_sTER = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------* *------*
| tEXt | | zTXt |
*------* *------*/
} else if (strcmp(chunkid, "tEXt") == 0 || strcmp(chunkid, "zTXt") == 0) {
int ztxt = (chunkid[0] == 'z');
int keylen;
if (check_keyword(buffer, toread, &keylen))
return CL_EPARSE;
else if (ztxt) {
int compr = (uch)buffer[keylen+1];
if (compr > 127) {
cli_dbgmsg("PNG: private (possibly invalid) compression method\n");
return CL_EPARSE;
} else if (compr > 0) {
cli_dbgmsg("PNG: invalid compression method\n");
return CL_EPARSE;
}
}
else if (check_text(buffer + keylen + 1, toread - keylen - 1)) {
return CL_EPARSE;
}
last_is_IDAT = last_is_JDAT = 0;
/*------*
| tIME |
*------*/
} else if (strcmp(chunkid, "tIME") == 0) {
if (have_tIME) {
cli_dbgmsg("PNG: multiple tIME not allowed\n");
return CL_EPARSE;
} else if (sz != 7) {
cli_dbgmsg("PNG: invalid tIME length\n");
return CL_EPARSE;
} else {
int yr = SH(buffer);
int mo = buffer[2];
int dy = buffer[3];
int hh = buffer[4];
int mm = buffer[5];
int ss = buffer[6];
if (yr < 1995) {
/* conversion to PNG format counts as modification... */
/* FIXME: also test for future dates? (may allow current year + 1) */
cli_dbgmsg("PNG: invalid year\n");
return CL_EPARSE;
} else if (mo < 1 || mo > 12) {
cli_dbgmsg("PNG: invalid month\n");
return CL_EPARSE;
} else if (dy < 1 || dy > 31) {
/* FIXME: also validate day given specified month? */
cli_dbgmsg("PNG: invalid day\n");
return CL_EPARSE;
} else if (hh < 0 || hh > 23) {
cli_dbgmsg("PNG: invalid hour\n");
return CL_EPARSE;
} else if (mm < 0 || mm > 59) {
cli_dbgmsg("PNG: invalid minute\n");
return CL_EPARSE;
} else if (ss < 0 || ss > 60) {
cli_dbgmsg("PNG: invalid second\n");
return CL_EPARSE;
}
cli_dbgmsg("PNG: Time: %2d %s %4d %02d:%02d:%02d UTC\n", dy, getmonth(mo), yr, hh, mm, ss);
}
have_tIME = 1;
last_is_IDAT = last_is_JDAT = 0;
/*------*
| tRNS |
*------*/
} else if (strcmp(chunkid, "tRNS") == 0) {
if (have_tRNS) {
cli_dbgmsg("PNG: multiple tRNS not allowed\n");
return CL_EPARSE;
} else if (ityp == 3 && !have_PLTE) {
cli_dbgmsg("PNG: tRNS must follow PLTE\n");
return CL_EPARSE;
} else if (have_IDAT) {
cli_dbgmsg("PNG: tRNS must precede IDAT\n");
return CL_EPARSE;
} else {
switch (ityp) {
case 0:
if (sz != 2) {
cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
return CL_EPARSE;
}
break;
case 2:
if (sz != 6) {
cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
return CL_EPARSE;
}
break;
case 3:
if (sz > nplte) {
cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
return CL_EPARSE;
}
break;
default:
cli_dbgmsg("PNG: tRNS not allowed in %s image\n", png_type[ityp]);
return CL_EPARSE;
break;
}
}
have_tRNS = 1;
last_is_IDAT = last_is_JDAT = 0;
/*===============*
* unknown chunk *
*===============*/
} else {
if (CRITICAL(chunkid) && SAFECOPY(chunkid)) {
/* a critical, safe-to-copy chunk is an error */
cli_dbgmsg("PNG: illegal critical, safe-to-copy chunk\n");
return CL_EPARSE;
} else if (RESERVED(chunkid)) {
/* a chunk with the reserved bit set is an error (or spec updated) */
cli_dbgmsg("PNG: illegal reserved-bit-set chunk\n");
return CL_EPARSE;
} else if (PUBLIC(chunkid)) {
/* GRR 20050725: all registered (public) PNG/MNG/JNG chunks are now
* known to pngcheck, so any unknown public ones are invalid (or have
* been proposed and approved since the last release of pngcheck) */
cli_dbgmsg("PNG: illegal (unless recently approved) unknown, public\n");
return CL_EPARSE;
} else if (/* !PUBLIC(chunkid) && */ CRITICAL(chunkid)) {
cli_dbgmsg("PNG: private, critical chunk (warning)\n");
return CL_EPARSE; /* not an error if used only internally */
}
last_is_IDAT = last_is_JDAT = 0;
}
while (sz > toread) {
int data_read;
sz -= toread;
toread = (sz > BS)? BS:sz;
data_read = fmap_readn(map, buffer, offset, toread);
if (data_read != toread) {
cli_dbgmsg("PNG: EOF while reading final data\n");
return CL_EPARSE;
}
offset += toread;
crc = update_crc(crc, (uch *)buffer, toread);
}
filecrc = getlong(map, &offset, "CRC value");
if (filecrc != CRCCOMPL(crc)) {
cli_dbgmsg("PNG: CRC error in chunk %s (computed %08lx, expected %08lx)\n",
chunkid, CRCCOMPL(crc), filecrc);
return CL_EPARSE;
}
}
/*----------------------- END OF IMMENSE WHILE-LOOP -----------------------*/
if (!have_IEND) {
cli_dbgmsg("PNG: file doesn't end with a IEND chunk\n");
return CL_EPARSE;
}
return CL_SUCCESS;
}