From: Egbert Eich Date: Tue Oct 4 23:09:01 2022 +0200 Subject: H5O__pline_decode() Make more resilient to out-of-bounds read Patch-mainline: Not yet Git-repo: ssh://eich@192.168.122.1:/home/eich/sources/HPC/hdf5 Git-commit: 35b798ca7542ce45ef016859b8e70d57b7f89cfe References: Malformed hdf5 files may have trunkated content which does not match the expected size. This function attempts to decode these it will read past the end of the allocated space which may lead to a crash. Make sure each element is within bounds before reading. This fixes CVE-2019-8396. Signed-off-by: Egbert Eich Signed-off-by: Egbert Eich --- src/H5Opline.c | 17 +++++++++++++++-- src/H5private.h | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/H5Opline.c b/src/H5Opline.c index ffc4557ffc..a532aa4512 100644 --- a/src/H5Opline.c +++ b/src/H5Opline.c @@ -110,6 +110,14 @@ H5FL_DEFINE(H5O_pline_t); * *------------------------------------------------------------------------- */ +static char err[] = "ran off the end of the buffer: current p = %p, p_end = %p"; + +#define VERIFY_LIMIT(p,s,l) \ + if (p + s - 1 > l) { \ + HCOMMON_ERROR(H5E_RESOURCE, H5E_NOSPACE, err, p + s, l); \ + HGOTO_DONE(NULL) \ + }; + static void * H5O__pline_decode(H5F_t H5_ATTR_UNUSED *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, size_t p_size, const uint8_t *p) @@ -159,6 +167,7 @@ H5O__pline_decode(H5F_t H5_ATTR_UNUSED *f, H5O_t H5_ATTR_UNUSED *open_oh, unsign /* Decode filters */ for (i = 0, filter = &pline->filter[0]; i < pline->nused; i++, filter++) { /* Filter ID */ + VERIFY_LIMIT(p, 6, p_end) /* 6 bytes minimum */ UINT16DECODE(p, filter->id); /* Length of filter name */ @@ -168,6 +177,7 @@ H5O__pline_decode(H5F_t H5_ATTR_UNUSED *f, H5O_t H5_ATTR_UNUSED *open_oh, unsign UINT16DECODE(p, name_length); if (pline->version == H5O_PLINE_VERSION_1 && name_length % 8) HGOTO_ERROR(H5E_PLINE, H5E_CANTLOAD, NULL, "filter name length is not a multiple of eight") + VERIFY_LIMIT(p, 4, p_end) /* with name_length 4 bytes to go */ } /* end if */ /* Filter flags */ @@ -179,9 +189,12 @@ H5O__pline_decode(H5F_t H5_ATTR_UNUSED *f, H5O_t H5_ATTR_UNUSED *open_oh, unsign /* Filter name, if there is one */ if (name_length) { size_t actual_name_length; /* Actual length of name */ - + size_t len = (size_t)(p_end - p + 1); /* Determine actual name length (without padding, but with null terminator) */ - actual_name_length = HDstrlen((const char *)p) + 1; + actual_name_length = HDstrnlen((const char *)p, len); + if (actual_name_length == len) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "filter name not null terminated") + actual_name_length += 1; /* include \0 byte */ HDassert(actual_name_length <= name_length); /* Allocate space for the filter name, or use the internal buffer */ diff --git a/src/H5private.h b/src/H5private.h index bc00f120d2..3285c36441 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -1485,6 +1485,9 @@ H5_DLL H5_ATTR_CONST int Nflock(int fd, int operation); #ifndef HDstrlen #define HDstrlen(S) strlen(S) #endif +#ifndef HDstrnlen +#define HDstrnlen(S,L) strnlen(S,L) +#endif #ifndef HDstrncat #define HDstrncat(X, Y, Z) strncat(X, Y, Z) #endif