mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 11:26:39 +00:00
[runtime_image] Prevent integer overflow in image buffer size calculation (#17624)
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#ifdef USE_RUNTIME_IMAGE_BMP
|
||||
#include "bmp_decoder.h"
|
||||
@@ -19,6 +21,13 @@ namespace esphome::runtime_image {
|
||||
|
||||
static const char *const TAG = "runtime_image";
|
||||
|
||||
// Widest supported format is 4 bytes/pixel, so 32767 * 32767 * 4 still fits a 32-bit size_t
|
||||
static constexpr int MAX_IMAGE_DIMENSION = 32767;
|
||||
static constexpr int MAX_IMAGE_BPP = 32;
|
||||
static_assert((static_cast<uint64_t>(MAX_IMAGE_BPP) * MAX_IMAGE_DIMENSION + 7) / 8 * MAX_IMAGE_DIMENSION <=
|
||||
std::numeric_limits<size_t>::max(),
|
||||
"MAX_IMAGE_DIMENSION must keep the worst-case buffer size within size_t");
|
||||
|
||||
inline bool is_color_on(const Color &color) {
|
||||
// This produces the most accurate monochrome conversion, but is slightly slower.
|
||||
// return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) > 127;
|
||||
@@ -257,6 +266,11 @@ void RuntimeImage::release_buffer_() {
|
||||
size_t RuntimeImage::resize_buffer_(int width, int height) {
|
||||
size_t new_size = this->get_buffer_size_(width, height);
|
||||
|
||||
if (new_size == 0) {
|
||||
ESP_LOGE(TAG, "Refusing to allocate buffer for invalid image dimensions %dx%d", width, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->buffer_ && this->buffer_width_ == width && this->buffer_height_ == height) {
|
||||
// Buffer already allocated with correct size
|
||||
return new_size;
|
||||
@@ -287,11 +301,15 @@ size_t RuntimeImage::resize_buffer_(int width, int height) {
|
||||
}
|
||||
|
||||
size_t RuntimeImage::get_buffer_size_(int width, int height) const {
|
||||
// Dimensions come from a remote image header; reject absurd values so the size math cannot overflow
|
||||
if (width <= 0 || height <= 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) {
|
||||
return 0;
|
||||
}
|
||||
if (this->get_type() == image::IMAGE_TYPE_RGB565 && this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
|
||||
// Add extra alpha channel for RGB565 with alpha
|
||||
return width * height * 3;
|
||||
return static_cast<size_t>(width) * height * 3;
|
||||
}
|
||||
return (this->get_bpp() * width + 7u) / 8u * height;
|
||||
return (static_cast<size_t>(this->get_bpp()) * width + 7u) / 8u * height;
|
||||
}
|
||||
|
||||
int RuntimeImage::get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; }
|
||||
|
||||
Reference in New Issue
Block a user