diff --git a/src/NCollection/NCollection_WinHeapAllocator.cxx b/src/NCollection/NCollection_WinHeapAllocator.cxx index 39628f5cae..4efabbf3f7 100644 --- a/src/NCollection/NCollection_WinHeapAllocator.cxx +++ b/src/NCollection/NCollection_WinHeapAllocator.cxx @@ -73,7 +73,7 @@ void* NCollection_WinHeapAllocator::Allocate (const Standard_Size theSize) if (aResult == NULL) { char aBuf[128]; - Sprintf (aBuf, "Failed to allocate " PRIuPTR " bytes in local dynamic heap", theSize); + Sprintf (aBuf, "Failed to allocate %" PRIuPTR " bytes in local dynamic heap", theSize); Standard_OutOfMemory::Raise (aBuf); } return aResult; diff --git a/src/QABugs/QABugs_20.cxx b/src/QABugs/QABugs_20.cxx index 2a467e70d5..f9b2e2bd41 100644 --- a/src/QABugs/QABugs_20.cxx +++ b/src/QABugs/QABugs_20.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1251,6 +1252,82 @@ static Standard_Integer SurfaceGenOCC26675_1( Draw_Interpretor& theDI, return 0; } +namespace AllocTest +{ + // The test is based of occupying of all available virtual memory. + // Obviously it has no sense on 64-bit platforms. + + enum Status + { + NotApplicable = 0x1, + OUMCatchOK = 0x2, + OUMCatchFail = 0x4 + }; + + template int test() + { + // non-32-bit implementation + return NotApplicable; + } + + template<> int test<4>() + { + // 32-bit implementation + NCollection_List aList; + const Standard_Integer aBlockSizes[] = {100000, 10000, 10}; + int aStatus = 0; + + // start populate memory with blocks of large size, then + // smaller ones and so on according to content of the array aBlockSizes + for (size_t i=0; i < sizeof(aBlockSizes)/sizeof(int); i++) + { + try + { + for (;;) + aList.Append(Standard::Allocate(aBlockSizes[i])); + } + catch (Standard_Failure) + { + aStatus |= OUMCatchOK; + } + catch (...) + { + aStatus |= OUMCatchFail; + break; + } + } + // release all allocated blocks + for (NCollection_List::Iterator it(aList); it.More(); it.Next()) + { + Standard::Free(it.Value()); + } + return aStatus; + } +} + +//======================================================================= +//function : OCC24836 +//purpose : +//======================================================================= +static Standard_Integer OCC24836 (Draw_Interpretor& theDI, Standard_Integer n, const char** a) +{ + if (n != 1) + { + theDI << "Usage : " << a[0] << "\n"; + return 1; + } + + int aStatus = AllocTest::test(); + + if (aStatus & AllocTest::NotApplicable) + theDI << "This test case is not applicable for 64-bit and higher platforms\n"; + if (aStatus & AllocTest::OUMCatchOK) + theDI << "out-of-memory has been caught: OK\n"; + if (aStatus & AllocTest::OUMCatchFail) + theDI << "Error: out-of-memory is not always caught\n"; + return 0; +} + //======================================================================= //function : OCC27021 @@ -1346,6 +1423,7 @@ void QABugs::Commands_20(Draw_Interpretor& theCommands) { const char *group = "QABugs"; theCommands.Add ("OCC26675_1", "OCC26675_1 result", __FILE__, SurfaceGenOCC26675_1, group); + theCommands.Add ("OCC24836", "OCC24836", __FILE__, OCC24836, group); theCommands.Add("OCC27021", "OCC27021", __FILE__, OCC27021, group); return; diff --git a/src/Standard/FILES b/src/Standard/FILES index 7b437a03c0..0169bc9b43 100755 --- a/src/Standard/FILES +++ b/src/Standard/FILES @@ -29,7 +29,6 @@ Standard_ExtString.cxx Standard_ExtString.hxx Standard_Failure.cxx Standard_Failure.hxx -Standard_Failure.lxx Standard_GUID.cxx Standard_GUID.hxx Standard_Handle.hxx @@ -62,6 +61,7 @@ Standard_NullObject.hxx Standard_NullValue.hxx Standard_NumericError.hxx Standard_OStream.hxx +Standard_OutOfMemory.cxx Standard_OutOfMemory.hxx Standard_OutOfRange.hxx Standard_Overflow.hxx diff --git a/src/Standard/Standard_Failure.cxx b/src/Standard/Standard_Failure.cxx index 31be9c34bd..07ecbcc4e1 100644 --- a/src/Standard/Standard_Failure.cxx +++ b/src/Standard/Standard_Failure.cxx @@ -88,7 +88,7 @@ Standard_Failure::Standard_Failure (const Standard_Failure& theFailure) myMessage = copy_message(theFailure.myMessage); } -void Standard_Failure::Destroy() +Standard_Failure::~Standard_Failure() { deallocate_message(myMessage); } @@ -195,3 +195,13 @@ Handle(Standard_Failure) Standard_Failure::NewInstance(const Standard_CString AS { return new Standard_Failure(AString) ; } + +//======================================================================= +//function : GetMessageString +//purpose : Returns error message +//======================================================================= +Standard_CString Standard_Failure::GetMessageString () const +{ + return (myMessage ? myMessage+sizeof(Standard_Integer) : ""); +} + diff --git a/src/Standard/Standard_Failure.hxx b/src/Standard/Standard_Failure.hxx index b48db08c8e..e3a9bb40f6 100644 --- a/src/Standard/Standard_Failure.hxx +++ b/src/Standard/Standard_Failure.hxx @@ -41,19 +41,18 @@ public: //! Creates a status object of type "Failure". Standard_EXPORT Standard_Failure(); -Standard_EXPORT Standard_Failure (const Standard_Failure& f); - + + //! Copy constructor + Standard_EXPORT Standard_Failure (const Standard_Failure& f); //! Creates a status object of type "Failure". Standard_EXPORT Standard_Failure(const Standard_CString aString); -Standard_EXPORT Standard_Failure& operator= (const Standard_Failure& f); - - Standard_EXPORT void Destroy(); -~Standard_Failure() -{ - Destroy(); -} + + //! Assignment operator + Standard_EXPORT Standard_Failure& operator= (const Standard_Failure& f); + //! Destructor + Standard_EXPORT ~Standard_Failure(); //! Prints on the stream the exception name followed by //! the error message. @@ -63,16 +62,12 @@ Standard_EXPORT Standard_Failure& operator= (const Standard_Failure& f); //! Handle(Standard_Failure)&)" //! is implemented. (This operator uses the method Print) Standard_EXPORT void Print (Standard_OStream& s) const; -void operator<< (Standard_OStream& s) const -{ - Print(s); -} //! Returns error message - Standard_CString GetMessageString() const; + Standard_EXPORT virtual Standard_CString GetMessageString() const; //! Sets error message - Standard_EXPORT void SetMessageString (const Standard_CString aMessage); + Standard_EXPORT virtual void SetMessageString (const Standard_CString aMessage); Standard_EXPORT void Reraise(); @@ -133,11 +128,11 @@ private: }; - -#include - - - - +inline Standard_OStream& operator << (Standard_OStream& AStream, + const Handle(Standard_Failure)& AFailure) +{ + AFailure->Print(AStream); + return AStream; +} #endif // _Standard_Failure_HeaderFile diff --git a/src/Standard/Standard_Failure.lxx b/src/Standard/Standard_Failure.lxx deleted file mode 100644 index cbc7f6cf08..0000000000 --- a/src/Standard/Standard_Failure.lxx +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 1998-1999 Matra Datavision -// Copyright (c) 1999-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -// ------------------------------------------------------------------ -// Print (me; s: in out OStream) returns OStream; -// ------------------------------------------------------------------ -//++++ void Standard_Failure::Print (Standard_OStream& AStream) const -//++++ { -//++++ AStream << ": " << myMessage << endl; - -inline Standard_OStream& operator <<(Standard_OStream& AStream, - const Handle(Standard_Failure)& AFailure) -{ - AFailure->Print(AStream); - return AStream; -} - -//======================================================================= -//function : GetMessageString -//purpose : Returns error message -//======================================================================= -inline Standard_CString Standard_Failure::GetMessageString () const -{ - return (myMessage ? myMessage+sizeof(Standard_Integer) : myMessage); -} - diff --git a/src/Standard/Standard_MMgrOpt.cxx b/src/Standard/Standard_MMgrOpt.cxx index 30b837fbc2..4d0470926d 100644 --- a/src/Standard/Standard_MMgrOpt.cxx +++ b/src/Standard/Standard_MMgrOpt.cxx @@ -132,10 +132,6 @@ extern "C" int getpagesize() ; #define GET_USER(block) (((Standard_Size*)(block)) + BLOCK_SHIFT) #define GET_BLOCK(storage) (((Standard_Size*)(storage))-BLOCK_SHIFT) -// create static instance of out-of-memory exception to protect -// against possible lack of memory for its raising -static Handle(Standard_OutOfMemory) anOutOfMemError = new Standard_OutOfMemory; - //======================================================================= //function : Standard_MMgr //purpose : @@ -400,7 +396,7 @@ Standard_Address Standard_MMgrOpt::Allocate(const Standard_Size aSize) aBlock = (Standard_Size*)calloc(RoundSizeN+BLOCK_SHIFT, sizeof(Standard_Size)); // if still not succeeded, raise exception if ( ! aBlock ) - anOutOfMemError->Reraise ("Standard_MMgrOpt::Allocate(): malloc failed"); + Standard_OutOfMemory::Raise ("Standard_MMgrOpt::Allocate(): malloc failed"); } // initialize new block header by its size @@ -725,7 +721,7 @@ retry: if ( Purge(Standard_False) ) goto retry; // if nothing helps, raise exception - anOutOfMemError->Reraise (strerror(errcode)); + Standard_OutOfMemory::Raise (strerror(errcode)); } // save actually allocated size into argument @@ -759,7 +755,7 @@ retry: char message[BUFSIZE]; if ( FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, message, BUFSIZE-1, 0) <=0 ) strcpy (message, "Standard_MMgrOpt::AllocMemory() failed to mmap"); - anOutOfMemError->Reraise (message); + Standard_OutOfMemory::Raise (message); } // record map handle in the beginning @@ -782,7 +778,7 @@ retry: if ( Purge(Standard_False) ) goto retry; // if nothing helps, raise exception - anOutOfMemError->Reraise ("Standard_MMgrOpt::Allocate(): malloc failed"); + Standard_OutOfMemory::Raise ("Standard_MMgrOpt::Allocate(): malloc failed"); } } // clear whole block if clearing option is set diff --git a/src/Standard/Standard_OutOfMemory.cxx b/src/Standard/Standard_OutOfMemory.cxx new file mode 100644 index 0000000000..915a3a1075 --- /dev/null +++ b/src/Standard/Standard_OutOfMemory.cxx @@ -0,0 +1,102 @@ +// Created on: 2016-01-06 +// Created by: Andrey Betenev +// Copyright (c) 2016 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(Standard_OutOfMemory,Standard_ProgramError) + +//======================================================================= +//function : Standard_OutOfMemory +//purpose : +//======================================================================= + +Standard_OutOfMemory::Standard_OutOfMemory(const Standard_CString theMessage) +{ + // call explicitly own method (non-virtual call) + Standard_OutOfMemory::SetMessageString (theMessage); +} + +//======================================================================= +//function : GetMessageString +//purpose : +//======================================================================= + +Standard_CString Standard_OutOfMemory::GetMessageString() const +{ + return myBuffer; +} + +//======================================================================= +//function : SetMessageString +//purpose : +//======================================================================= + +void Standard_OutOfMemory::SetMessageString (const Standard_CString theMessage) +{ + // restrict length of the message by buffer size + size_t n = (theMessage ? std::min (strlen (theMessage), sizeof(myBuffer) - 1) : 0); + + // first set line end symbol to be safe in case of concurrent call + myBuffer[n] = '\0'; + if (n > 0) + memcpy (myBuffer, theMessage, n); +} + +//======================================================================= +//function : Raise +//purpose : +//======================================================================= + +void Standard_OutOfMemory::Raise(const Standard_CString theMessage) +{ + NewInstance(theMessage)->Reraise(); +} + +//======================================================================= +//function : Raise +//purpose : +//======================================================================= + +void Standard_OutOfMemory::Raise(Standard_SStream& theMessage) +{ + NewInstance(theMessage.str().c_str())->Reraise(); +} + +//======================================================================= +//function : NewInstance +//purpose : +//======================================================================= + +// global instance must be allocated at load-time +static Handle(Standard_OutOfMemory) anOutOfMemInstance = new Standard_OutOfMemory; + +Handle(Standard_OutOfMemory) Standard_OutOfMemory::NewInstance(const Standard_CString theMessage) +{ + anOutOfMemInstance->SetMessageString (theMessage); + return anOutOfMemInstance; +} + +//======================================================================= +//function : Throw +//purpose : +//======================================================================= + +void Standard_OutOfMemory::Throw () const +{ + throw *this; +} diff --git a/src/Standard/Standard_OutOfMemory.hxx b/src/Standard/Standard_OutOfMemory.hxx index 9eada6dccf..81ed7b34eb 100644 --- a/src/Standard/Standard_OutOfMemory.hxx +++ b/src/Standard/Standard_OutOfMemory.hxx @@ -32,6 +32,48 @@ DEFINE_STANDARD_HANDLE(Standard_OutOfMemory, Standard_ProgramError) #define Standard_OutOfMemory_Raise_if(CONDITION, MESSAGE) #endif -DEFINE_STANDARD_EXCEPTION(Standard_OutOfMemory, Standard_ProgramError) +//! Standard_OutOfMemory exception is defined explicitly and not by +//! macro DEFINE_STANDARD_EXCEPTION, to avoid necessity of dynamic +//! memory allocations during throwing and stack unwinding: +//! +//! - method NewInstance() returns static instance (singleton) +//! - method Raise() raises copy of that singleton, resetting +//! its message string +//! - message string is stored as field, not allocated dynamically +//! (storable message length is limited by buffer size) +//! +//! The reason is that in out-of-memory condition any memory allocation can +//! fail, thus use of operator new for allocation of new exception instance +//! is dangerous (can cause recursion until stack overflow, see #24836). + +class Standard_OutOfMemory : public Standard_ProgramError +{ + void Throw () const Standard_OVERRIDE; + +public: + + //! Constructor is kept public for backward compatibility + Standard_EXPORT Standard_OutOfMemory(const Standard_CString theMessage = 0); + + //! Returns error message + Standard_EXPORT Standard_CString GetMessageString() const Standard_OVERRIDE; + + //! Sets error message + Standard_EXPORT void SetMessageString (const Standard_CString aMessage) Standard_OVERRIDE; + + //! Raises exception with specified message string + Standard_EXPORT static void Raise(const Standard_CString theMessage = ""); + + //! Raises exception with specified message string + Standard_EXPORT static void Raise(Standard_SStream& theMessage); + + //! Returns global instance of exception + Standard_EXPORT static Handle(Standard_OutOfMemory) NewInstance(const Standard_CString theMessage = ""); + + DEFINE_STANDARD_RTTIEXT(Standard_OutOfMemory,Standard_ProgramError) + +protected: + char myBuffer[1024]; +}; #endif // _Standard_OutOfMemory_HeaderFile diff --git a/tests/bugs/fclasses/bug24836 b/tests/bugs/fclasses/bug24836 new file mode 100644 index 0000000000..d8cac9cd41 --- /dev/null +++ b/tests/bugs/fclasses/bug24836 @@ -0,0 +1,11 @@ +puts "============" +puts "OCC24836" +puts "============" +puts "" +####################################################################### +# Stack overflow when raising exception in low memory condition +####################################################################### + +pload QAcommands + +OCC24836