bug #363 - check for integer overflow in size computations

This commit is contained in:
Benoit Jacob 2011-10-16 16:12:19 -04:00
parent 074755a27c
commit 8f7fb19907
5 changed files with 140 additions and 17 deletions

View File

@ -167,7 +167,7 @@
#include <intrin.h> #include <intrin.h>
#endif #endif
#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !defined(EIGEN_NO_EXCEPTIONS) #if defined(_CPPUNWIND) || defined(__EXCEPTIONS)
#define EIGEN_EXCEPTIONS #define EIGEN_EXCEPTIONS
#endif #endif

View File

@ -34,6 +34,19 @@
namespace internal { namespace internal {
template<typename Index>
inline void check_rows_cols_for_overflow(Index rows, Index cols)
{
// http://hg.mozilla.org/mozilla-central/file/6c8a909977d3/xpcom/ds/CheckedInt.h#l242
// we assume Index is signed
Index max_index = (size_t(1) << (8 * sizeof(Index) - 1)) - 1; // assume Index is signed
bool error = (rows < 0 || cols < 0) ? true
: (rows == 0 || cols == 0) ? false
: (rows > max_index / cols);
if (error)
throw_std_bad_alloc();
}
template <typename Derived, typename OtherDerived = Derived, bool IsVector = static_cast<bool>(Derived::IsVectorAtCompileTime)> struct conservative_resize_like_impl; template <typename Derived, typename OtherDerived = Derived, bool IsVector = static_cast<bool>(Derived::IsVectorAtCompileTime)> struct conservative_resize_like_impl;
template<typename MatrixTypeA, typename MatrixTypeB, bool SwapPointers> struct matrix_swap_impl; template<typename MatrixTypeA, typename MatrixTypeB, bool SwapPointers> struct matrix_swap_impl;
@ -200,11 +213,13 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
EIGEN_STRONG_INLINE void resize(Index rows, Index cols) EIGEN_STRONG_INLINE void resize(Index rows, Index cols)
{ {
#ifdef EIGEN_INITIALIZE_MATRICES_BY_ZERO #ifdef EIGEN_INITIALIZE_MATRICES_BY_ZERO
internal::check_rows_cols_for_overflow(rows, cols);
Index size = rows*cols; Index size = rows*cols;
bool size_changed = size != this->size(); bool size_changed = size != this->size();
m_storage.resize(size, rows, cols); m_storage.resize(size, rows, cols);
if(size_changed) EIGEN_INITIALIZE_BY_ZERO_IF_THAT_OPTION_IS_ENABLED if(size_changed) EIGEN_INITIALIZE_BY_ZERO_IF_THAT_OPTION_IS_ENABLED
#else #else
internal::check_rows_cols_for_overflow(rows, cols);
m_storage.resize(rows*cols, rows, cols); m_storage.resize(rows*cols, rows, cols);
#endif #endif
} }
@ -273,6 +288,7 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
EIGEN_STRONG_INLINE void resizeLike(const EigenBase<OtherDerived>& _other) EIGEN_STRONG_INLINE void resizeLike(const EigenBase<OtherDerived>& _other)
{ {
const OtherDerived& other = _other.derived(); const OtherDerived& other = _other.derived();
internal::check_rows_cols_for_overflow(other.rows(), other.cols());
const Index othersize = other.rows()*other.cols(); const Index othersize = other.rows()*other.cols();
if(RowsAtCompileTime == 1) if(RowsAtCompileTime == 1)
{ {
@ -417,6 +433,7 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
: m_storage(other.derived().rows() * other.derived().cols(), other.derived().rows(), other.derived().cols()) : m_storage(other.derived().rows() * other.derived().cols(), other.derived().rows(), other.derived().cols())
{ {
_check_template_params(); _check_template_params();
internal::check_rows_cols_for_overflow(other.derived().rows(), other.derived().cols());
Base::operator=(other.derived()); Base::operator=(other.derived());
} }
@ -581,6 +598,7 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
{ {
eigen_assert(rows >= 0 && (RowsAtCompileTime == Dynamic || RowsAtCompileTime == rows) eigen_assert(rows >= 0 && (RowsAtCompileTime == Dynamic || RowsAtCompileTime == rows)
&& cols >= 0 && (ColsAtCompileTime == Dynamic || ColsAtCompileTime == cols)); && cols >= 0 && (ColsAtCompileTime == Dynamic || ColsAtCompileTime == cols));
internal::check_rows_cols_for_overflow(rows, cols);
m_storage.resize(rows*cols,rows,cols); m_storage.resize(rows*cols,rows,cols);
EIGEN_INITIALIZE_BY_ZERO_IF_THAT_OPTION_IS_ENABLED EIGEN_INITIALIZE_BY_ZERO_IF_THAT_OPTION_IS_ENABLED
} }
@ -638,6 +656,7 @@ struct internal::conservative_resize_like_impl
if ( ( Derived::IsRowMajor && _this.cols() == cols) || // row-major and we change only the number of rows if ( ( Derived::IsRowMajor && _this.cols() == cols) || // row-major and we change only the number of rows
(!Derived::IsRowMajor && _this.rows() == rows) ) // column-major and we change only the number of columns (!Derived::IsRowMajor && _this.rows() == rows) ) // column-major and we change only the number of columns
{ {
internal::check_rows_cols_for_overflow(rows, cols);
_this.derived().m_storage.conservativeResize(rows*cols,rows,cols); _this.derived().m_storage.conservativeResize(rows*cols,rows,cols);
} }
else else

View File

@ -82,6 +82,15 @@
namespace internal { namespace internal {
inline void throw_std_bad_alloc()
{
#ifdef EIGEN_EXCEPTIONS
throw std::bad_alloc();
#else
new int[size_t(-1)];
#endif
}
/***************************************************************************** /*****************************************************************************
*** Implementation of handmade aligned functions *** *** Implementation of handmade aligned functions ***
*****************************************************************************/ *****************************************************************************/
@ -192,7 +201,7 @@ inline void check_that_malloc_is_allowed()
#endif #endif
/** \internal Allocates \a size bytes. The returned pointer is guaranteed to have 16 bytes alignment. /** \internal Allocates \a size bytes. The returned pointer is guaranteed to have 16 bytes alignment.
* On allocation error, the returned pointer is null, and if exceptions are enabled then a std::bad_alloc is thrown. * On allocation error, the returned pointer is null, and std::bad_alloc is thrown.
*/ */
inline void* aligned_malloc(size_t size) inline void* aligned_malloc(size_t size)
{ {
@ -213,10 +222,9 @@ inline void* aligned_malloc(size_t size)
result = handmade_aligned_malloc(size); result = handmade_aligned_malloc(size);
#endif #endif
#ifdef EIGEN_EXCEPTIONS if(!result && size)
if(result == 0) throw_std_bad_alloc();
throw std::bad_alloc();
#endif
return result; return result;
} }
@ -241,7 +249,7 @@ inline void aligned_free(void *ptr)
/** /**
* \internal * \internal
* \brief Reallocates an aligned block of memory. * \brief Reallocates an aligned block of memory.
* \throws std::bad_alloc if EIGEN_EXCEPTIONS are defined. * \throws std::bad_alloc on allocation failure
**/ **/
inline void* aligned_realloc(void *ptr, size_t new_size, size_t old_size) inline void* aligned_realloc(void *ptr, size_t new_size, size_t old_size)
{ {
@ -269,10 +277,9 @@ inline void* aligned_realloc(void *ptr, size_t new_size, size_t old_size)
result = handmade_aligned_realloc(ptr,new_size,old_size); result = handmade_aligned_realloc(ptr,new_size,old_size);
#endif #endif
#ifdef EIGEN_EXCEPTIONS if (!result && new_size)
if (result==0 && new_size!=0) throw_std_bad_alloc();
throw std::bad_alloc();
#endif
return result; return result;
} }
@ -281,7 +288,7 @@ inline void* aligned_realloc(void *ptr, size_t new_size, size_t old_size)
*****************************************************************************/ *****************************************************************************/
/** \internal Allocates \a size bytes. If Align is true, then the returned ptr is 16-byte-aligned. /** \internal Allocates \a size bytes. If Align is true, then the returned ptr is 16-byte-aligned.
* On allocation error, the returned pointer is null, and if exceptions are enabled then a std::bad_alloc is thrown. * On allocation error, the returned pointer is null, and a std::bad_alloc is thrown.
*/ */
template<bool Align> inline void* conditional_aligned_malloc(size_t size) template<bool Align> inline void* conditional_aligned_malloc(size_t size)
{ {
@ -293,9 +300,8 @@ template<> inline void* conditional_aligned_malloc<false>(size_t size)
check_that_malloc_is_allowed(); check_that_malloc_is_allowed();
void *result = std::malloc(size); void *result = std::malloc(size);
#ifdef EIGEN_EXCEPTIONS if(!result && size)
if(!result) throw std::bad_alloc(); throw_std_bad_alloc();
#endif
return result; return result;
} }
@ -347,18 +353,27 @@ template<typename T> inline void destruct_elements_of_array(T *ptr, size_t size)
*** Implementation of aligned new/delete-like functions *** *** Implementation of aligned new/delete-like functions ***
*****************************************************************************/ *****************************************************************************/
template<typename T>
inline void check_size_for_overflow(size_t size)
{
if(size > size_t(-1) / sizeof(T))
throw_std_bad_alloc();
}
/** \internal Allocates \a size objects of type T. The returned pointer is guaranteed to have 16 bytes alignment. /** \internal Allocates \a size objects of type T. The returned pointer is guaranteed to have 16 bytes alignment.
* On allocation error, the returned pointer is undefined, but if exceptions are enabled then a std::bad_alloc is thrown. * On allocation error, the returned pointer is undefined, but a std::bad_alloc is thrown.
* The default constructor of T is called. * The default constructor of T is called.
*/ */
template<typename T> inline T* aligned_new(size_t size) template<typename T> inline T* aligned_new(size_t size)
{ {
check_size_for_overflow<T>(size);
T *result = reinterpret_cast<T*>(aligned_malloc(sizeof(T)*size)); T *result = reinterpret_cast<T*>(aligned_malloc(sizeof(T)*size));
return construct_elements_of_array(result, size); return construct_elements_of_array(result, size);
} }
template<typename T, bool Align> inline T* conditional_aligned_new(size_t size) template<typename T, bool Align> inline T* conditional_aligned_new(size_t size)
{ {
check_size_for_overflow<T>(size);
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size)); T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
return construct_elements_of_array(result, size); return construct_elements_of_array(result, size);
} }
@ -383,6 +398,8 @@ template<typename T, bool Align> inline void conditional_aligned_delete(T *ptr,
template<typename T, bool Align> inline T* conditional_aligned_realloc_new(T* pts, size_t new_size, size_t old_size) template<typename T, bool Align> inline T* conditional_aligned_realloc_new(T* pts, size_t new_size, size_t old_size)
{ {
check_size_for_overflow<T>(new_size);
check_size_for_overflow<T>(old_size);
if(new_size < old_size) if(new_size < old_size)
destruct_elements_of_array(pts+new_size, old_size-new_size); destruct_elements_of_array(pts+new_size, old_size-new_size);
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size)); T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
@ -394,6 +411,7 @@ template<typename T, bool Align> inline T* conditional_aligned_realloc_new(T* pt
template<typename T, bool Align> inline T* conditional_aligned_new_auto(size_t size) template<typename T, bool Align> inline T* conditional_aligned_new_auto(size_t size)
{ {
check_size_for_overflow<T>(size);
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size)); T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
if(NumTraits<T>::RequireInitialization) if(NumTraits<T>::RequireInitialization)
construct_elements_of_array(result, size); construct_elements_of_array(result, size);
@ -402,6 +420,8 @@ template<typename T, bool Align> inline T* conditional_aligned_new_auto(size_t s
template<typename T, bool Align> inline T* conditional_aligned_realloc_new_auto(T* pts, size_t new_size, size_t old_size) template<typename T, bool Align> inline T* conditional_aligned_realloc_new_auto(T* pts, size_t new_size, size_t old_size)
{ {
check_size_for_overflow<T>(new_size);
check_size_for_overflow<T>(old_size);
if(NumTraits<T>::RequireInitialization && (new_size < old_size)) if(NumTraits<T>::RequireInitialization && (new_size < old_size))
destruct_elements_of_array(pts+new_size, old_size-new_size); destruct_elements_of_array(pts+new_size, old_size-new_size);
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size)); T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
@ -536,6 +556,7 @@ template<typename T> class aligned_stack_memory_handler
#endif #endif
#define ei_declare_aligned_stack_constructed_variable(TYPE,NAME,SIZE,BUFFER) \ #define ei_declare_aligned_stack_constructed_variable(TYPE,NAME,SIZE,BUFFER) \
Eigen::internal::check_size_for_overflow<TYPE>(SIZE); \
TYPE* NAME = (BUFFER)!=0 ? (BUFFER) \ TYPE* NAME = (BUFFER)!=0 ? (BUFFER) \
: reinterpret_cast<TYPE*>( \ : reinterpret_cast<TYPE*>( \
(sizeof(TYPE)*SIZE<=EIGEN_STACK_ALLOCATION_LIMIT) ? EIGEN_ALIGNED_ALLOCA(sizeof(TYPE)*SIZE) \ (sizeof(TYPE)*SIZE<=EIGEN_STACK_ALLOCATION_LIMIT) ? EIGEN_ALIGNED_ALLOCA(sizeof(TYPE)*SIZE) \
@ -545,6 +566,7 @@ template<typename T> class aligned_stack_memory_handler
#else #else
#define ei_declare_aligned_stack_constructed_variable(TYPE,NAME,SIZE,BUFFER) \ #define ei_declare_aligned_stack_constructed_variable(TYPE,NAME,SIZE,BUFFER) \
Eigen::internal::check_size_for_overflow<TYPE>(SIZE); \
TYPE* NAME = (BUFFER)!=0 ? BUFFER : reinterpret_cast<TYPE*>(Eigen::internal::aligned_malloc(sizeof(TYPE)*SIZE)); \ TYPE* NAME = (BUFFER)!=0 ? BUFFER : reinterpret_cast<TYPE*>(Eigen::internal::aligned_malloc(sizeof(TYPE)*SIZE)); \
Eigen::internal::aligned_stack_memory_handler<TYPE> EIGEN_CAT(NAME,_stack_memory_destructor)((BUFFER)==0 ? NAME : 0,SIZE,true) Eigen::internal::aligned_stack_memory_handler<TYPE> EIGEN_CAT(NAME,_stack_memory_destructor)((BUFFER)==0 ? NAME : 0,SIZE,true)
@ -669,6 +691,7 @@ public:
pointer allocate( size_type num, const void* hint = 0 ) pointer allocate( size_type num, const void* hint = 0 )
{ {
EIGEN_UNUSED_VARIABLE(hint); EIGEN_UNUSED_VARIABLE(hint);
internal::check_size_for_overflow<T>(num);
return static_cast<pointer>( internal::aligned_malloc( num * sizeof(T) ) ); return static_cast<pointer>( internal::aligned_malloc( num * sizeof(T) ) );
} }

View File

@ -121,7 +121,7 @@ ei_add_test(nullary)
ei_add_test(nesting_ops "${CMAKE_CXX_FLAGS_DEBUG}") ei_add_test(nesting_ops "${CMAKE_CXX_FLAGS_DEBUG}")
ei_add_test(zerosized) ei_add_test(zerosized)
ei_add_test(dontalign) ei_add_test(dontalign)
ei_add_test(sizeoverflow)
ei_add_test(prec_inverse_4x4) ei_add_test(prec_inverse_4x4)
string(TOLOWER "${CMAKE_CXX_COMPILER}" cmake_cxx_compiler_tolower) string(TOLOWER "${CMAKE_CXX_COMPILER}" cmake_cxx_compiler_tolower)

81
test/sizeoverflow.cpp Normal file
View File

@ -0,0 +1,81 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#define VERIFY_THROWS_BADALLOC(a) { \
bool threw = false; \
try { \
a; \
} \
catch (std::bad_alloc&) { threw = true; } \
VERIFY(threw && "should have thrown bad_alloc: " #a); \
}
typedef DenseIndex Index;
template<typename MatrixType>
void triggerMatrixBadAlloc(Index rows, Index cols)
{
VERIFY_THROWS_BADALLOC( MatrixType m(rows, cols) );
VERIFY_THROWS_BADALLOC( MatrixType m; m.resize(rows, cols) );
VERIFY_THROWS_BADALLOC( MatrixType m; m.conservativeResize(rows, cols) );
}
template<typename VectorType>
void triggerVectorBadAlloc(Index size)
{
VERIFY_THROWS_BADALLOC( VectorType v(size) );
VERIFY_THROWS_BADALLOC( VectorType v; v.resize(size) );
VERIFY_THROWS_BADALLOC( VectorType v; v.conservativeResize(size) );
}
void test_sizeoverflow()
{
// there are 2 levels of overflow checking. first in PlainObjectBase.h we check for overflow in rows*cols computations.
// this is tested in tests of the form times_itself_gives_0 * times_itself_gives_0
// Then in Memory.h we check for overflow in size * sizeof(T) computations.
// this is tested in tests of the form times_4_gives_0 * sizeof(float)
size_t times_itself_gives_0 = size_t(1) << (8 * sizeof(Index) / 2);
VERIFY(times_itself_gives_0 * times_itself_gives_0 == 0);
size_t times_4_gives_0 = size_t(1) << (8 * sizeof(Index) - 2);
VERIFY(times_4_gives_0 * 4 == 0);
size_t times_8_gives_0 = size_t(1) << (8 * sizeof(Index) - 3);
VERIFY(times_8_gives_0 * 8 == 0);
triggerMatrixBadAlloc<MatrixXf>(times_itself_gives_0, times_itself_gives_0);
triggerMatrixBadAlloc<MatrixXf>(times_itself_gives_0 / 4, times_itself_gives_0);
triggerMatrixBadAlloc<MatrixXf>(times_4_gives_0, 1);
triggerMatrixBadAlloc<MatrixXd>(times_itself_gives_0, times_itself_gives_0);
triggerMatrixBadAlloc<MatrixXd>(times_itself_gives_0 / 8, times_itself_gives_0);
triggerMatrixBadAlloc<MatrixXd>(times_8_gives_0, 1);
triggerVectorBadAlloc<VectorXf>(times_4_gives_0);
triggerVectorBadAlloc<VectorXd>(times_8_gives_0);
}