Better size calculations for struct PixelWeight
This CL implements many of the suggestions I made on
https://pdfium-review.googlesource.com/c/pdfium/+/81650
so that those changes can be made independently.
-- Use safe arithmetic when calculating allocation sizes.
-- Make calculations robust in face of changes to PixelWeight.
-- Add sanity check on `dest_max` argument.
Change-Id: Idd4fef6f6784fbd6175eb7d7927398e0907e9dc5
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/81671
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcodec/progressive_decoder.cpp b/core/fxcodec/progressive_decoder.cpp
index f797a4c..764e5fa 100644
--- a/core/fxcodec/progressive_decoder.cpp
+++ b/core/fxcodec/progressive_decoder.cpp
@@ -71,9 +71,12 @@
void ProgressiveDecoder::WeightTable::Calc(int dest_len, int src_len) {
CHECK_GE(dest_len, 0);
double scale = static_cast<double>(src_len) / dest_len;
- m_ItemSize = (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs(scale)) + 1));
+ const size_t weight_count = static_cast<size_t>(ceil(fabs(scale))) + 1;
+ m_ItemSize = PixelWeight::TotalBytesForWeightCount(weight_count);
+ FX_SAFE_SIZE_T safe_size = m_ItemSize;
+ safe_size *= dest_len;
+ m_pWeightTables.resize(safe_size.ValueOrDie());
m_DestMin = 0;
- m_pWeightTables.resize(dest_len * m_ItemSize + 4);
if (fabs(scale) < 1.0) {
for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) {
PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
@@ -144,10 +147,11 @@
void ProgressiveDecoder::HorzTable::Calc(int dest_len, int src_len) {
CHECK_GE(dest_len, 0);
+ m_ItemSize = PixelWeight::TotalBytesForWeightCount(2);
+ FX_SAFE_SIZE_T safe_size = m_ItemSize;
+ safe_size *= dest_len;
+ m_pWeightTables.resize(safe_size.ValueOrDie(), 0);
double scale = (double)dest_len / (double)src_len;
- m_ItemSize = sizeof(int) * 4;
- int size = dest_len * m_ItemSize + 4;
- m_pWeightTables.resize(size, 0);
if (scale > 1) {
int pre_dest_col = 0;
for (int src_col = 0; src_col < src_len; src_col++) {
@@ -198,10 +202,11 @@
void ProgressiveDecoder::VertTable::Calc(int dest_len, int src_len) {
CHECK_GE(dest_len, 0);
+ m_ItemSize = PixelWeight::TotalBytesForWeightCount(2);
+ FX_SAFE_SIZE_T safe_size = m_ItemSize;
+ safe_size *= dest_len;
+ m_pWeightTables.resize(safe_size.ValueOrDie(), 0);
double scale = (double)dest_len / (double)src_len;
- m_ItemSize = sizeof(int) * 4;
- int size = dest_len * m_ItemSize + 4;
- m_pWeightTables.resize(size, 0);
if (scale <= 1) {
for (int dest_row = 0; dest_row < dest_len; dest_row++) {
PixelWeight* pWeight = GetPixelWeight(dest_row);
diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp
index fd0aed2..222dd29 100644
--- a/core/fxge/dib/cstretchengine.cpp
+++ b/core/fxge/dib/cstretchengine.cpp
@@ -29,8 +29,8 @@
CStretchEngine::CWeightTable::~CWeightTable() = default;
-size_t CStretchEngine::CWeightTable::GetPixelWeightSize() const {
- return m_ItemSize / sizeof(int) - 2;
+size_t CStretchEngine::CWeightTable::GetPixelWeightCount() const {
+ return PixelWeight::WeightCountFromTotalBytes(m_ItemSizeBytes);
}
bool CStretchEngine::CWeightTable::Calc(int dest_len,
@@ -40,23 +40,35 @@
int src_min,
int src_max,
const FXDIB_ResampleOptions& options) {
+ static constexpr size_t kBytesPerWeight = sizeof(PixelWeight::m_Weights[0]);
+ static constexpr size_t kMaxTableBytesAllowed = (1 << 30) - kBytesPerWeight;
+
// Help the compiler realize that these can't change during a loop iteration:
const bool bilinear = options.bInterpolateBilinear;
+ m_DestMin = 0;
+ m_ItemSizeBytes = 0;
+ m_WeightTablesSizeBytes = 0;
m_WeightTables.clear();
- m_dwWeightTablesSize = 0;
- const double scale = static_cast<float>(src_len) / dest_len;
- const double base = dest_len < 0 ? src_len : 0;
- m_ItemSize = sizeof(int) * 2 +
- static_cast<int>(sizeof(int) *
- (ceil(fabs(static_cast<float>(scale))) + 1));
-
- m_DestMin = dest_min;
- if (dest_max - dest_min > static_cast<int>((1U << 30) - 4) / m_ItemSize)
+ if (dest_min > dest_max)
return false;
- m_dwWeightTablesSize = (dest_max - dest_min) * m_ItemSize + 4;
- m_WeightTables.resize(m_dwWeightTablesSize);
+ m_DestMin = dest_min;
+
+ // TODO(tsepez): test results are sensitive to `scale` being a double
+ // rather than a float with an initial value no more precise than float.
+ const double scale = static_cast<float>(src_len) / dest_len;
+ const double base = dest_len < 0 ? src_len : 0;
+ const size_t weight_count = static_cast<size_t>(ceil(fabs(scale))) + 1;
+ m_ItemSizeBytes = PixelWeight::TotalBytesForWeightCount(weight_count);
+
+ const size_t dest_range = static_cast<size_t>(dest_max - dest_min);
+ const size_t kMaxTableItemsAllowed = kMaxTableBytesAllowed / m_ItemSizeBytes;
+ if (dest_range > kMaxTableItemsAllowed)
+ return false;
+
+ m_WeightTablesSizeBytes = dest_range * m_ItemSizeBytes;
+ m_WeightTables.resize(m_WeightTablesSizeBytes);
if (options.bNoSmoothing || fabs(static_cast<float>(scale)) < 1.0f) {
for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
@@ -116,7 +128,7 @@
break;
}
size_t idx = j - start_i;
- if (idx >= GetPixelWeightSize())
+ if (idx >= GetPixelWeightCount())
return false;
pixel_weights.m_Weights[idx] = FXSYS_roundf(weight * 65536);
@@ -129,7 +141,7 @@
int pixel) const {
DCHECK(pixel >= m_DestMin);
return reinterpret_cast<const PixelWeight*>(
- &m_WeightTables[(pixel - m_DestMin) * m_ItemSize]);
+ &m_WeightTables[(pixel - m_DestMin) * m_ItemSizeBytes]);
}
int* CStretchEngine::CWeightTable::GetValueFromPixelWeight(PixelWeight* pWeight,
@@ -138,7 +150,7 @@
return nullptr;
size_t idx = index - pWeight->m_SrcStart;
- return idx < GetPixelWeightSize() ? &pWeight->m_Weights[idx] : nullptr;
+ return idx < GetPixelWeightCount() ? &pWeight->m_Weights[idx] : nullptr;
}
CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap,
diff --git a/core/fxge/dib/cstretchengine.h b/core/fxge/dib/cstretchengine.h
index d631a59..fc23e85 100644
--- a/core/fxge/dib/cstretchengine.h
+++ b/core/fxge/dib/cstretchengine.h
@@ -57,12 +57,12 @@
}
int* GetValueFromPixelWeight(PixelWeight* pWeight, int index) const;
- size_t GetPixelWeightSize() const;
+ size_t GetPixelWeightCount() const;
private:
int m_DestMin = 0;
- int m_ItemSize = 0;
- size_t m_dwWeightTablesSize = 0;
+ size_t m_ItemSizeBytes = 0;
+ size_t m_WeightTablesSizeBytes = 0;
std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_WeightTables;
};
diff --git a/core/fxge/dib/fx_dib.cpp b/core/fxge/dib/fx_dib.cpp
index 1f67cd8..2d50e83 100644
--- a/core/fxge/dib/fx_dib.cpp
+++ b/core/fxge/dib/fx_dib.cpp
@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
#if defined(OS_WIN)
static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF),
@@ -32,6 +33,22 @@
}
}
+// static
+size_t PixelWeight::TotalBytesForWeightCount(size_t weight_count) {
+ const size_t extra_weights = weight_count > 0 ? weight_count - 1 : 0;
+ FX_SAFE_SIZE_T total_bytes = extra_weights;
+ total_bytes *= sizeof(m_Weights[0]);
+ total_bytes += sizeof(PixelWeight);
+ return total_bytes.ValueOrDie();
+}
+
+// static
+size_t PixelWeight::WeightCountFromTotalBytes(size_t total_bytes) {
+ const size_t extra_bytes =
+ total_bytes > sizeof(PixelWeight) ? total_bytes - sizeof(PixelWeight) : 0;
+ return 1 + extra_bytes / sizeof(m_Weights[0]);
+}
+
FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default;
bool FXDIB_ResampleOptions::HasAnyOptions() const {
diff --git a/core/fxge/dib/fx_dib.h b/core/fxge/dib/fx_dib.h
index 6ea7502..d88bd79 100644
--- a/core/fxge/dib/fx_dib.h
+++ b/core/fxge/dib/fx_dib.h
@@ -30,9 +30,12 @@
};
struct PixelWeight {
+ static size_t TotalBytesForWeightCount(size_t weight_count);
+ static size_t WeightCountFromTotalBytes(size_t total_bytes);
+
int m_SrcStart;
int m_SrcEnd;
- int m_Weights[1];
+ int m_Weights[1]; // Not really 1, variable size.
};
using FX_ARGB = uint32_t;