Skip to content

Commit

Permalink
Add support for multiple PNG chunks (#83)
Browse files Browse the repository at this point in the history
* Add support for multiple PNG chunks to be concatenated
* Ignore chunks with invalid lengths
* Pack the image structures, since we can't be sure about alignment
* save artifacts from windows build
  • Loading branch information
AndreRenaud authored Jan 11, 2021
1 parent 16ab229 commit f49dd3d
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 48 deletions.
19 changes: 17 additions & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,28 @@ jobs:
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
make clean
make testprog CC=cl
make testprog.exe CC=cl
cp testprog.exe testprog-64.exe
testprog-64
cp output.pdf output-win64.pdf
- name: make x86
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
make clean
make testprog CC=cl
make testprog.exe CC=cl
cp testprog.exe testprog-32.exe
testprog-32
cp output.pdf output-win32.pdf
- name: Upload aritfacts
uses: actions/upload-artifact@v2
with:
name: artifacts
path: |
output-win32.pdf
output-win64.pdf
testprog-32.exe
testprog-64.exe
build-macos:
runs-on: macos-latest
Expand Down
11 changes: 7 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ CFLAGS=-Wall
CFLAGS_OBJECT=/Fo:
CFLAGS_EXE=/Fe:
O_SUFFIX=.obj
EXE_SUFFIX=.exe
else
CFLAGS=-g -Wall -pipe --std=c1x -O3 -pedantic -Wsuggest-attribute=const -Wsuggest-attribute=format -Wclobbered -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wold-style-declaration -Wmissing-parameter-type -Woverride-init -Wtype-limits -Wuninitialized -Wunused-but-set-parameter -fprofile-arcs -ftest-coverage
CFLAGS_OBJECT=-o
CFLAGS_EXE=-o
O_SUFFIX=.o
EXE_SUFFIX=
endif

TESTPROG=testprog$(EXE_SUFFIX)

default: testprog
default: $(TESTPROG)

testprog: pdfgen$(O_SUFFIX) tests/main$(O_SUFFIX) tests/penguin$(O_SUFFIX) tests/rgb$(O_SUFFIX)
$(TESTPROG): pdfgen$(O_SUFFIX) tests/main$(O_SUFFIX) tests/penguin$(O_SUFFIX) tests/rgb$(O_SUFFIX)
$(CC) $(CFLAGS_EXE) $@ pdfgen$(O_SUFFIX) tests/main$(O_SUFFIX) tests/penguin$(O_SUFFIX) tests/rgb$(O_SUFFIX) $(LFLAGS)

tests/fuzz-%: tests/fuzz-%.c pdfgen.c
Expand All @@ -31,7 +34,7 @@ tests/penguin.c: data/penguin.jpg
%$(O_SUFFIX): %.c
$(CC) -I. -c $< $(CFLAGS_OBJECT) $@ $(CFLAGS)

check: testprog pdfgen.c pdfgen.h example-check
check: $(TESTPROG) pdfgen.c pdfgen.h example-check
cppcheck --std=c99 --enable=style,warning,performance,portability,unusedFunction --quiet pdfgen.c pdfgen.h tests/main.c
$(CXX) -c pdfgen.c $(CFLAGS_OBJECT) /dev/null -Werror -Wall -Wextra
./tests.sh
Expand Down Expand Up @@ -63,5 +66,5 @@ docs: FORCE
FORCE:

clean:
rm -f *$(O_SUFFIX) tests/*$(O_SUFFIX) testprog *.gcda *.gcno *.gcov tests/*.gcda tests/*.gcno output.pdf output.txt tests/fuzz-header tests/fuzz-text tests/fuzz-image-data tests/fuzz-image-file output.pdftk fuzz-image-file.pdf fuzz-image-data.pdf fuzz-image.dat doxygen.log tests/penguin.c fuzz.pdf output.ps
rm -f *$(O_SUFFIX) tests/*$(O_SUFFIX) $(TESTPROG) *.gcda *.gcno *.gcov tests/*.gcda tests/*.gcno output.pdf output.txt tests/fuzz-header tests/fuzz-text tests/fuzz-image-data tests/fuzz-image-file output.pdftk fuzz-image-file.pdf fuzz-image-data.pdf fuzz-image.dat doxygen.log tests/penguin.c fuzz.pdf output.ps
rm -rf docs fuzz-artifacts infer-out
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ before_build:
- if "%PLATFORM%"=="win32" SET PATH=C:\msys64\mingw32\bin;C:\msys64\mingw64\bin;C:\MinGW\bin;%PATH%

build_script:
- mingw32-make testprog CC=cl
- mingw32-make testprog.exe CC=cl
- testprog

artifacts:
Expand Down
116 changes: 75 additions & 41 deletions pdfgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,20 +275,16 @@ struct pdf_doc {
#define PNG_COLOR_INDEXED (3)
#define PNG_COLOR_GREY (0)

/**
* Since we're casting random areas of memory to these, make sure
* they're packed properly to match the image format requirements
*/
#pragma pack(push, 1)
struct png_chunk {
uint32_t length;
char type[4];
};

struct png_info {
uint32_t pos;
uint32_t length;
uint8_t bitdepth;
uint8_t ncolours;
uint32_t width;
uint32_t height;
};

struct png_header {
uint32_t width;
uint32_t height;
Expand All @@ -311,6 +307,16 @@ struct bmp_header {
uint16_t biBitCount;
uint32_t biCompression;
};
#pragma pack(pop)

struct png_info {
uint32_t length;
uint8_t bitdepth;
uint8_t ncolours;
uint32_t width;
uint32_t height;
uint8_t *data;
};

/**
* Simple flexible resizing array implementation
Expand Down Expand Up @@ -2495,12 +2501,12 @@ static int pdf_add_png_data(struct pdf_doc *pdf, struct pdf_object *page,
int written = 0;
uint32_t pos;
struct png_info info = {
.pos = 0,
.length = 0,
.bitdepth = 0,
.ncolours = 0,
.width = 0,
.height = 0,
.data = NULL,
};

if (len <= sizeof(png_signature))
Expand All @@ -2515,60 +2521,82 @@ static int pdf_add_png_data(struct pdf_doc *pdf, struct pdf_object *page,

chunk = (const struct png_chunk *)&png_data[pos];
pos += sizeof(struct png_chunk);
if (pos > len)
return pdf_set_err(pdf, -EINVAL, "PNG file too short");
if (pos > len) {
pdf_set_err(pdf, -EINVAL, "PNG file too short");
goto info_free;
}
if (strncmp(chunk->type, png_chunk_header, 4) == 0) {
/* header found, process width and height, check errors */
const struct png_header *header =
(const struct png_header *)&png_data[pos];
if (pos + sizeof(struct png_header) > len)
return pdf_set_err(pdf, -EINVAL, "PNG file too short");
if (header->deflate != 0)
return pdf_set_err(pdf, -EINVAL,
"Deflate wrong in PNG header");
if (header->colortype & PNG_COLOR_ALPHA)
return pdf_set_err(pdf, -EINVAL,
"PDF doesn't support PNG alpha channel");
if (pos + sizeof(struct png_header) > len) {
pdf_set_err(pdf, -EINVAL, "PNG file too short");
goto info_free;
}
if (header->deflate != 0) {
pdf_set_err(pdf, -EINVAL, "Deflate wrong in PNG header");
goto info_free;
}
if (header->colortype & PNG_COLOR_ALPHA) {
pdf_set_err(pdf, -EINVAL,
"PDF doesn't support PNG alpha channel");
goto info_free;
}
info.width = ntoh32(header->width);
info.height = ntoh32(header->height);
info.bitdepth = header->bitdepth;
if (header->colortype == PNG_COLOR_RGB)
info.ncolours = 3;
else if (header->colortype == PNG_COLOR_INDEXED)
info.ncolours = 1;
else
return pdf_set_err(pdf, -EINVAL,
"PNG has unsupported color type: %d",
header->colortype);
else {
pdf_set_err(pdf, -EINVAL,
"PNG has unsupported color type: %d",
header->colortype);
goto info_free;
}
} else if (strncmp(chunk->type, png_chunk_data, 4) == 0) {
info.length = ntoh32(chunk->length);
info.pos = pos;
uint32_t chunk_len = ntoh32(chunk->length);
if (chunk_len > 0 && chunk_len < len - pos) {
uint8_t *data =
(uint8_t *)realloc(info.data, info.length + chunk_len);
if (!data) {
pdf_set_err(pdf, -ENOMEM, "No memory for PNG data");
goto info_free;
}
info.data = data;
memcpy(&info.data[info.length], &png_data[pos], chunk_len);
info.length += chunk_len;
}
} else if (strncmp(chunk->type, png_chunk_end, 4) == 0) {
/* end of file, exit */
break;
}

if (ntoh32(chunk->length) >= len)
return pdf_set_err(pdf, -EINVAL,
"PNG chunk length larger than file");
if (ntoh32(chunk->length) >= len) {
pdf_set_err(pdf, -EINVAL, "PNG chunk length larger than file");
goto info_free;
}

pos += ntoh32(chunk->length); // add chunk length
pos += sizeof(uint32_t); // add CRC length
if (pos > len)
return pdf_set_err(pdf, -errno,
"Wrong PNG format, chunks not found");
if (pos > len) {
pdf_set_err(pdf, -errno, "Wrong PNG format, chunks not found");
goto info_free;
}
}
/* if no length was found */
if (info.length == 0 || info.bitdepth == 0)
return pdf_set_err(pdf, -EINVAL, "PNG file has zero length/bitdepth");

