Std :: map causes a "stack overflow" in low memory conditions

This application is being developed in VS2010 on Windows XP in C ++.

When the computer was running very low in physical memory (and the page file was disabled, as this was our test case), this line of code:

std::map<UINT, std::vector<void *>> MyMap;

caused a stack overflow error in the malloc.c file

 'return HeapAlloc(_crtheap, 0, size ? size : 1);' 

Unhandled exception at 0x7c90e8e5 in MyApp.exe: 0xC00000FD: stack overflow.

This call was made from one of the application threads. If the memory was low, the error should have been selected by bad_alloc

Can anyone advise what could be the reason here.

EDIT:

This is what the actual stack looks like

 ntdll.dll!7c90e8e5() [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] ntdll.dll!7c9100d3() MyApp.exe!_heap_alloc_base(unsigned int size=72) Line 55 C MyApp.exe!_heap_alloc_dbg_impl(unsigned int nSize=36, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x0af3f0e4) Line 431 + 0x9 bytes C++ MyApp.exe!_nh_malloc_dbg_impl(unsigned int nSize=36, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x0af3f0e4) Line 239 + 0x19 bytes C++ MyApp.exe!_nh_malloc_dbg(unsigned int nSize=36, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0) Line 302 + 0x1d bytes C++ MyApp.exe!malloc(unsigned int nSize=36) Line 56 + 0x15 bytes C++ MyApp.exe!operator new(unsigned int size=36) Line 59 + 0x9 bytes C++ MyApp.exe!std::_Allocate<std::_Tree_nod<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >::_Node>(unsigned int _Count=1, std::_Tree_nod<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >::_Node * __formal=0x00000000) Line 36 + 0x15 bytes C++ MyApp.exe!std::allocator<std::_Tree_nod<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >::_Node>::allocate(unsigned int _Count=1) Line 187 + 0xb bytes C++ MyApp.exe!std::_Tree_val<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >::_Tree_val<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >(const std::less<unsigned int> & _Parg=less, std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > > _Al={...}) Line 544 + 0xd bytes C++ MyApp.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >::_Tree<std::_Tmap_traits<unsigned int,std::vector<void *,std::allocator<void *> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > >,0> >(const std::less<unsigned int> & _Parg=less, const std::allocator<std::pair<unsigned int const ,std::vector<void *,std::allocator<void *> > > > & _Al={...}) Line 699 C++ 
+6
source share
3 answers

I realized that this is not a problem specific to std :: map. When memory is low and the page file does not exist, a simple application can also cause a stack overflow. This is because Windows reserves a certain amount of memory per thread for the stack. However, he may not be able to fix the memory. In this case, a "stack overflow" occurs.

 #include <stdio.h> #include <conio.h> #define STACKSIZE (1024*896) void FunctionWithBigStack() { char stack[STACKSIZE]; printf("\nAllocated %d KB stack successfully", STACKSIZE/1024); stack[STACKSIZE-1] = 123; // Let use 'stack' array so that optimizer won't discard it while compiling } int _tmain(int argc, _TCHAR* argv[]) { printf("\nThrough another application, try make all the memory full and then press a key to call a function that has %d KB stack", STACKSIZE/1024); _getch(); FunctionWithBigStack(); return 0; } 

This application works fine under normal memory conditions, but if we make the memory full while it waits for a key press , we can see that FunctionWithBigStack crashing with a "stack overflow"

0
source
  #define STACKSIZE (1024*896) 

This is certainly a problem, the main thread of your program is hovering at the edge of disabling an exception to protect the page that triggers the exception. HeapAlloc () will not work for two main reasons on Windows. The usual, which you assumed so far, ends with the address space - this is the normal failure mode.

But Windows also provides a reliable guarantee that any virtual memory allocation can always be mapped to RAM. It has nothing like the β€œout of memory killer” that accidentally interrupts processes when memory is overloaded, and there is no RAM to fix the page error. Allocations are always performed with the guarantee that the RAM page can be dropped or replaced with a disk. If you start Windows without a swap file, then this warranty will be difficult to provide. A commit error is an obvious possibility.

What happens next is hard to guess; your stack trace does not give any hints. Be sure to configure the character server so that you get the characters for ntdll.dll. But it is clear that HeapAlloc () will follow a different path than usual when the commit is not executed. I would suggest that some kind of debugging probe has no idea. With a deeper nesting requiring more stack space. This is enough to disable the stack protection page and throw a stackoverflow exception.

Not sure if the problem in this problem is really important, your program is dead anyway. Recovering from a commit failure is almost impossible. It is completely random, affected by VM allocations in other processes. To run without a page file requires a lot of RAM and very careful control over which processes can run on the machine. RAM is all cheaper than anything you could do in software.

+3
source

Low memory does not always mean bad_alloc . The call stack also consumes memory. If the system cannot create a new stack for another function call or limit the number of call tables, this will result in an error.

I think HeapAlloc is part of CRT (basically a C function call). new operator throws bad_alloc , not HeapAlloc .

+2
source

Source: https://habr.com/ru/post/970558/


All Articles