1
2/* pngunknown.c - test the read side unknown chunk handling
3 *
4 * Last changed in libpng 1.6.22 [May 26, 2016]
5 * Copyright (c) 2015,2016 Glenn Randers-Pehrson
6 * Written by John Cunningham Bowler
7 *
8 * This code is released under the libpng license.
9 * For conditions of distribution and use, see the disclaimer
10 * and license in png.h
11 *
12 * NOTES:
13 *   This is a C program that is intended to be linked against libpng.  It
14 *   allows the libpng unknown handling code to be tested by interpreting
15 *   arguments to save or discard combinations of chunks.  The program is
16 *   currently just a minimal validation for the built-in libpng facilities.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <stdio.h>
22#include <setjmp.h>
23
24/* Define the following to use this test against your installed libpng, rather
25 * than the one being built here:
26 */
27#ifdef PNG_FREESTANDING_TESTS
28#  include <png.h>
29#else
30#  include "../../png.h"
31#endif
32
33/* 1.6.1 added support for the configure test harness, which uses 77 to indicate
34 * a skipped test, in earlier versions we need to succeed on a skipped test, so:
35 */
36#if PNG_LIBPNG_VER >= 10601 && defined(HAVE_CONFIG_H)
37#  define SKIP 77
38#else
39#  define SKIP 0
40#endif
41
42
43/* Since this program tests the ability to change the unknown chunk handling
44 * these must be defined:
45 */
46#if defined(PNG_SET_UNKNOWN_CHUNKS_SUPPORTED) &&\
47   defined(PNG_STDIO_SUPPORTED) &&\
48   defined(PNG_READ_SUPPORTED)
49
50/* One of these must be defined to allow us to find out what happened.  It is
51 * still useful to set unknown chunk handling without either of these in order
52 * to cause *known* chunks to be discarded.  This can be a significant
53 * efficiency gain, but it can't really be tested here.
54 */
55#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) ||\
56   defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
57
58#if PNG_LIBPNG_VER < 10500
59/* This deliberately lacks the PNG_CONST. */
60typedef png_byte *png_const_bytep;
61
62/* This is copied from 1.5.1 png.h: */
63#define PNG_INTERLACE_ADAM7_PASSES 7
64#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
65#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
66#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
67#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
68#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
69   -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
70#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
71   -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
72#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
73   (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
74#define PNG_COL_FROM_PASS_COL(xIn, pass) \
75   (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
76#define PNG_PASS_MASK(pass,off) ( \
77   ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
78   ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
79#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
80   ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
81#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
82   ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
83
84/* These are needed too for the default build: */
85#define PNG_WRITE_16BIT_SUPPORTED
86#define PNG_READ_16BIT_SUPPORTED
87
88/* This comes from pnglibconf.h afer 1.5: */
89#define PNG_FP_1 100000
90#define PNG_GAMMA_THRESHOLD_FIXED\
91   ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
92#endif
93
94#if PNG_LIBPNG_VER < 10600
95   /* 1.6.0 constifies many APIs. The following exists to allow pngvalid to be
96    * compiled against earlier versions.
97    */
98#  define png_const_structp png_structp
99#endif
100
101#if PNG_LIBPNG_VER < 10700
102   /* Copied from libpng 1.7.0 png.h */
103#define PNG_u2(b1, b2) (((unsigned int)(b1) << 8) + (b2))
104
105#define PNG_U16(b1, b2) ((png_uint_16)PNG_u2(b1, b2))
106#define PNG_U32(b1, b2, b3, b4)\
107   (((png_uint_32)PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4))
108
109/* Constants for known chunk types.
110 */
111#define png_IDAT PNG_U32( 73,  68,  65,  84)
112#define png_IEND PNG_U32( 73,  69,  78,  68)
113#define png_IHDR PNG_U32( 73,  72,  68,  82)
114#define png_PLTE PNG_U32( 80,  76,  84,  69)
115#define png_bKGD PNG_U32( 98,  75,  71,  68)
116#define png_cHRM PNG_U32( 99,  72,  82,  77)
117#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
118#define png_gAMA PNG_U32(103,  65,  77,  65)
119#define png_gIFg PNG_U32(103,  73,  70, 103)
120#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */
121#define png_gIFx PNG_U32(103,  73,  70, 120)
122#define png_hIST PNG_U32(104,  73,  83,  84)
123#define png_iCCP PNG_U32(105,  67,  67,  80)
124#define png_iTXt PNG_U32(105,  84,  88, 116)
125#define png_oFFs PNG_U32(111,  70,  70, 115)
126#define png_pCAL PNG_U32(112,  67,  65,  76)
127#define png_pHYs PNG_U32(112,  72,  89, 115)
128#define png_sBIT PNG_U32(115,  66,  73,  84)
129#define png_sCAL PNG_U32(115,  67,  65,  76)
130#define png_sPLT PNG_U32(115,  80,  76,  84)
131#define png_sRGB PNG_U32(115,  82,  71,  66)
132#define png_sTER PNG_U32(115,  84,  69,  82)
133#define png_tEXt PNG_U32(116,  69,  88, 116)
134#define png_tIME PNG_U32(116,  73,  77,  69)
135#define png_tRNS PNG_U32(116,  82,  78,  83)
136#define png_zTXt PNG_U32(122,  84,  88, 116)
137
138/* Test on flag values as defined in the spec (section 5.4): */
139#define PNG_CHUNK_ANCILLARY(c)    (1 & ((c) >> 29))
140#define PNG_CHUNK_CRITICAL(c)     (!PNG_CHUNK_ANCILLARY(c))
141#define PNG_CHUNK_PRIVATE(c)      (1 & ((c) >> 21))
142#define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))
143#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))
144
145#endif /* PNG_LIBPNG_VER < 10700 */
146
147#ifdef __cplusplus
148#  define this not_the_cpp_this
149#  define new not_the_cpp_new
150#  define voidcast(type, value) static_cast<type>(value)
151#else
152#  define voidcast(type, value) (value)
153#endif /* __cplusplus */
154
155/* Unused formal parameter errors are removed using the following macro which is
156 * expected to have no bad effects on performance.
157 */
158#ifndef UNUSED
159#  if defined(__GNUC__) || defined(_MSC_VER)
160#     define UNUSED(param) (void)param;
161#  else
162#     define UNUSED(param)
163#  endif
164#endif
165
166/* Types of chunks not known to libpng */
167#define png_vpAg PNG_U32(118, 112, 65, 103)
168
169/* Chunk information */
170#define PNG_INFO_tEXt 0x10000000U
171#define PNG_INFO_iTXt 0x20000000U
172#define PNG_INFO_zTXt 0x40000000U
173
174#define PNG_INFO_sTER 0x01000000U
175#define PNG_INFO_vpAg 0x02000000U
176
177#define ABSENT  0
178#define START   1
179#define END     2
180
181static struct
182{
183   char        name[5];
184   png_uint_32 flag;
185   png_uint_32 tag;
186   int         unknown;    /* Chunk not known to libpng */
187   int         all;        /* Chunk set by the '-1' option */
188   int         position;   /* position in pngtest.png */
189   int         keep;       /* unknown handling setting */
190} chunk_info[] = {
191   /* Critical chunks */
192   { "IDAT", PNG_INFO_IDAT, png_IDAT, 0, 0,  START, 0 }, /* must be [0] */
193   { "PLTE", PNG_INFO_PLTE, png_PLTE, 0, 0, ABSENT, 0 },
194
195   /* Non-critical chunks that libpng handles */
196   /* This is a mess but it seems to be the only way to do it - there is no way
197    * to check for a definition outside a #if.
198    */
199   { "bKGD", PNG_INFO_bKGD, png_bKGD,
200#     ifdef PNG_READ_bKGD_SUPPORTED
201         0,
202#     else
203         1,
204#     endif
205      1,  START, 0 },
206   { "cHRM", PNG_INFO_cHRM, png_cHRM,
207#     ifdef PNG_READ_cHRM_SUPPORTED
208         0,
209#     else
210         1,
211#     endif
212      1,  START, 0 },
213   { "gAMA", PNG_INFO_gAMA, png_gAMA,
214#     ifdef PNG_READ_gAMA_SUPPORTED
215         0,
216#     else
217         1,
218#     endif
219      1,  START, 0 },
220   { "hIST", PNG_INFO_hIST, png_hIST,
221#     ifdef PNG_READ_hIST_SUPPORTED
222         0,
223#     else
224         1,
225#     endif
226      1, ABSENT, 0 },
227   { "iCCP", PNG_INFO_iCCP, png_iCCP,
228#     ifdef PNG_READ_iCCP_SUPPORTED
229         0,
230#     else
231         1,
232#     endif
233      1, ABSENT, 0 },
234   { "iTXt", PNG_INFO_iTXt, png_iTXt,
235#     ifdef PNG_READ_iTXt_SUPPORTED
236         0,
237#     else
238         1,
239#     endif
240      1, ABSENT, 0 },
241   { "oFFs", PNG_INFO_oFFs, png_oFFs,
242#     ifdef PNG_READ_oFFs_SUPPORTED
243         0,
244#     else
245         1,
246#     endif
247      1,  START, 0 },
248   { "pCAL", PNG_INFO_pCAL, png_pCAL,
249#     ifdef PNG_READ_pCAL_SUPPORTED
250         0,
251#     else
252         1,
253#     endif
254      1,  START, 0 },
255   { "pHYs", PNG_INFO_pHYs, png_pHYs,
256#     ifdef PNG_READ_pHYs_SUPPORTED
257         0,
258#     else
259         1,
260#     endif
261      1,  START, 0 },
262   { "sBIT", PNG_INFO_sBIT, png_sBIT,
263#     ifdef PNG_READ_sBIT_SUPPORTED
264         0,
265#     else
266         1,
267#     endif
268      1,  START, 0 },
269   { "sCAL", PNG_INFO_sCAL, png_sCAL,
270#     ifdef PNG_READ_sCAL_SUPPORTED
271         0,
272#     else
273         1,
274#     endif
275      1,  START, 0 },
276   { "sPLT", PNG_INFO_sPLT, png_sPLT,
277#     ifdef PNG_READ_sPLT_SUPPORTED
278         0,
279#     else
280         1,
281#     endif
282      1, ABSENT, 0 },
283   { "sRGB", PNG_INFO_sRGB, png_sRGB,
284#     ifdef PNG_READ_sRGB_SUPPORTED
285         0,
286#     else
287         1,
288#     endif
289      1,  START, 0 },
290   { "tEXt", PNG_INFO_tEXt, png_tEXt,
291#     ifdef PNG_READ_tEXt_SUPPORTED
292         0,
293#     else
294         1,
295#     endif
296      1,  START, 0 },
297   { "tIME", PNG_INFO_tIME, png_tIME,
298#     ifdef PNG_READ_tIME_SUPPORTED
299         0,
300#     else
301         1,
302#     endif
303      1,  START, 0 },
304   { "tRNS", PNG_INFO_tRNS, png_tRNS,
305#     ifdef PNG_READ_tRNS_SUPPORTED
306         0,
307#     else
308         1,
309#     endif
310      0, ABSENT, 0 },
311   { "zTXt", PNG_INFO_zTXt, png_zTXt,
312#     ifdef PNG_READ_zTXt_SUPPORTED
313         0,
314#     else
315         1,
316#     endif
317      1,    END, 0 },
318
319   /* No libpng handling */
320   { "sTER", PNG_INFO_sTER, png_sTER, 1, 1,  START, 0 },
321   { "vpAg", PNG_INFO_vpAg, png_vpAg, 1, 0,  START, 0 },
322};
323
324#define NINFO ((int)((sizeof chunk_info)/(sizeof chunk_info[0])))
325
326static void
327clear_keep(void)
328{
329   int i = NINFO;
330   while (--i >= 0)
331      chunk_info[i].keep = 0;
332}
333
334static int
335find(const char *name)
336{
337   int i = NINFO;
338   while (--i >= 0)
339   {
340      if (memcmp(chunk_info[i].name, name, 4) == 0)
341         break;
342   }
343
344   return i;
345}
346
347static int
348findb(const png_byte *name)
349{
350   int i = NINFO;
351   while (--i >= 0)
352   {
353      if (memcmp(chunk_info[i].name, name, 4) == 0)
354         break;
355   }
356
357   return i;
358}
359
360static int
361find_by_flag(png_uint_32 flag)
362{
363   int i = NINFO;
364
365   while (--i >= 0) if (chunk_info[i].flag == flag) return i;
366
367   fprintf(stderr, "pngunknown: internal error\n");
368   exit(4);
369}
370
371static int
372ancillary(const char *name)
373{
374   return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
375}
376
377#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
378static int
379ancillaryb(const png_byte *name)
380{
381   return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
382}
383#endif
384
385/* Type of an error_ptr */
386typedef struct
387{
388   jmp_buf     error_return;
389   png_structp png_ptr;
390   png_infop   info_ptr, end_ptr;
391   png_uint_32 before_IDAT;
392   png_uint_32 after_IDAT;
393   int         error_count;
394   int         warning_count;
395   int         keep; /* the default value */
396   const char *program;
397   const char *file;
398   const char *test;
399} display;
400
401static const char init[] = "initialization";
402static const char cmd[] = "command line";
403
404static void
405init_display(display *d, const char *program)
406{
407   memset(d, 0, sizeof *d);
408   d->png_ptr = NULL;
409   d->info_ptr = d->end_ptr = NULL;
410   d->error_count = d->warning_count = 0;
411   d->program = program;
412   d->file = program;
413   d->test = init;
414}
415
416static void
417clean_display(display *d)
418{
419   png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_ptr);
420
421   /* This must not happen - it might cause an app crash */
422   if (d->png_ptr != NULL || d->info_ptr != NULL || d->end_ptr != NULL)
423   {
424      fprintf(stderr, "%s(%s): png_destroy_read_struct error\n", d->file,
425         d->test);
426      exit(1);
427   }
428}
429
430PNG_FUNCTION(void, display_exit, (display *d), static PNG_NORETURN)
431{
432   ++(d->error_count);
433
434   if (d->png_ptr != NULL)
435      clean_display(d);
436
437   /* During initialization and if this is a single command line argument set
438    * exit now - there is only one test, otherwise longjmp to do the next test.
439    */
440   if (d->test == init || d->test == cmd)
441      exit(1);
442
443   longjmp(d->error_return, 1);
444}
445
446static int
447display_rc(const display *d, int strict)
448{
449   return d->error_count + (strict ? d->warning_count : 0);
450}
451
452/* libpng error and warning callbacks */
453PNG_FUNCTION(void, (PNGCBAPI error), (png_structp png_ptr, const char *message),
454   static PNG_NORETURN)
455{
456   display *d = (display*)png_get_error_ptr(png_ptr);
457
458   fprintf(stderr, "%s(%s): libpng error: %s\n", d->file, d->test, message);
459   display_exit(d);
460}
461
462static void PNGCBAPI
463warning(png_structp png_ptr, const char *message)
464{
465   display *d = (display*)png_get_error_ptr(png_ptr);
466
467   fprintf(stderr, "%s(%s): libpng warning: %s\n", d->file, d->test, message);
468   ++(d->warning_count);
469}
470
471static png_uint_32
472get_valid(display *d, png_infop info_ptr)
473{
474   png_uint_32 flags = png_get_valid(d->png_ptr, info_ptr, (png_uint_32)~0);
475
476   /* Map the text chunks back into the flags */
477   {
478      png_textp text;
479      png_uint_32 ntext = png_get_text(d->png_ptr, info_ptr, &text, NULL);
480
481      while (ntext-- > 0) switch (text[ntext].compression)
482      {
483         case -1:
484            flags |= PNG_INFO_tEXt;
485            break;
486         case 0:
487            flags |= PNG_INFO_zTXt;
488            break;
489         case 1:
490         case 2:
491            flags |= PNG_INFO_iTXt;
492            break;
493         default:
494            fprintf(stderr, "%s(%s): unknown text compression %d\n", d->file,
495               d->test, text[ntext].compression);
496            display_exit(d);
497      }
498   }
499
500   return flags;
501}
502
503#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
504static int PNGCBAPI
505read_callback(png_structp pp, png_unknown_chunkp pc)
506{
507   /* This function mimics the behavior of png_set_keep_unknown_chunks by
508    * returning '0' to keep the chunk and '1' to discard it.
509    */
510   display *d = voidcast(display*, png_get_user_chunk_ptr(pp));
511   int chunk = findb(pc->name);
512   int keep, discard;
513
514   if (chunk < 0) /* not one in our list, so not a known chunk */
515      keep = d->keep;
516
517   else
518   {
519      keep = chunk_info[chunk].keep;
520      if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
521      {
522         /* See the comments in png.h - use the default for unknown chunks,
523          * do not keep known chunks.
524          */
525         if (chunk_info[chunk].unknown)
526            keep = d->keep;
527
528         else
529            keep = PNG_HANDLE_CHUNK_NEVER;
530      }
531   }
532
533   switch (keep)
534   {
535      default:
536         fprintf(stderr, "%s(%s): %d: unrecognized chunk option\n", d->file,
537            d->test, chunk_info[chunk].keep);
538         display_exit(d);
539
540      case PNG_HANDLE_CHUNK_AS_DEFAULT:
541      case PNG_HANDLE_CHUNK_NEVER:
542         discard = 1/*handled; discard*/;
543         break;
544
545      case PNG_HANDLE_CHUNK_IF_SAFE:
546      case PNG_HANDLE_CHUNK_ALWAYS:
547         discard = 0/*not handled; keep*/;
548         break;
549   }
550
551   /* Also store information about this chunk in the display, the relevant flag
552    * is set if the chunk is to be kept ('not handled'.)
553    */
554   if (chunk >= 0) if (!discard) /* stupidity to stop a GCC warning */
555   {
556      png_uint_32 flag = chunk_info[chunk].flag;
557
558      if (pc->location & PNG_AFTER_IDAT)
559         d->after_IDAT |= flag;
560
561      else
562         d->before_IDAT |= flag;
563   }
564
565   /* However if there is no support to store unknown chunks don't ask libpng to
566    * do it; there will be an png_error.
567    */
568#  ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
569      return discard;
570#  else
571      return 1; /*handled; discard*/
572#  endif
573}
574#endif /* READ_USER_CHUNKS_SUPPORTED */
575
576#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
577static png_uint_32
578get_unknown(display *d, png_infop info_ptr, int after_IDAT)
579{
580   /* Create corresponding 'unknown' flags */
581   png_uint_32 flags = 0;
582
583   UNUSED(after_IDAT)
584
585   {
586      png_unknown_chunkp unknown;
587      int num_unknown = png_get_unknown_chunks(d->png_ptr, info_ptr, &unknown);
588
589      while (--num_unknown >= 0)
590      {
591         int chunk = findb(unknown[num_unknown].name);
592
593         /* Chunks not known to pngunknown must be validated here; since they
594          * must also be unknown to libpng the 'display->keep' behavior should
595          * have been used.
596          */
597         if (chunk < 0) switch (d->keep)
598         {
599            default: /* impossible */
600            case PNG_HANDLE_CHUNK_AS_DEFAULT:
601            case PNG_HANDLE_CHUNK_NEVER:
602               fprintf(stderr, "%s(%s): %s: %s: unknown chunk saved\n",
603                  d->file, d->test, d->keep ? "discard" : "default",
604                  unknown[num_unknown].name);
605               ++(d->error_count);
606               break;
607
608            case PNG_HANDLE_CHUNK_IF_SAFE:
609               if (!ancillaryb(unknown[num_unknown].name))
610               {
611                  fprintf(stderr,
612                     "%s(%s): if-safe: %s: unknown critical chunk saved\n",
613                     d->file, d->test, unknown[num_unknown].name);
614                  ++(d->error_count);
615                  break;
616               }
617               /* FALL THROUGH (safe) */
618            case PNG_HANDLE_CHUNK_ALWAYS:
619               break;
620         }
621
622         else
623            flags |= chunk_info[chunk].flag;
624      }
625   }
626
627   return flags;
628}
629#else /* SAVE_UNKNOWN_CHUNKS */
630static png_uint_32
631get_unknown(display *d, png_infop info_ptr, int after_IDAT)
632   /* Otherwise this will return the cached values set by any user callback */
633{
634   UNUSED(info_ptr);
635
636   if (after_IDAT)
637      return d->after_IDAT;
638
639   else
640      return d->before_IDAT;
641}
642
643#  ifndef PNG_READ_USER_CHUNKS_SUPPORTED
644      /* The #defines above should mean this is never reached, it's just here as
645       * a check to ensure the logic is correct.
646       */
647#     error No store support and no user chunk support, this will not work
648#  endif /* READ_USER_CHUNKS */
649#endif /* SAVE_UNKNOWN_CHUNKS */
650
651static int
652check(FILE *fp, int argc, const char **argv, png_uint_32p flags/*out*/,
653   display *d, int set_callback)
654{
655   int i, npasses, ipass;
656   png_uint_32 height;
657
658   d->keep = PNG_HANDLE_CHUNK_AS_DEFAULT;
659   d->before_IDAT = 0;
660   d->after_IDAT = 0;
661
662   /* Some of these errors are permanently fatal and cause an exit here, others
663    * are per-test and cause an error return.
664    */
665   d->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, d, error,
666      warning);
667   if (d->png_ptr == NULL)
668   {
669      fprintf(stderr, "%s(%s): could not allocate png struct\n", d->file,
670         d->test);
671      /* Terminate here, this error is not test specific. */
672      exit(1);
673   }
674
675   d->info_ptr = png_create_info_struct(d->png_ptr);
676   d->end_ptr = png_create_info_struct(d->png_ptr);
677   if (d->info_ptr == NULL || d->end_ptr == NULL)
678   {
679      fprintf(stderr, "%s(%s): could not allocate png info\n", d->file,
680         d->test);
681      clean_display(d);
682      exit(1);
683   }
684
685   png_init_io(d->png_ptr, fp);
686
687#  ifdef PNG_READ_USER_CHUNKS_SUPPORTED
688      /* This is only done if requested by the caller; it interferes with the
689       * standard store/save mechanism.
690       */
691      if (set_callback)
692         png_set_read_user_chunk_fn(d->png_ptr, d, read_callback);
693#  else
694      UNUSED(set_callback)
695#  endif
696
697   /* Handle each argument in turn; multiple settings are possible for the same
698    * chunk and multiple calls will occur (the last one should override all
699    * preceding ones).
700    */
701   for (i=0; i<argc; ++i)
702   {
703      const char *equals = strchr(argv[i], '=');
704
705      if (equals != NULL)
706      {
707         int chunk, option;
708
709         if (strcmp(equals+1, "default") == 0)
710            option = PNG_HANDLE_CHUNK_AS_DEFAULT;
711         else if (strcmp(equals+1, "discard") == 0)
712            option = PNG_HANDLE_CHUNK_NEVER;
713         else if (strcmp(equals+1, "if-safe") == 0)
714            option = PNG_HANDLE_CHUNK_IF_SAFE;
715         else if (strcmp(equals+1, "save") == 0)
716            option = PNG_HANDLE_CHUNK_ALWAYS;
717         else
718         {
719            fprintf(stderr, "%s(%s): %s: unrecognized chunk option\n", d->file,
720               d->test, argv[i]);
721            display_exit(d);
722         }
723
724         switch (equals - argv[i])
725         {
726            case 4: /* chunk name */
727               chunk = find(argv[i]);
728
729               if (chunk >= 0)
730               {
731                  /* These #if tests have the effect of skipping the arguments
732                   * if SAVE support is unavailable - we can't do a useful test
733                   * in this case, so we just check the arguments!  This could
734                   * be improved in the future by using the read callback.
735                   */
736#                 if PNG_LIBPNG_VER >= 10700 &&\
737                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
738                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
739#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
740                  {
741                     png_byte name[5];
742
743                     memcpy(name, chunk_info[chunk].name, 5);
744                     png_set_keep_unknown_chunks(d->png_ptr, option, name, 1);
745                     chunk_info[chunk].keep = option;
746                  }
747                  continue;
748               }
749
750               break;
751
752            case 7: /* default */
753               if (memcmp(argv[i], "default", 7) == 0)
754               {
755#                 if PNG_LIBPNG_VER >= 10700 &&\
756                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
757                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
758#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
759                     png_set_keep_unknown_chunks(d->png_ptr, option, NULL, 0);
760
761                  d->keep = option;
762                  continue;
763               }
764
765               break;
766
767            case 3: /* all */
768               if (memcmp(argv[i], "all", 3) == 0)
769               {
770#                 if PNG_LIBPNG_VER >= 10700 &&\
771                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
772                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
773#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
774                     png_set_keep_unknown_chunks(d->png_ptr, option, NULL, -1);
775
776                  d->keep = option;
777
778                  for (chunk = 0; chunk < NINFO; ++chunk)
779                     if (chunk_info[chunk].all)
780                        chunk_info[chunk].keep = option;
781                  continue;
782               }
783
784               break;
785
786            default: /* some misplaced = */
787
788               break;
789         }
790      }
791
792      fprintf(stderr, "%s(%s): %s: unrecognized chunk argument\n", d->file,
793         d->test, argv[i]);
794      display_exit(d);
795   }
796
797   png_read_info(d->png_ptr, d->info_ptr);
798
799   switch (png_get_interlace_type(d->png_ptr, d->info_ptr))
800   {
801      case PNG_INTERLACE_NONE:
802         npasses = 1;
803         break;
804
805      case PNG_INTERLACE_ADAM7:
806         npasses = PNG_INTERLACE_ADAM7_PASSES;
807         break;
808
809      default:
810         /* Hard error because it is not test specific */
811         fprintf(stderr, "%s(%s): invalid interlace type\n", d->file, d->test);
812         clean_display(d);
813         exit(1);
814   }
815
816   /* Skip the image data, if IDAT is not being handled then don't do this
817    * because it will cause a CRC error.
818    */
819   if (chunk_info[0/*IDAT*/].keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
820   {
821      png_start_read_image(d->png_ptr);
822      height = png_get_image_height(d->png_ptr, d->info_ptr);
823
824      if (npasses > 1)
825      {
826         png_uint_32 width = png_get_image_width(d->png_ptr, d->info_ptr);
827
828         for (ipass=0; ipass<npasses; ++ipass)
829         {
830            png_uint_32 wPass = PNG_PASS_COLS(width, ipass);
831
832            if (wPass > 0)
833            {
834               png_uint_32 y;
835
836               for (y=0; y<height; ++y) if (PNG_ROW_IN_INTERLACE_PASS(y, ipass))
837                  png_read_row(d->png_ptr, NULL, NULL);
838            }
839         }
840      } /* interlaced */
841
842      else /* not interlaced */
843      {
844         png_uint_32 y;
845
846         for (y=0; y<height; ++y)
847            png_read_row(d->png_ptr, NULL, NULL);
848      }
849   }
850
851   png_read_end(d->png_ptr, d->end_ptr);
852
853   flags[0] = get_valid(d, d->info_ptr);
854   flags[1] = get_unknown(d, d->info_ptr, 0/*before IDAT*/);
855
856   /* Only png_read_png sets PNG_INFO_IDAT! */
857   flags[chunk_info[0/*IDAT*/].keep != PNG_HANDLE_CHUNK_AS_DEFAULT] |=
858      PNG_INFO_IDAT;
859
860   flags[2] = get_valid(d, d->end_ptr);
861   flags[3] = get_unknown(d, d->end_ptr, 1/*after IDAT*/);
862
863   clean_display(d);
864
865   return d->keep;
866}
867
868static void
869check_error(display *d, png_uint_32 flags, const char *message)
870{
871   while (flags)
872   {
873      png_uint_32 flag = flags & -(png_int_32)flags;
874      int i = find_by_flag(flag);
875
876      fprintf(stderr, "%s(%s): chunk %s: %s\n", d->file, d->test,
877         chunk_info[i].name, message);
878      ++(d->error_count);
879
880      flags &= ~flag;
881   }
882}
883
884static void
885check_handling(display *d, int def, png_uint_32 chunks, png_uint_32 known,
886   png_uint_32 unknown, const char *position, int set_callback)
887{
888   while (chunks)
889   {
890      png_uint_32 flag = chunks & -(png_int_32)chunks;
891      int i = find_by_flag(flag);
892      int keep = chunk_info[i].keep;
893      const char *type;
894      const char *errorx = NULL;
895
896      if (chunk_info[i].unknown)
897      {
898         if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
899         {
900            type = "UNKNOWN (default)";
901            keep = def;
902         }
903
904         else
905            type = "UNKNOWN (specified)";
906
907         if (flag & known)
908            errorx = "chunk processed";
909
910         else switch (keep)
911         {
912            case PNG_HANDLE_CHUNK_AS_DEFAULT:
913               if (flag & unknown)
914                  errorx = "DEFAULT: unknown chunk saved";
915               break;
916
917            case PNG_HANDLE_CHUNK_NEVER:
918               if (flag & unknown)
919                  errorx = "DISCARD: unknown chunk saved";
920               break;
921
922            case PNG_HANDLE_CHUNK_IF_SAFE:
923               if (ancillary(chunk_info[i].name))
924               {
925                  if (!(flag & unknown))
926                     errorx = "IF-SAFE: unknown ancillary chunk lost";
927               }
928
929               else if (flag & unknown)
930                  errorx = "IF-SAFE: unknown critical chunk saved";
931               break;
932
933            case PNG_HANDLE_CHUNK_ALWAYS:
934               if (!(flag & unknown))
935                  errorx = "SAVE: unknown chunk lost";
936               break;
937
938            default:
939               errorx = "internal error: bad keep";
940               break;
941         }
942      } /* unknown chunk */
943
944      else /* known chunk */
945      {
946         type = "KNOWN";
947
948         if (flag & known)
949         {
950            /* chunk was processed, it won't have been saved because that is
951             * caught below when checking for inconsistent processing.
952             */
953            if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
954               errorx = "!DEFAULT: known chunk processed";
955         }
956
957         else /* not processed */ switch (keep)
958         {
959            case PNG_HANDLE_CHUNK_AS_DEFAULT:
960               errorx = "DEFAULT: known chunk not processed";
961               break;
962
963            case PNG_HANDLE_CHUNK_NEVER:
964               if (flag & unknown)
965                  errorx = "DISCARD: known chunk saved";
966               break;
967
968            case PNG_HANDLE_CHUNK_IF_SAFE:
969               if (ancillary(chunk_info[i].name))
970               {
971                  if (!(flag & unknown))
972                     errorx = "IF-SAFE: known ancillary chunk lost";
973               }
974
975               else if (flag & unknown)
976                  errorx = "IF-SAFE: known critical chunk saved";
977               break;
978
979            case PNG_HANDLE_CHUNK_ALWAYS:
980               if (!(flag & unknown))
981                  errorx = "SAVE: known chunk lost";
982               break;
983
984            default:
985               errorx = "internal error: bad keep (2)";
986               break;
987         }
988      }
989
990      if (errorx != NULL)
991      {
992         ++(d->error_count);
993         fprintf(stderr, "%s(%s%s): %s %s %s: %s\n", d->file, d->test,
994            set_callback ? ",callback" : "",
995            type, chunk_info[i].name, position, errorx);
996      }
997
998      chunks &= ~flag;
999   }
1000}
1001
1002static void
1003perform_one_test(FILE *fp, int argc, const char **argv,
1004   png_uint_32 *default_flags, display *d, int set_callback)
1005{
1006   int def;
1007   png_uint_32 flags[2][4];
1008
1009   rewind(fp);
1010   clear_keep();
1011   memcpy(flags[0], default_flags, sizeof flags[0]);
1012
1013   def = check(fp, argc, argv, flags[1], d, set_callback);
1014
1015   /* If IDAT is being handled as unknown the image read is skipped and all the
1016    * IDATs after the first end up in the end info struct, so in this case add
1017    * IDAT to the list of unknowns.  (Do this after 'check' above sets the
1018    * chunk_info 'keep' fields.)
1019    *
1020    * Note that the flag setting has to be in the 'known' field to avoid
1021    * triggering the consistency check below and the flag must only be set if
1022    * there are multiple IDATs, so if the check above did find an unknown IDAT
1023    * after IDAT.
1024    */
1025   if (chunk_info[0/*IDAT*/].keep != PNG_HANDLE_CHUNK_AS_DEFAULT &&
1026       (flags[1][3] & PNG_INFO_IDAT) != 0)
1027      flags[0][2] |= PNG_INFO_IDAT;
1028
1029   /* Chunks should either be known or unknown, never both and this should apply
1030    * whether the chunk is before or after the IDAT (actually, the app can
1031    * probably change this by swapping the handling after the image, but this
1032    * test does not do that.)
1033    */
1034   check_error(d, (flags[0][0]|flags[0][2]) & (flags[0][1]|flags[0][3]),
1035      "chunk handled inconsistently in count tests");
1036   check_error(d, (flags[1][0]|flags[1][2]) & (flags[1][1]|flags[1][3]),
1037      "chunk handled inconsistently in option tests");
1038
1039   /* Now find out what happened to each chunk before and after the IDAT and
1040    * determine if the behavior was correct.  First some basic sanity checks,
1041    * any known chunk should be known in the original count, any unknown chunk
1042    * should be either known or unknown in the original.
1043    */
1044   {
1045      png_uint_32 test;
1046
1047      test = flags[1][0] & ~flags[0][0];
1048      check_error(d, test, "new known chunk before IDAT");
1049      test = flags[1][1] & ~(flags[0][0] | flags[0][1]);
1050      check_error(d, test, "new unknown chunk before IDAT");
1051      test = flags[1][2] & ~flags[0][2];
1052      check_error(d, test, "new known chunk after IDAT");
1053      test = flags[1][3] & ~(flags[0][2] | flags[0][3]);
1054      check_error(d, test, "new unknown chunk after IDAT");
1055   }
1056
1057   /* Now each chunk in the original list should have been handled according to
1058    * the options set for that chunk, regardless of whether libpng knows about
1059    * it or not.
1060    */
1061   check_handling(d, def, flags[0][0] | flags[0][1], flags[1][0], flags[1][1],
1062      "before IDAT", set_callback);
1063   check_handling(d, def, flags[0][2] | flags[0][3], flags[1][2], flags[1][3],
1064      "after IDAT", set_callback);
1065}
1066
1067static void
1068perform_one_test_safe(FILE *fp, int argc, const char **argv,
1069   png_uint_32 *default_flags, display *d, const char *test)
1070{
1071   if (setjmp(d->error_return) == 0)
1072   {
1073      d->test = test; /* allow use of d->error_return */
1074#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
1075         perform_one_test(fp, argc, argv, default_flags, d, 0);
1076#     endif
1077#     ifdef PNG_READ_USER_CHUNKS_SUPPORTED
1078         perform_one_test(fp, argc, argv, default_flags, d, 1);
1079#     endif
1080      d->test = init; /* prevent use of d->error_return */
1081   }
1082}
1083
1084static const char *standard_tests[] =
1085{
1086 "discard", "default=discard", 0,
1087 "save", "default=save", 0,
1088 "if-safe", "default=if-safe", 0,
1089 "vpAg", "vpAg=if-safe", 0,
1090 "sTER", "sTER=if-safe", 0,
1091 "IDAT", "default=discard", "IDAT=save", 0,
1092 "sAPI", "bKGD=save", "cHRM=save", "gAMA=save", "all=discard", "iCCP=save",
1093   "sBIT=save", "sRGB=save", 0,
1094 0/*end*/
1095};
1096
1097static PNG_NORETURN void
1098usage(const char *program, const char *reason)
1099{
1100   fprintf(stderr, "pngunknown: %s: usage:\n %s [--strict] "
1101      "--default|{(CHNK|default|all)=(default|discard|if-safe|save)} "
1102      "testfile.png\n", reason, program);
1103   exit(99);
1104}
1105
1106int
1107main(int argc, const char **argv)
1108{
1109   FILE *fp;
1110   png_uint_32 default_flags[4/*valid,unknown{before,after}*/];
1111   int strict = 0, default_tests = 0;
1112   const char *count_argv = "default=save";
1113   const char *touch_file = NULL;
1114   display d;
1115
1116   init_display(&d, argv[0]);
1117
1118   while (++argv, --argc > 0)
1119   {
1120      if (strcmp(*argv, "--strict") == 0)
1121         strict = 1;
1122
1123      else if (strcmp(*argv, "--default") == 0)
1124         default_tests = 1;
1125
1126      else if (strcmp(*argv, "--touch") == 0)
1127      {
1128         if (argc > 1)
1129            touch_file = *++argv, --argc;
1130
1131         else
1132            usage(d.program, "--touch: missing file name");
1133      }
1134
1135      else
1136         break;
1137   }
1138
1139   /* A file name is required, but there should be no other arguments if
1140    * --default was specified.
1141    */
1142   if (argc <= 0)
1143      usage(d.program, "missing test file");
1144
1145   /* GCC BUG: if (default_tests && argc != 1) triggers some weird GCC argc
1146    * optimization which causes warnings with -Wstrict-overflow!
1147    */
1148   else if (default_tests) if (argc != 1)
1149      usage(d.program, "extra arguments");
1150
1151   /* The name of the test file is the last argument; remove it. */
1152   d.file = argv[--argc];
1153
1154   fp = fopen(d.file, "rb");
1155   if (fp == NULL)
1156   {
1157      perror(d.file);
1158      exit(99);
1159   }
1160
1161   /* First find all the chunks, known and unknown, in the test file, a failure
1162    * here aborts the whole test.
1163    *
1164    * If 'save' is supported then the normal saving method should happen,
1165    * otherwise if 'read' is supported then the read callback will do the
1166    * same thing.  If both are supported the 'read' callback won't be
1167    * instantiated by default.  If 'save' is *not* supported then a user
1168    * callback is required even though we can call png_get_unknown_chunks.
1169    */
1170   if (check(fp, 1, &count_argv, default_flags, &d,
1171#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
1172         0
1173#     else
1174         1
1175#     endif
1176      ) != PNG_HANDLE_CHUNK_ALWAYS)
1177   {
1178      fprintf(stderr, "%s: %s: internal error\n", d.program, d.file);
1179      exit(99);
1180   }
1181
1182   /* Now find what the various supplied options cause to change: */
1183   if (!default_tests)
1184   {
1185      d.test = cmd; /* acts as a flag to say exit, do not longjmp */
1186#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
1187         perform_one_test(fp, argc, argv, default_flags, &d, 0);
1188#     endif
1189#     ifdef PNG_READ_USER_CHUNKS_SUPPORTED
1190         perform_one_test(fp, argc, argv, default_flags, &d, 1);
1191#     endif
1192      d.test = init;
1193   }
1194
1195   else
1196   {
1197      const char **test = standard_tests;
1198
1199      /* Set the exit_test pointer here so we can continue after a libpng error.
1200       * NOTE: this leaks memory because the png_struct data from the failing
1201       * test is never freed.
1202       */
1203      while (*test)
1204      {
1205         const char *this_test = *test++;
1206         const char **next = test;
1207         int count = display_rc(&d, strict), new_count;
1208         const char *result;
1209         int arg_count = 0;
1210
1211         while (*next) ++next, ++arg_count;
1212
1213         perform_one_test_safe(fp, arg_count, test, default_flags, &d,
1214            this_test);
1215
1216         new_count = display_rc(&d, strict);
1217
1218         if (new_count == count)
1219            result = "PASS";
1220
1221         else
1222            result = "FAIL";
1223
1224         printf("%s: %s %s\n", result, d.program, this_test);
1225
1226         test = next+1;
1227      }
1228   }
1229
1230   fclose(fp);
1231
1232   if (display_rc(&d, strict) == 0)
1233   {
1234      /* Success, touch the success file if appropriate */
1235      if (touch_file != NULL)
1236      {
1237         FILE *fsuccess = fopen(touch_file, "wt");
1238
1239         if (fsuccess != NULL)
1240         {
1241            int err = 0;
1242            fprintf(fsuccess, "PNG unknown tests succeeded\n");
1243            fflush(fsuccess);
1244            err = ferror(fsuccess);
1245
1246            if (fclose(fsuccess) || err)
1247            {
1248               fprintf(stderr, "%s: write failed\n", touch_file);
1249               exit(99);
1250            }
1251         }
1252
1253         else
1254         {
1255            fprintf(stderr, "%s: open failed\n", touch_file);
1256            exit(99);
1257         }
1258      }
1259
1260      return 0;
1261   }
1262
1263   return 1;
1264}
1265
1266#else /* !(READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS) */
1267int
1268main(void)
1269{
1270   fprintf(stderr,
1271      " test ignored: no support to find out about unknown chunks\n");
1272   /* So the test is skipped: */
1273   return SKIP;
1274}
1275#endif /* READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS */
1276
1277#else /* !(SET_UNKNOWN_CHUNKS && READ) */
1278int
1279main(void)
1280{
1281   fprintf(stderr,
1282      " test ignored: no support to modify unknown chunk handling\n");
1283   /* So the test is skipped: */
1284   return SKIP;
1285}
1286#endif /* SET_UNKNOWN_CHUNKS && READ*/
1287