Fix imagecreatefromavif() memory leak

This has been reported as https://github.com/libgd/libgd/issues/831.
We port the respective fix to our bundled libgd.

Closes GH-8812.
This commit is contained in:
Christoph M. Becker 2022-06-17 13:19:51 +02:00
parent 3fed226e62
commit 036bed01ce
No known key found for this signature in database
GPG key ID: D66C9593118BCCB6
2 changed files with 36 additions and 17 deletions

3
NEWS
View file

@ -19,6 +19,9 @@ PHP NEWS
. Fixed bug #78139 (timezone_open accepts invalid timezone string argument). . Fixed bug #78139 (timezone_open accepts invalid timezone string argument).
(Derick) (Derick)
GD:
. Fixed imagecreatefromavif() memory leak. (cmb)
- MBString: - MBString:
. mb_detect_encoding recognizes all letters in Czech alphabet (alexdowad) . mb_detect_encoding recognizes all letters in Czech alphabet (alexdowad)
. mb_detect_encoding recognizes all letters in Hungarian alphabet (alexdowad) . mb_detect_encoding recognizes all letters in Hungarian alphabet (alexdowad)

View file

@ -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 <readfromCtx> implements the avifIOReadFunc interface by calling the relevant functions
in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead(). in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead().
@ -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) static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, avifROData *out)
{ {
void *dataBuf = NULL;
gdIOCtx *ctx = (gdIOCtx *) io->data; gdIOCtx *ctx = (gdIOCtx *) io->data;
avifIOCtxReader *reader = (avifIOCtxReader *) io;
// readFlags is unsupported // readFlags is unsupported
if (readFlags != 0) { if (readFlags != 0) {
@ -182,28 +187,34 @@ static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, s
if (!ctx->seek(ctx, (int) offset)) if (!ctx->seek(ctx, (int) offset))
return AVIF_RESULT_IO_ERROR; return AVIF_RESULT_IO_ERROR;
dataBuf = avifAlloc(size); if (size > reader->rodata.size) {
if (!dataBuf) { 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"); gd_error("avif error - couldn't allocate memory");
return AVIF_RESULT_UNKNOWN_ERROR; return AVIF_RESULT_UNKNOWN_ERROR;
} }
// Read the number of bytes requested. // Read the number of bytes requested.
// If getBuf() returns a negative value, that means there was an error. // 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) { if (charsRead < 0) {
avifFree(dataBuf);
return AVIF_RESULT_IO_ERROR; return AVIF_RESULT_IO_ERROR;
} }
out->data = dataBuf; out->data = reader->rodata.data;
out->size = charsRead; out->size = charsRead;
return AVIF_RESULT_OK; return AVIF_RESULT_OK;
} }
// avif.h says this is optional, but it seemed easy to implement. // avif.h says this is optional, but it seemed easy to implement.
static void destroyAvifIO(struct avifIO *io) { 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. /* Set up an avifIO object.
@ -217,21 +228,23 @@ static void destroyAvifIO(struct avifIO *io) {
// TODO: can we get sizeHint somehow? // TODO: can we get sizeHint somehow?
static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) { static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) {
avifIO *io; struct avifIOCtxReader *reader;
io = gdMalloc(sizeof(*io)); reader = gdMalloc(sizeof(*reader));
if (io == NULL) if (reader == NULL)
return NULL; return NULL;
// TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary? // TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary?
io->persistent = AVIF_FALSE; reader->io.persistent = AVIF_FALSE;
io->read = readFromCtx; reader->io.read = readFromCtx;
io->write = NULL; // this function is currently unused; see avif.h reader->io.write = NULL; // this function is currently unused; see avif.h
io->destroy = destroyAvifIO; reader->io.destroy = destroyAvifIO;
io->sizeHint = 0; // sadly, we don't get this information from the gdIOCtx. reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
io->data = ctx; reader->io.data = ctx;
reader->rodata.data = NULL;
reader->rodata.size = 0;
return io; return (avifIO *) reader;
} }
@ -576,6 +589,9 @@ cleanup:
if (avifOutput.data) if (avifOutput.data)
avifRWDataFree(&avifOutput); avifRWDataFree(&avifOutput);
if (avifIm)
avifImageDestroy(avifIm);
} }
void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed) void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed)