Commit e561521f for libheif
commit e561521f2382cf8f49b581a6044882390b8cc7a5
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Dec 26 12:51:13 2025 +0100
regions: check mask allocations against integer overflows
diff --git a/libheif/region.cc b/libheif/region.cc
index 63e2914d..a52c988f 100644
--- a/libheif/region.cc
+++ b/libheif/region.cc
@@ -27,7 +27,8 @@
#include <utility>
-Error RegionItem::parse(const std::vector<uint8_t>& data)
+Error RegionItem::parse(const std::vector<uint8_t>& data,
+ const heif_security_limits* limits)
{
if (data.size() < 8) {
return Error(heif_error_Invalid_input, heif_suberror_Invalid_region_data,
@@ -46,16 +47,13 @@ Error RegionItem::parse(const std::vector<uint8_t>& data)
return Error(heif_error_Invalid_input, heif_suberror_Invalid_region_data,
"Region data incomplete");
}
- reference_width =
- ((data[2] << 24) | (data[3] << 16) | (data[4] << 8) | (data[5]));
-
- reference_height =
- ((data[6] << 24) | (data[7] << 16) | (data[8] << 8) | (data[9]));
+ reference_width = four_bytes_to_uint32(data[2], data[3], data[4], data[5]);
+ reference_height = four_bytes_to_uint32(data[6], data[7], data[8], data[9]);
dataOffset = 10;
}
else {
- reference_width = ((data[2] << 8) | (data[3]));
- reference_height = ((data[4] << 8) | (data[5]));
+ reference_width = four_bytes_to_uint32(0, 0, data[2], data[3]);
+ reference_height = four_bytes_to_uint32(0, 0, data[4], data[5]);
dataOffset = 6;
}
@@ -105,7 +103,7 @@ Error RegionItem::parse(const std::vector<uint8_t>& data)
continue;
}
- Error error = region->parse(data, field_size, &dataOffset);
+ Error error = region->parse(data, field_size, &dataOffset, limits);
if (error) {
return error;
}
@@ -178,14 +176,14 @@ uint32_t RegionGeometry::parse_unsigned(const std::vector<uint8_t>& data,
{
uint32_t x;
if (field_size == 32) {
- x = ((static_cast<uint32_t>(data[*dataOffset + 0]) << 24) |
- (static_cast<uint32_t>(data[*dataOffset + 1]) << 16) |
- (static_cast<uint32_t>(data[*dataOffset + 2]) << 8) |
- (static_cast<uint32_t>(data[*dataOffset + 3])));
+ x = four_bytes_to_uint32(data[*dataOffset + 0],
+ data[*dataOffset + 1],
+ data[*dataOffset + 2],
+ data[*dataOffset + 3]);
*dataOffset = *dataOffset + 4;
}
else {
- x = ((data[*dataOffset] << 8) | (data[*dataOffset + 1]));
+ x = four_bytes_to_uint32(0, 0, data[*dataOffset], data[*dataOffset + 1]);
*dataOffset = *dataOffset + 2;
}
return x;
@@ -204,7 +202,8 @@ int32_t RegionGeometry::parse_signed(const std::vector<uint8_t>& data,
Error RegionGeometry_Point::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
unsigned int bytesRequired = (field_size / 8) * 2;
if (data.size() - *dataOffset < bytesRequired) {
@@ -245,7 +244,8 @@ void RegionGeometry_Point::encode(StreamWriter& writer, int field_size_bytes) co
Error RegionGeometry_Rectangle::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
unsigned int bytesRequired = (field_size / 8) * 4;
if (data.size() - *dataOffset < bytesRequired) {
@@ -277,7 +277,8 @@ void RegionGeometry_Rectangle::encode(StreamWriter& writer, int field_size_bytes
Error RegionGeometry_Ellipse::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
unsigned int bytesRequired = (field_size / 8) * 4;
if (data.size() - *dataOffset < bytesRequired) {
@@ -310,7 +311,8 @@ void RegionGeometry_Ellipse::encode(StreamWriter& writer, int field_size_bytes)
Error RegionGeometry_Polygon::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
uint32_t bytesRequired1 = (field_size / 8) * 1;
if (data.size() - *dataOffset < bytesRequired1) {
@@ -328,6 +330,18 @@ Error RegionGeometry_Polygon::parse(const std::vector<uint8_t>& data,
"Insufficient data remaining for polygon");
}
+ if (UINT32_MAX / numPoints < sizeof(Point)) {
+ return {
+ heif_error_Memory_allocation_error,
+ heif_suberror_Unspecified,
+ "Region polygon size exceeds integer range."
+ };
+ }
+
+ if (auto err = m_memory_handle.alloc(numPoints * sizeof(Point), limits, "region polygon")) {
+ return err;
+ }
+
for (uint32_t i = 0; i < numPoints; i++) {
Point p;
p.x = parse_signed(data, field_size, dataOffset);
@@ -341,7 +355,8 @@ Error RegionGeometry_Polygon::parse(const std::vector<uint8_t>& data,
Error RegionGeometry_ReferencedMask::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
unsigned int bytesRequired = (field_size / 8) * 4;
if (data.size() - *dataOffset < bytesRequired) {
@@ -396,7 +411,8 @@ void RegionGeometry_Polygon::encode(StreamWriter& writer, int field_size_bytes)
Error RegionGeometry_InlineMask::parse(const std::vector<uint8_t>& data,
int field_size,
- unsigned int* dataOffset)
+ unsigned int* dataOffset,
+ const heif_security_limits* limits)
{
unsigned int bytesRequired = (field_size / 8) * 4 + 1;
if (data.size() - *dataOffset < bytesRequired) {
@@ -409,15 +425,38 @@ Error RegionGeometry_InlineMask::parse(const std::vector<uint8_t>& data,
height = parse_unsigned(data, field_size, dataOffset);
uint8_t mask_coding_method = data[*dataOffset];
*dataOffset = *dataOffset + 1;
+
if (mask_coding_method != 0) {
return Error(heif_error_Invalid_input, heif_suberror_Invalid_region_data,
"Deflate compressed inline mask is not yet supported");
}
+
+ if (width / 8 + 1 > UINT32_MAX / height) {
+ return {
+ heif_error_Memory_allocation_error,
+ heif_suberror_Unspecified,
+ "Mask image size exceeds maximum integer range."
+ };
+ }
+
+ if (limits->max_image_size_pixels / width < height) {
+ return {
+ heif_error_Memory_allocation_error,
+ heif_suberror_Security_limit_exceeded,
+ "Inline mask image exceeds maximum image size."
+ };
+ }
+
unsigned int additionalBytesRequired = width * height / 8;
if (data.size() - *dataOffset < additionalBytesRequired) {
return Error(heif_error_Invalid_input, heif_suberror_Invalid_region_data,
"Insufficient data remaining for inline mask region data[]");
}
+
+ if (auto err = m_memory_handle.alloc(additionalBytesRequired, limits, "region mask")) {
+ return err;
+ }
+
mask_data.resize(additionalBytesRequired);
std::copy(data.begin() + *dataOffset, data.begin() + *dataOffset + additionalBytesRequired, mask_data.begin());
return Error::Ok;
diff --git a/libheif/region.h b/libheif/region.h
index 7509c656..b21c7a5a 100644
--- a/libheif/region.h
+++ b/libheif/region.h
@@ -38,7 +38,7 @@ public:
RegionItem(heif_item_id itemId, uint32_t ref_width, uint32_t ref_height)
: item_id(itemId), reference_width(ref_width), reference_height(ref_height) {}
- Error parse(const std::vector<uint8_t>& data);
+ Error parse(const std::vector<uint8_t>& data, const heif_security_limits*);
Error encode(std::vector<uint8_t>& result) const;
@@ -67,7 +67,8 @@ public:
virtual heif_region_type getRegionType() = 0;
- virtual Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset) = 0;
+ virtual Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) = 0;
virtual bool encode_needs_32bit() const { return false; }
@@ -82,7 +83,8 @@ protected:
class RegionGeometry_Point : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
bool encode_needs_32bit() const override;
@@ -96,7 +98,8 @@ public:
class RegionGeometry_Rectangle : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
bool encode_needs_32bit() const override;
@@ -111,7 +114,8 @@ public:
class RegionGeometry_Ellipse : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
bool encode_needs_32bit() const override;
@@ -126,7 +130,8 @@ public:
class RegionGeometry_Polygon : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
bool encode_needs_32bit() const override;
@@ -144,12 +149,14 @@ public:
bool closed = true;
std::vector<Point> points;
+ MemoryHandle m_memory_handle;
};
class RegionGeometry_ReferencedMask : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int *dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
void encode(StreamWriter&, int field_size_bytes) const override;
@@ -163,13 +170,15 @@ public:
class RegionGeometry_InlineMask : public RegionGeometry
{
public:
- Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int *dataOffset) override;
+ Error parse(const std::vector<uint8_t>& data, int field_size, unsigned int* dataOffset,
+ const heif_security_limits* limits) override;
void encode(StreamWriter&, int field_size_bytes) const override;
int32_t x,y;
uint32_t width, height;
std::vector<uint8_t> mask_data;
+ MemoryHandle m_memory_handle;
heif_region_type getRegionType() override { return heif_region_type_inline_mask; }
};