2

I am trying the simulate the Error Scenario of a Process in Linux that Heap is not enough to allocate the memory in a C++ Linux Application. But Eventhough I use the "setrlimit" to reduce the Heap Memory available to the Process, still the heap memory is getting allocated successfully.

struct rlimit the_limit = { 1, 1 };
if (-1 == setrlimit(RLIMIT_DATA, &the_limit)) {
    perror("setrlimit failed");
}

try
{
   char *n = new char[5600];

   if (n==NULL)
   {
      cout <<"\nAllocation Failure\n";
   }
}
catch (std::bad_alloc& ba)
{
   std::cerr << "bad_alloc caught: " << ba.what() << '\n';
}
Timisorean
  • 1,388
  • 7
  • 20
  • 30
  • 1
    Linux is happy to *overcommit* (that is, allow programs to allocate more than is available). This is because the memory isn't mapped to the processes unless they are actually used (read from or written to) and it's uncommon for multiple processes to need all their pages simultaneously. – Some programmer dude Jul 17 '19 at 11:39
  • 1
    Besides, the `RLIMIT_DATA` doesn't mean what you think it means, which is explained in the duplicate. – Some programmer dude Jul 17 '19 at 11:40
  • @Someprogrammerdude That's likely not the cause. In this case, I believe the problem is more likely to do with the code using C++. Could you reopen it please? – Petr Skocik Jul 17 '19 at 11:51
  • @PSkocik `new` will use `malloc` which (on relatively modern Linux) will use `mmap`. And `RLIMIT_DATA` doesn't change the limits used by `mmap`, as explained in the duplicate. – Some programmer dude Jul 17 '19 at 11:55
  • 1
    @Someprogrammerdude The `setrlimit` manpage says that `RLIMIT_DATA` does apply to `mmap` since Linux 4.7. The problem with the C++ example appears to be the C++ standard libary preallocating some heap at startup and then using the preallocated heap to satisfy the small 5600B request. Either increasing the request size (on my system, to at least 1<<16) or switching to plain C (on top of glibc or musl) and malloc causes the request to be served directly from the OS (sbrk or mmap) and then the limit applies. This, I think, makes this C++ `setrlimit` question different from the C counterpart. – Petr Skocik Jul 17 '19 at 12:05

1 Answers1

1

Most C++ standard libs including the one supplied with g++ start off with some heap memory preallocated. 5600 is a small request and as such, on my Linux system it gets satisfied from the preallocated memory as evidenced from an strace:

Modified example:

#include <stdio.h>
#include <sys/resource.h>
int main()
{
    struct rlimit the_limit = { 1, 1 };
    if (-1 == setrlimit(RLIMIT_DATA, &the_limit)) { perror("setrlimit failed"); }

    puts("ALLOC");
    #if __cplusplus
    try { char *n = new char[5600]; } catch (...) { perror("alloc failure"); }
    #else
    { char *n = malloc(1); if(!n) perror("alloc failure"); }
    #endif
}

End of example's strace:

...
write(1, "ALLOC\n", 6ALLOC
)                  = 6
exit_group(0)                           = ?

Either increasing the request size, e.g. in my case to at least 1<<16, or switching to plain C, causes the allocation request to be served from the OS, and then the limit does apply:

End of strace with an 1<<16 allocation request:

write(1, "ALLOC\n", 6ALLOC
)                  = 6
brk(0x561bcc5d4000)                     = 0x561bcc5b2000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 14), ...}) = 0
write(3, "alloc failure: Cannot allocate m"..., 38alloc failure: Cannot allocate memory
) = 38
close(3)                                = 0
exit_group(0)                           = ?

Note that generic allocator implementations generally use sbrk and/or mmap to get memory directly from the OS, and as you can glean from the setrlimit manpage, RLIMIT_DATA will only apply to a mmap-backed allocation iff you're on a Linux >= 4.7.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142