fix: FontDecompressor OOM aborts on the render path (-fno-exceptions makes vector resize fatal) (#2526)
This commit is contained in:
@@ -33,12 +33,24 @@ void FontDecompressor::freePageBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FontDecompressor::freeHotGroup() {
|
void FontDecompressor::freeHotGroup() {
|
||||||
hotGroup.clear();
|
free(hotGroup);
|
||||||
hotGroup.shrink_to_fit();
|
hotGroup = nullptr;
|
||||||
|
hotGroupCapacity = 0;
|
||||||
hotGroupFont = nullptr;
|
hotGroupFont = nullptr;
|
||||||
hotGroupIndex = UINT16_MAX;
|
hotGroupIndex = UINT16_MAX;
|
||||||
hotGlyphBuf.clear();
|
free(hotGlyphBuf);
|
||||||
hotGlyphBuf.shrink_to_fit();
|
hotGlyphBuf = nullptr;
|
||||||
|
hotGlyphBufCapacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontDecompressor::ensureCapacity(uint8_t*& buf, uint32_t& capacity, uint32_t needed) {
|
||||||
|
if (capacity >= needed) return true;
|
||||||
|
// Grow-only, free-then-malloc: every caller fully rewrites the buffer after a grow, so the
|
||||||
|
// old contents are dead -- freeing first gives the allocator its best shot on a tight heap.
|
||||||
|
free(buf);
|
||||||
|
buf = static_cast<uint8_t*>(malloc(needed)); // owned by FontDecompressor, freed in freeHotGroup()
|
||||||
|
capacity = buf ? needed : 0;
|
||||||
|
return buf != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t FontDecompressor::getGroupIndex(const EpdFontData* fontData, uint32_t glyphIndex) {
|
uint16_t FontDecompressor::getGroupIndex(const EpdFontData* fontData, uint32_t glyphIndex) {
|
||||||
@@ -170,24 +182,20 @@ const uint8_t* FontDecompressor::getBitmap(const EpdFontData* fontData, const Ep
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if hot group already has this group decompressed — if not, decompress it
|
// Check if hot group already has this group decompressed — if not, decompress it
|
||||||
if (!(!hotGroup.empty() && hotGroupFont == fontData && hotGroupIndex == groupIndex)) {
|
if (!(hotGroup != nullptr && hotGroupFont == fontData && hotGroupIndex == groupIndex)) {
|
||||||
stats.cacheMisses++;
|
stats.cacheMisses++;
|
||||||
const EpdFontGroup& group = fontData->groups[groupIndex];
|
const EpdFontGroup& group = fontData->groups[groupIndex];
|
||||||
|
|
||||||
hotGroup.resize(group.uncompressedSize);
|
// ensureCapacity may free the buffer, so the cached-group identity dies with it either way.
|
||||||
if (hotGroup.empty()) {
|
hotGroupFont = nullptr;
|
||||||
|
hotGroupIndex = UINT16_MAX;
|
||||||
|
if (!ensureCapacity(hotGroup, hotGroupCapacity, group.uncompressedSize)) {
|
||||||
LOG_ERR("FDC", "Failed to allocate %u bytes for hot group %u", group.uncompressedSize, groupIndex);
|
LOG_ERR("FDC", "Failed to allocate %u bytes for hot group %u", group.uncompressedSize, groupIndex);
|
||||||
hotGroupFont = nullptr;
|
|
||||||
hotGroupIndex = UINT16_MAX;
|
|
||||||
stats.getBitmapTimeUs += micros() - tStart;
|
stats.getBitmapTimeUs += micros() - tStart;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decompressGroup(fontData, groupIndex, hotGroup.data(), group.uncompressedSize)) {
|
if (!decompressGroup(fontData, groupIndex, hotGroup, group.uncompressedSize)) {
|
||||||
hotGroup.clear();
|
|
||||||
hotGroup.shrink_to_fit();
|
|
||||||
hotGroupFont = nullptr;
|
|
||||||
hotGroupIndex = UINT16_MAX;
|
|
||||||
stats.getBitmapTimeUs += micros() - tStart;
|
stats.getBitmapTimeUs += micros() - tStart;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -200,18 +208,16 @@ const uint8_t* FontDecompressor::getBitmap(const EpdFontData* fontData, const Ep
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compact just the requested glyph from byte-aligned data into scratch buffer
|
// Compact just the requested glyph from byte-aligned data into scratch buffer
|
||||||
if (glyph->dataLength > hotGlyphBuf.size()) {
|
if (!ensureCapacity(hotGlyphBuf, hotGlyphBufCapacity, glyph->dataLength)) {
|
||||||
hotGlyphBuf.resize(glyph->dataLength);
|
LOG_ERR("FDC", "Failed to allocate %u bytes for glyph scratch", (unsigned)glyph->dataLength);
|
||||||
}
|
|
||||||
if (hotGlyphBuf.empty()) {
|
|
||||||
stats.getBitmapTimeUs += micros() - tStart;
|
stats.getBitmapTimeUs += micros() - tStart;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t alignedOff = getAlignedOffset(fontData, groupIndex, glyphIndex);
|
uint32_t alignedOff = getAlignedOffset(fontData, groupIndex, glyphIndex);
|
||||||
compactSingleGlyph(&hotGroup[alignedOff], hotGlyphBuf.data(), glyph->width, glyph->height);
|
compactSingleGlyph(&hotGroup[alignedOff], hotGlyphBuf, glyph->width, glyph->height);
|
||||||
stats.getBitmapTimeUs += micros() - tStart;
|
stats.getBitmapTimeUs += micros() - tStart;
|
||||||
return hotGlyphBuf.data();
|
return hotGlyphBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Prewarm: pre-decompress glyph bitmaps for a page of text ---
|
// --- Prewarm: pre-decompress glyph bitmaps for a page of text ---
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <InflateReader.h>
|
#include <InflateReader.h>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "EpdFontData.h"
|
#include "EpdFontData.h"
|
||||||
|
|
||||||
class FontDecompressor {
|
class FontDecompressor {
|
||||||
@@ -67,13 +65,22 @@ class FontDecompressor {
|
|||||||
|
|
||||||
// Hot group: last decompressed group (byte-aligned) for non-prewarmed fallback path.
|
// Hot group: last decompressed group (byte-aligned) for non-prewarmed fallback path.
|
||||||
// Kept in byte-aligned format; individual glyphs are compacted on demand into hotGlyphBuf.
|
// Kept in byte-aligned format; individual glyphs are compacted on demand into hotGlyphBuf.
|
||||||
|
// Nothrow high-water malloc buffers, NOT std::vector: getBitmap() runs on the render path,
|
||||||
|
// and under -fno-exceptions a vector resize that hits OOM abort()s the firmware instead of
|
||||||
|
// failing (field crash: hotGroup.resize() -> std::bad_alloc -> abort with ~11 KB free).
|
||||||
|
// ensureCapacity() returns false on OOM so the caller can skip the glyph gracefully.
|
||||||
const EpdFontData* hotGroupFont = nullptr;
|
const EpdFontData* hotGroupFont = nullptr;
|
||||||
uint16_t hotGroupIndex = UINT16_MAX;
|
uint16_t hotGroupIndex = UINT16_MAX;
|
||||||
std::vector<uint8_t> hotGroup;
|
uint8_t* hotGroup = nullptr; // owned; freed in freeHotGroup()/dtor
|
||||||
|
uint32_t hotGroupCapacity = 0;
|
||||||
|
|
||||||
// Scratch buffer for compacting a single glyph from the hot group.
|
// Scratch buffer for compacting a single glyph from the hot group.
|
||||||
// Valid until the next getBitmap() call.
|
// Valid until the next getBitmap() call. Same ownership/OOM contract as hotGroup.
|
||||||
std::vector<uint8_t> hotGlyphBuf;
|
uint8_t* hotGlyphBuf = nullptr;
|
||||||
|
uint32_t hotGlyphBufCapacity = 0;
|
||||||
|
|
||||||
|
// Grow (never shrink) an owned buffer to at least `needed` bytes; false on OOM, buffer freed.
|
||||||
|
static bool ensureCapacity(uint8_t*& buf, uint32_t& capacity, uint32_t needed);
|
||||||
|
|
||||||
void freePageBuffer();
|
void freePageBuffer();
|
||||||
void freeHotGroup();
|
void freeHotGroup();
|
||||||
|
|||||||
Reference in New Issue
Block a user