if (info.length + info.pos > len)
return pdf_set_err(pdf, -EINVAL, "PNG data length is out of bounds");
if (info.length == 0 || info.bitdepth == 0) {
pdf_set_err(pdf, -EINVAL, "PNG file has zero length/bitdepth");
goto info_free;
}

final_data = (uint8_t *)malloc(info.length + 1024);
if (!final_data)
return pdf_set_err(pdf, -ENOMEM, "Unable to allocate PNG data %d",
info.length + 1024);
if (!final_data) {
pdf_set_err(pdf, -ENOMEM, "Unable to allocate PNG data %d",
info.length + 1024);
goto info_free;
}

/* RGB colored image */
written = sprintf((char *)final_data,
Expand All @@ -2585,7 +2613,8 @@ static int pdf_add_png_data(struct pdf_doc *pdf, struct pdf_object *page,
info.height, info.bitdepth, info.ncolours,
info.bitdepth, info.width, info.length);

memcpy(&final_data[written], &png_data[info.pos], info.length);
memcpy(&final_data[written], info.data, info.length);
free(info.data);
written += info.length;
written += sprintf((char *)&final_data[written], "\r\nendstream\r\n");

Expand All @@ -2599,6 +2628,11 @@ static int pdf_add_png_data(struct pdf_doc *pdf, struct pdf_object *page,
free(final_data);

return pdf_add_image(pdf, page, obj, x, y, display_width, display_height);

info_free:
if (info.data)
free(info.data);
return pdf->errval;
}

static int pdf_add_bmp_data(struct pdf_doc *pdf, struct pdf_object *page,
Expand Down

0 comments on commit f49dd3d

Please sign in to comment.