Skip to content

Commit

Permalink
Fix imagecreatefromavif() memory leak
Browse files Browse the repository at this point in the history
This has been reported as libgd/libgd#831.
We port the respective fix to our bundled libgd.

Closes GH-8812.
  • Loading branch information
cmb69 committed Jun 17, 2022
1 parent 3fed226 commit 036bed0
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 18 deletions.
3 changes: 3 additions & 0 deletions NEWS
Expand Up @@ -19,6 +19,9 @@ PHP NEWS
. Fixed bug #78139 (timezone_open accepts invalid timezone string argument).
(Derick)

GD:
. Fixed imagecreatefromavif() memory leak. (cmb)

- MBString:
. mb_detect_encoding recognizes all letters in Czech alphabet (alexdowad)
. mb_detect_encoding recognizes all letters in Hungarian alphabet (alexdowad)
Expand Down
52 changes: 34 additions & 18 deletions ext/gd/libgd/gd_avif.c
Expand Up @@ -150,6 +150,11 @@ static avifBool isAvifError(avifResult result, const char *msg) {
}


typedef struct avifIOCtxReader {
avifIO io; // this must be the first member for easy casting to avifIO*
avifROData rodata;
} avifIOCtxReader;

/*
<readfromCtx> implements the avifIOReadFunc interface by calling the relevant functions
in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead().
Expand All @@ -165,8 +170,8 @@ static avifBool isAvifError(avifResult result, const char *msg) {
*/
static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, avifROData *out)
{
void *dataBuf = NULL;
gdIOCtx *ctx = (gdIOCtx *) io->data;
avifIOCtxReader *reader = (avifIOCtxReader *) io;

// readFlags is unsupported
if (readFlags != 0) {
Expand All @@ -182,28 +187,34 @@ static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, s
if (!ctx->seek(ctx, (int) offset))
return AVIF_RESULT_IO_ERROR;

dataBuf = avifAlloc(size);
if (!dataBuf) {
if (size > reader->rodata.size) {
reader->rodata.data = gdRealloc((void *) reader->rodata.data, size);
reader->rodata.size = size;
}
if (!reader->rodata.data) {
gd_error("avif error - couldn't allocate memory");
return AVIF_RESULT_UNKNOWN_ERROR;
}

// Read the number of bytes requested.
// If getBuf() returns a negative value, that means there was an error.
int charsRead = ctx->getBuf(ctx, dataBuf, (int) size);
int charsRead = ctx->getBuf(ctx, (void *) reader->rodata.data, (int) size);
if (charsRead < 0) {
avifFree(dataBuf);
return AVIF_RESULT_IO_ERROR;
}

out->data = dataBuf;
out->data = reader->rodata.data;
out->size = charsRead;
return AVIF_RESULT_OK;
}

// avif.h says this is optional, but it seemed easy to implement.
static void destroyAvifIO(struct avifIO *io) {
gdFree(io);
avifIOCtxReader *reader = (avifIOCtxReader *) io;
if (reader->rodata.data != NULL) {
gdFree((void *) reader->rodata.data);
}
gdFree(reader);
}

/* Set up an avifIO object.
Expand All @@ -217,21 +228,23 @@ static void destroyAvifIO(struct avifIO *io) {

// TODO: can we get sizeHint somehow?
static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) {
avifIO *io;
struct avifIOCtxReader *reader;

io = gdMalloc(sizeof(*io));
if (io == NULL)
reader = gdMalloc(sizeof(*reader));
if (reader == NULL)
return NULL;

// TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary?
io->persistent = AVIF_FALSE;
io->read = readFromCtx;
io->write = NULL; // this function is currently unused; see avif.h
io->destroy = destroyAvifIO;
io->sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
io->data = ctx;

return io;
reader->io.persistent = AVIF_FALSE;
reader->io.read = readFromCtx;
reader->io.write = NULL; // this function is currently unused; see avif.h
reader->io.destroy = destroyAvifIO;
reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
reader->io.data = ctx;
reader->rodata.data = NULL;
reader->rodata.size = 0;

return (avifIO *) reader;
}


Expand Down Expand Up @@ -576,6 +589,9 @@ void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed)

if (avifOutput.data)
avifRWDataFree(&avifOutput);

if (avifIm)
avifImageDestroy(avifIm);
}

void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed)
Expand Down

0 comments on commit 036bed0

Please sign in to comment.