From 68dda34448909ffb26eb32061d4fae1dda071b31 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Tue, 6 Jan 2026 11:18:52 -0500 Subject: [PATCH] Fix an error propagation bug in _pdfioValueCopy (Issue #146) --- CHANGES.md | 6 ++ NOTICE | 2 +- README.md | 2 +- pdfio-dict.c | 22 ++++++-- pdfio-object.c | 9 ++- pdfio-value.c | 11 ++-- testpdfio.c | 148 +++++++++++++++++++++++++++++++------------------ 7 files changed, 134 insertions(+), 66 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e2bd3a9..0ffda8c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,12 @@ v1.7.0 - YYYY-MM-DD - Now use TTF 1.1 or later for font support. +v1.6.2 - YYYY-MM-DD +------------------- + +- Fixed an error propagation bug when reading too-long values (Issue #146) + + v1.6.1 - 2025-12-26 ------------------- diff --git a/NOTICE b/NOTICE index 846ae73..27ba703 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ PDFio - PDF Read/Write Library -Copyright © 2021-2025 by Michael R Sweet. +Copyright © 2021-2026 by Michael R Sweet. (Optional) Exceptions to the Apache 2.0 License: ================================================ diff --git a/README.md b/README.md index 9f95399..960326f 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ generates a static library that will be installed under "/usr/local" with: Legal Stuff ----------- -PDFio is Copyright © 2021-2025 by Michael R Sweet. +PDFio is Copyright © 2021-2026 by Michael R Sweet. This software is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the diff --git a/pdfio-dict.c b/pdfio-dict.c index 660d1f9..99d623e 100644 --- a/pdfio-dict.c +++ b/pdfio-dict.c @@ -1,7 +1,7 @@ // // PDF dictionary functions for PDFio. // -// Copyright © 2021-2025 by Michael R Sweet. +// Copyright © 2021-2026 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. @@ -92,6 +92,8 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file // Copy and add each of the source dictionary's key/value pairs... for (i = dict->num_pairs, p = dict->pairs; i > 0; i --, p ++) { + PDFIO_DEBUG("pdfioDictCopy: key=\"%s\", value.type=%d\n", p->key, p->value.type); + if (!strcmp(p->key, "Length") && p->value.type == PDFIO_VALTYPE_INDIRECT && dict->pdf != pdf) { // Don't use indirect stream lengths for copied objects... @@ -102,15 +104,28 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file if (lenobj) { if (lenobj->value.type == PDFIO_VALTYPE_NONE) - _pdfioObjLoad(lenobj); + { + if (!_pdfioObjLoad(lenobj)) + { + PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key); + return (NULL); + } + } v.value.number = lenobj->value.value.number; } else + { v.value.number = 0.0; + } + + PDFIO_DEBUG("pdfioDictCopy: Length is %.0f.\n", v.value.number); } else if (!_pdfioValueCopy(pdf, &v, dict->pdf, &p->value)) + { + PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key); return (NULL); // Let pdfioFileClose do the cleanup... + } if (_pdfioStringIsAllocated(dict->pdf, p->key)) key = pdfioStringCreate(pdf, p->key); @@ -650,8 +665,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file } else if (_pdfioDictGetValue(dict, key + 1)) { - // Issue 118: Discard duplicate key/value pairs, in the future this will - // be a warning message... + // Issue 118: Discard duplicate key/value pairs... _pdfioValueDelete(&value); if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1)) continue; diff --git a/pdfio-object.c b/pdfio-object.c index b480404..814a923 100644 --- a/pdfio-object.c +++ b/pdfio-object.c @@ -1,7 +1,7 @@ // // PDF object functions for PDFio. // -// Copyright © 2021-2025 by Michael R Sweet. +// Copyright © 2021-2026 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. @@ -69,7 +69,7 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file ssize_t bytes; // Bytes read - PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%p))\n", (void *)pdf, (void *)srcobj, srcobj ? (void *)srcobj->pdf : NULL); + PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%u,%p))\n", (void *)pdf, (void *)srcobj, srcobj ? (unsigned)srcobj->number : 0, srcobj ? (void *)srcobj->pdf : NULL); // Range check input if (!pdf || !srcobj) @@ -77,7 +77,10 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file // Load the object value if needed... if (srcobj->value.type == PDFIO_VALTYPE_NONE) - _pdfioObjLoad(srcobj); + { + if (!_pdfioObjLoad(srcobj)) + return (NULL); + } // See if we have already mapped this object... if ((dstobj = _pdfioFileFindMappedObj(pdf, srcobj->pdf, srcobj->number)) != NULL) diff --git a/pdfio-value.c b/pdfio-value.c index 46bb234..7efc38f 100644 --- a/pdfio-value.c +++ b/pdfio-value.c @@ -1,7 +1,7 @@ // // PDF value functions for PDFio. // -// Copyright © 2021-2025 by Michael R Sweet. +// Copyright © 2021-2026 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. @@ -76,7 +76,8 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file return (NULL); case PDFIO_VALTYPE_ARRAY : - vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array); + if ((vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array)) == NULL) + return (NULL); break; case PDFIO_VALTYPE_BINARY : @@ -97,12 +98,14 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file return (vdst); case PDFIO_VALTYPE_DICT : - vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict); + if ((vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict)) == NULL) + return (NULL); break; case PDFIO_VALTYPE_NAME : case PDFIO_VALTYPE_STRING : - vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name); + if ((vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name)) == NULL) + return (NULL); break; } diff --git a/testpdfio.c b/testpdfio.c index 4916366..af660f9 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -1,16 +1,20 @@ // // Test program for PDFio. // -// Copyright © 2021-2025 by Michael R Sweet. +// Copyright © 2021-2026 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // Usage: // -// ./testpdfio +// ./testpdfio OPTIONS [FILENAME {OBJECT-NUMBER,OUT-FILENAME}] ... // -// ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ... +// Options: +// +// --help Show help +// --password PASSWORD Set access password +// --verbose Be verbose // #include "pdfio-private.h" @@ -29,7 +33,7 @@ static int do_crypto_tests(void); static int do_pdfa_tests(void); -static int do_test_file(const char *filename, int objnum, const char *password, bool verbose); +static int do_test_file(const char *filename, const char *outfile, int objnum, const char *password, bool verbose); static int do_unit_tests(void); static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label); static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error); @@ -103,14 +107,18 @@ main(int argc, // I - Number of command-line arguments else if ((i + 1) < argc && isdigit(argv[i + 1][0] & 255)) { // filename.pdf object-number - if (do_test_file(argv[i], atoi(argv[i + 1]), password, verbose)) + if (do_test_file(argv[i], /*outfile*/NULL, atoi(argv[i + 1]), password, verbose)) ret = 1; i ++; } - else if (do_test_file(argv[i], 0, password, verbose)) + else { - ret = 1; + if (do_test_file(argv[i], argv[i + 1], /*objnum*/0, password, verbose)) + ret = 1; + + if (argv[i + 1]) + i ++; } } } @@ -130,6 +138,7 @@ main(int argc, // I - Number of command-line arguments return (ret); } + // // 'do_crypto_tests()' - Test the various cryptographic functions in PDFio. // @@ -429,12 +438,15 @@ do_pdfa_tests(void) static int // O - Exit status do_test_file(const char *filename, // I - PDF filename + const char *outfile, // I - Output filename, if any int objnum, // I - Object number to dump, if any const char *password, // I - Password for file bool verbose) // I - Be verbose? { + int status = 0; // Exit status bool error = false; // Have we shown an error yet? - pdfio_file_t *pdf; // PDF file + pdfio_file_t *pdf, // PDF file + *outpdf; // Output PDF file, if any size_t n, // Object/page index num_objs, // Number of objects num_pages; // Number of pages @@ -444,7 +456,12 @@ do_test_file(const char *filename, // I - PDF filename // Try opening the file... if (!objnum) - testBegin("%s", filename); + { + if (outfile) + testBegin("%s -> %s", filename, outfile); + else + testBegin("%s", filename); + } if ((pdf = pdfioFileOpen(filename, password_cb, (void *)password, (pdfio_error_cb_t)error_cb, &error)) != NULL) { @@ -458,6 +475,7 @@ do_test_file(const char *filename, // I - PDF filename if ((obj = pdfioFileFindObj(pdf, (size_t)objnum)) == NULL) { puts("Not found."); + pdfioFileClose(pdf); return (1); } @@ -465,6 +483,7 @@ do_test_file(const char *filename, // I - PDF filename { _pdfioValueDebug(&obj->value, stdout); putchar('\n'); + pdfioFileClose(pdf); return (0); } @@ -474,6 +493,7 @@ do_test_file(const char *filename, // I - PDF filename { _pdfioValueDebug(&obj->value, stdout); putchar('\n'); + pdfioFileClose(pdf); return (0); } @@ -481,6 +501,7 @@ do_test_file(const char *filename, // I - PDF filename fwrite(buffer, 1, (size_t)bytes, stdout); pdfioStreamClose(st); + pdfioFileClose(pdf); return (0); } @@ -488,56 +509,77 @@ do_test_file(const char *filename, // I - PDF filename { testEnd(true); - // Show basic stats... - num_objs = pdfioFileGetNumObjs(pdf); - num_pages = pdfioFileGetNumPages(pdf); - - printf(" PDF %s, %d pages, %d objects.\n", pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs); - - if (verbose) + if (outfile) { - // Show a summary of each page... - for (n = 0; n < num_pages; n ++) - { - if ((obj = pdfioFileGetPage(pdf, n)) == NULL) - { - printf("%s: Unable to get page #%d.\n", filename, (int)n + 1); - } - else - { - pdfio_rect_t media_box; // MediaBox value - - memset(&media_box, 0, sizeof(media_box)); - dict = pdfioObjGetDict(obj); - - if (!pdfioDictGetRect(dict, "MediaBox", &media_box)) + // Copy pages to the output file... + if ((outpdf = pdfioFileCreate(outfile, pdfioFileGetVersion(pdf), /*media_box*/NULL, /*crop_box*/NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL) + { + for (n = 0, num_pages = pdfioFileGetNumPages(pdf); n < num_pages; n ++) + { + if (!pdfioPageCopy(outpdf, pdfioFileGetPage(pdf, n))) { - if ((obj = pdfioDictGetObj(dict, "Parent")) != NULL) - { - dict = pdfioObjGetDict(obj); - pdfioDictGetRect(dict, "MediaBox", &media_box); - } + status = 1; + break; } + } - printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2); - } - } + pdfioFileClose(outpdf); + } + } + else + { + // Show basic stats... + num_objs = pdfioFileGetNumObjs(pdf); + num_pages = pdfioFileGetNumPages(pdf); - // Show the associated value with each object... - for (n = 0; n < num_objs; n ++) + printf(" PDF %s, %d pages, %d objects.\n", pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs); + + if (verbose) { - if ((obj = pdfioFileGetObj(pdf, n)) == NULL) + // Show a summary of each page... + for (n = 0; n < num_pages; n ++) { - printf(" Unable to get object #%d.\n", (int)n); - } - else - { - dict = pdfioObjGetDict(obj); + if ((obj = pdfioFileGetPage(pdf, n)) == NULL) + { + printf("%s: Unable to get page #%d.\n", filename, (int)n + 1); + } + else + { + pdfio_rect_t media_box; // MediaBox value - printf(" %u %u obj dict=%p(%lu pairs)\n", (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), (void *)dict, dict ? (unsigned long)dict->num_pairs : 0UL); - fputs(" ", stdout); - _pdfioValueDebug(&obj->value, stdout); - putchar('\n'); + memset(&media_box, 0, sizeof(media_box)); + dict = pdfioObjGetDict(obj); + + if (!pdfioDictGetRect(dict, "MediaBox", &media_box)) + { + if ((obj = pdfioDictGetObj(dict, "Parent")) != NULL) + { + dict = pdfioObjGetDict(obj); + pdfioDictGetRect(dict, "MediaBox", &media_box); + } + } + + printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2); + } + } + + // Show the associated value with each object... + for (n = 0; n < num_objs; n ++) + { + if ((obj = pdfioFileGetObj(pdf, n)) == NULL) + { + printf(" Unable to get object #%d.\n", (int)n); + status = 1; + } + else + { + dict = pdfioObjGetDict(obj); + + printf(" %u %u obj dict=%p(%lu pairs)\n", (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), (void *)dict, dict ? (unsigned long)dict->num_pairs : 0UL); + fputs(" ", stdout); + _pdfioValueDebug(&obj->value, stdout); + putchar('\n'); + } } } } @@ -545,7 +587,7 @@ do_test_file(const char *filename, // I - PDF filename // Close the file and return success... pdfioFileClose(pdf); - return (0); + return (status); } else { @@ -1739,7 +1781,7 @@ token_peek_cb(const char **s, // IO - Test string static int // O - Exit status usage(FILE *fp) // I - Output file { - fputs("Usage: ./testpdfio [OPTIONS] [FILENAME [OBJNUM]] ...\n", fp); + fputs("Usage: ./testpdfio [OPTIONS] [FILENAME {OBJECT-NUM,OUT-FILENAME}] ...\n", fp); fputs("Options:\n", fp); fputs(" --help Show program help.\n", fp); fputs(" --password PASSWORD Set PDF password.\n", fp);