summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph M. Becker <cmbecker69@gmx.de>2022-06-10 13:58:56 +0200
committerChristoph M. Becker <cmbecker69@gmx.de>2022-06-10 13:58:56 +0200
commit4b5b2f122bf5df628d4e07631b599e8ef3144f70 (patch)
tree5bf5fcfd42030c0e81c076532b6cb0e120a4ae09
parent7efcf4f935d0466b6ada44bed62d729cd2cddbc4 (diff)
downloadlibgd-4b5b2f122bf5df628d4e07631b599e8ef3144f70.tar.gz
Fix #831: gdImageAvif memory leak
First, we must not forget to call `avifImageDestroy()` when we're finished with the image. Then we also need to cater to the allocated `dataBuf`. To keep track of that, we "extend" `avifIO` as `avifIOCtxReader`. To simplify, and to avoid unnecessary allocations, we use `realloc()`. To better fit with GD, we also use the GD memory allocation functions instead of the ones provided by libavif.
-rw-r--r--src/gd_avif.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/src/gd_avif.c b/src/gd_avif.c
index 5935623..48369fe 100644
--- a/src/gd_avif.c
+++ b/src/gd_avif.c
@@ -159,6 +159,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().
@@ -174,8 +179,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) {
@@ -191,28 +196,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) {
- avifFree(io);
+ avifIOCtxReader *reader = (avifIOCtxReader *) io;
+ if (reader->rodata.data != NULL) {
+ gdFree((void *) reader->rodata.data);
+ }
+ gdFree(reader);
}
/* Set up an avifIO object.
@@ -226,21 +237,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;
}
@@ -599,6 +612,9 @@ cleanup:
if (avifOutput.data)
avifRWDataFree(&avifOutput);
+ if (avifIm)
+ avifImageDestroy(avifIm);
+
return failed;
}