0

When alloca can't allocate memory on heap it creates structured exception stackoverflow and program halt with Stackoverflow. Ok. But when _malloca can not allocate memory on heap it says nothing. I allocate great amount of memory and after that use it, but get access violation exception. Example

#include <stdio.h>
#include <malloc.h>
#include <conio.h>

void foo(size_t n) {
    int *arr = (int*) _malloca(n*sizeof(int));
    size_t i;

    for (i = 0; i < n; i++) {
        arr[i] = rand();
    }
    for (i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    _freea(arr);
}

void main() {
    foo(900000000000);
    _getch();
}

BUT when I use only part of allocated memory, I get no exceptions at all. Example

#include <stdio.h>
#include <malloc.h>
#include <conio.h>

void foo(size_t n) {
    int *arr = (int*) _malloca(n*sizeof(int));
    size_t i;

    for (i = 0; i < 100; i++) {
        arr[i] = rand();
    }
    for (i = 0; i < 100; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    _freea(arr);
}

void main() {
    foo(900000000000);
    _getch();
}

VSE2013 WinDesktop. Oki, you may say, try to catch exception

#include <stdio.h>
#include <malloc.h>
#include <conio.h>
#include <windows.h>

void foo(size_t n) {
    int *arr = NULL; 
    size_t i;

    __try {
        arr = (int*)_malloca(n*sizeof(int));
    } __except (GetExceptionCode() == STATUS_STACK_OVERFLOW) {
        int errcode;
        printf("_malloca failed!\n");
        _getch();
        errcode = _resetstkoflw();
        if (errcode) {
            printf("Could not reset the stack!");
            _getch();
            _exit(1);
        }
    }
    for (i = 0; i < 100; i++) {
        arr[i] = rand();
    }
    for (i = 0; i < 100; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    _freea(arr);
}

void main() {
    foo(900000000000);
    _getch();
}

but it continue to work. And if use all element of array then again get access violation.
Question: it is bug or a feature?

Ivan Ivanov
  • 2,076
  • 16
  • 33
  • @JoachimPileborg From the same manual page: "_malloca allocates size bytes from the program stack **or the heap** if the request exceeds a certain size in bytes" (emphasis by me just to rub it in ;-) ) – Peter - Reinstate Monica Jul 11 '14 at 15:36
  • @PeterSchneider Ah missed that. Consider me properly rubbed. :) – Some programmer dude Jul 11 '14 at 15:37
  • 1
    @JoachimPileborg Weird function though. One needs to bracket it with _freea() (somewhat logically). So it's merely a performance thing for cases with an unpredictable mix of small and big allocs and local scope. – Peter - Reinstate Monica Jul 11 '14 at 15:39
  • Ok, I understand that it is weird. I even understand that MS is weird, but... Why doesn't it return NULL if can't allocate memory on heap? Why doesn't it fire an exception then? It's not php, it is c. Maybe there are some other places where I can check that an error has happened? – Ivan Ivanov Jul 11 '14 at 15:56
  • According to [the reference page](http://msdn.microsoft.com/en-us/library/5471dc8s.aspx) it should throw a structured exception when there's not enough space, not return `NULL`. However, why it doesn't do that I don't know. Unless you're on some kind of super-computer with over three terabytes of memory? :) – Some programmer dude Jul 11 '14 at 16:11

1 Answers1

2

Yey! I was right that _malloca returns NULL when can not allocate memory using malloc on heap. The problem is in call, that is stupid(((

foo(900000000000);

is invalid, because it is bigger than size_t size on my computer. malloc.h use this function to check size is normal

__inline int _MallocaIsSizeInRange(size_t size)
{
    return size + _ALLOCA_S_MARKER_SIZE > size;
}

when I call

foo(INT_MAX);

it returns NULL, as memory can not be allocated.

Ivan Ivanov
  • 2,076
  • 16
  • 33
  • Nice one. Caught me. Should always -Wall though which would have warned. – Peter - Reinstate Monica Jul 11 '14 at 17:02
  • Still have question. VS writes warning: reduce __int64 to size_t. So, it is size_t; well, it wouldn't compile if not. But why does it allocate memory if size + 8 become less than size. Don't get it. – Ivan Ivanov Jul 11 '14 at 17:22
  • The compiler makes the literal 900000000000 a 64 bit integer because that's the only type which fits that number. Now size_t is probably 32 bit (or we wouldn't have this trouble). Note that size_t may even be compiler option dependent. Then that 64 bit number gets copied into the 32 bit size_t parameter, probably by just cutting the upper 32 bits off. The remaining lower 32 bits will just be the remainder modulo 2^32 which is kindof arbitrary. ... – Peter - Reinstate Monica Jul 12 '14 at 19:25
  • ... As an example, if size_t had 4 bits one could at most allocate 15 bytes (all 4 bits set, 1 + 2 + 4 + 8). If you pass the int 18 (0 + 2 + 0 + 0 + 1) only the lower 4 bits would be considered: 0 + 2 + 0 + 0, which have the value 2. The function would dutifully allocate 2 bytes, and the check in _MallocaIsSizeInRange would not bark because it only sees the 2. In your case it tries to allocate 4*900000000000 bytes. The number modulo 32 is 817405952 which is something you probably still get from the system, depending on limits and stuff. – Peter - Reinstate Monica Jul 12 '14 at 19:38
  • I clearly understand byte cutting here. I don't understand why it remains working. There are two possible variants in my world - computer can allocate memory and return valid pointer, computer can not allocate sufficient amount of memory and returns NULL. MS gives me third variant - return valid pointer to memory block which is smaller than needed. I can use only part of this space, if try to go further (how long - who knows!) then meet with the accident. Worst thing is that *arr now is not a pointer, it is something unusual so I can not _msize and determine what size does it really have. – Ivan Ivanov Jul 13 '14 at 15:26
  • Hm. "I don't understand why it remains working." Well, I explained it step by step. The key is that the literal you pass has a 64 bit type while the number which _malloca operates on is 32 bits. The compiler performs a *narrowing conversion* which potentially, and in this case actually, loses information; that's why the compiler routinely issues a warning there, which you chose to ignore. _malloca operates on incomplete information and cannot do a thing about it. It allocates exactly the memory it sees being requested. These dangers (narrowing conversion without cast) are inherent to C. – Peter - Reinstate Monica Jul 13 '14 at 17:42
  • I understand: error in multiplication. Not in casting. I overflows when multiplied by 4. So requested memory size is smaller than sizeof(int) * n, and in cycle from 1 .. n it hung. – Ivan Ivanov Jul 13 '14 at 18:02