From 18c438bdf0be8ad98f968bb77b327d7a3f17a3f5 Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Wed, 9 Nov 2022 19:25:11 +0100 Subject: H5O__pline_decode() Make more resilient to out-of-bounds read (#2210) Malformed hdf5 files may have trunkated content which does not match the expected size. When this function attempts to decode these it may read past the end of the allocated space leading to heap overflows as bounds checking is incomplete. Make sure each element is within bounds before reading. This fixes CVE-2019-8396 / HDFFV-10712 / github bug #2209. 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 d1ac36e..20e9509 100644 --- a/src/H5Opline.c +++ b/src/H5Opline.c @@ -110,6 +110,7 @@ H5FL_DEFINE(H5O_pline_t); * *------------------------------------------------------------------------- */ + 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) @@ -131,6 +132,9 @@ H5O__pline_decode(H5F_t H5_ATTR_UNUSED *f, H5O_t H5_ATTR_UNUSED *open_oh, unsign HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") /* Version */ + if (p + 4 - 1 > p_end) /* 4 byte is minimum for all versions */ + HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, NULL, "ran off the end of the buffer: current p = %p, p_end = %p", + p + 4, p_end) pline->version = *p++; if (pline->version < H5O_PLINE_VERSION_1 || pline->version > H5O_PLINE_VERSION_LATEST) HGOTO_ERROR(H5E_PLINE, H5E_CANTLOAD, NULL, "bad version number for filter pipeline message") @@ -159,6 +163,9 @@ 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 */ + if (p + 6 - 1 > p_end) /* 6 bytes minimum */ + HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, NULL, + "ran off the end of the buffer: current p = %p, p_end = %p", p + 6, p_end) UINT16DECODE(p, filter->id); /* Length of filter name */ @@ -168,6 +175,9 @@ 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") + if (p + 4 - 1 > p_end) /* with name_length 4 bytes to go */ + HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, NULL, + "ran off the end of the buffer: current p = %p, p_end = %p", p + 4, p_end) } /* 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_OHDR, 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 2ed55bb..78df3fd 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -1415,6 +1415,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 -- cgit v0.12