From 4f184f89f9ab6785a6c90fd238dbaa6d901d3505 Mon Sep 17 00:00:00 2001 From: Kai Dietrich <kai.dietrich@meelogic.com> Date: Thu, 22 Oct 2020 08:16:07 +0200 Subject: [PATCH] Fix multiple heap buffer overflows The size calculation pattern (size_t)size_x*size_y*size_z*size_c can overflow the resulting size_t. Especially on 32bit size_t platforms this is trivial and can be achieved using a simple PNM image, e.g. the following ASCII PNM would allocate only 6 byte and result in a trivial arbitrary heap write: P3 2147483649 2 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 ... --- CImg.h | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/CImg.h b/CImg.h index 0827ece..8294527 100644 --- a/CImg.h +++ b/CImg.h @@ -11764,6 +11764,27 @@ namespace cimg_library_suffixed { **/ CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} + size_t _safe_size(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c) const + { + const unsigned int dim[4] = {size_x, size_y, size_z, size_c}; + size_t size = 1; + int overflows = 0; + for (int d = 0; d < sizeof(dim)/sizeof(dim[0]); d++) { + if (dim[d]>1 && size*dim[d] <= size) { overflows++; } + size *= dim[d]; + } + if (sizeof(T)>1 && size*sizeof(T) <= size) { overflows++; } + if (overflows != 0) { + throw CImgArgumentException(_cimg_instance + "_safe_size(): Invalid size - size_t overflow" + "(%u,%u,%u,%u).", + cimg_instance, + size_x, size_y, size_z, size_c); + } + return size; + } + //! Construct image with specified size. /** \param size_x Image width(). @@ -11790,7 +11811,7 @@ namespace cimg_library_suffixed { explicit CImg(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1): _is_shared(false) { - size_t siz = (size_t)size_x*size_y*size_z*size_c; + size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; try { _data = new T[siz]; } catch (...) { @@ -11822,7 +11843,7 @@ namespace cimg_library_suffixed { CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T& value): _is_shared(false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; try { _data = new T[siz]; } catch (...) { @@ -11883,7 +11904,7 @@ namespace cimg_library_suffixed { } \ } assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int); + _CImg_stdarg(*this,value0,value1,_safe_size(size_x,size_y,size_z,size_c),int); } #if cimg_use_cpp11==1 @@ -12012,7 +12033,7 @@ namespace cimg_library_suffixed { const double value0, const double value1, ...): _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double); + _CImg_stdarg(*this,value0,value1,_safe_size(size_x,size_y,size_z,size_c),double); } //! Construct image with specified size and initialize pixel values from a value string. @@ -12047,7 +12068,7 @@ namespace cimg_library_suffixed { **/ CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const char *const values, const bool repeat_values):_is_shared(false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; try { _data = new T[siz]; } catch (...) { @@ -12103,7 +12124,7 @@ namespace cimg_library_suffixed { cimg_instance, size_x,size_y,size_z,size_c,CImg<t>::pixel_type()); } - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (values && siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; try { _data = new T[siz]; } catch (...) { @@ -12122,7 +12143,7 @@ namespace cimg_library_suffixed { //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (values && siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; if (_is_shared) _data = const_cast<T*>(values); @@ -12423,7 +12444,7 @@ namespace cimg_library_suffixed { **/ CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (!siz) return assign(); const size_t curr_siz = (size_t)size(); if (siz!=curr_siz) { @@ -12466,7 +12487,7 @@ namespace cimg_library_suffixed { const unsigned int size_z, const unsigned int size_c, const int value0, const int value1, ...) { assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int); + _CImg_stdarg(*this,value0,value1,_safe_size(size_x,size_y,size_z,size_c),int); return *this; } @@ -12478,7 +12499,7 @@ namespace cimg_library_suffixed { const unsigned int size_z, const unsigned int size_c, const double value0, const double value1, ...) { assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double); + _CImg_stdarg(*this,value0,value1,_safe_size(size_x,size_y,size_z,size_c),double); return *this; } @@ -12499,7 +12520,7 @@ namespace cimg_library_suffixed { template<typename t> CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (!values || !siz) return assign(); assign(size_x,size_y,size_z,size_c); const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); @@ -12509,7 +12530,7 @@ namespace cimg_library_suffixed { //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (!values || !siz) return assign(); const size_t curr_siz = (size_t)size(); if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); @@ -12549,7 +12570,7 @@ namespace cimg_library_suffixed { //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const bool is_shared) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; + const size_t siz = _safe_size(size_x,size_y,size_z,size_c); if (!values || !siz) return assign(); if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } else {