6

If I have understood correctly, it is also important to check that the desired space passed to malloc(), calloc() or realloc() does not exceed the SIZE_MAX constant after the multiplication with the sizeof(desired_type). In order to make sure that does not happen I started to do something like this in my codes:

#define MAX_CHAIN (50)


#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif

int main(int argc, char const *argv[]){

    char **parsed_string;

    if(MAX_CHAIN > SIZE_MAX/sizeof(char*) || 
    (parsed_string=(char**)malloc(MAX_CHAIN * sizeof(char*)))==NULL){
        printf("%s\n", "Error, cannot allocate memory");
        exit(1);
    }

    /*Otherwise pointer is valid and the execution continues*/


}

So it basically checks for two things:

  1. That the requested memory is not bigger that SIZE_MAX
  2. That the pointer returned by malloc() is valid.

My questions are: Is it a correct to do these checks? Is this considered as good practice or should I use something else instead? Also, can someone explain why the allocation is limited by SIZE_MAX?

tyrana4
  • 197
  • 9
  • 2
    To all answerers, an example scenario: Imagine you try to allocate `((size_t) -1) / 2` integers, which are 4 bytes each. Multiplying those two numbers together will overflow and cause malloc() to allocate less memory than you thought. malloc() might "succeed" because it receives a smaller size than you intended. That's what this question is about. – John Kugelman Dec 06 '13 at 14:59
  • That's what I thought! Thank you for this comment. I wish someone could formulate a complete answer. – tyrana4 Dec 06 '13 at 15:02
  • Good point. However, [please don't cast the return value of `malloc()` in C](http://stackoverflow.com/a/605858/28169). – unwind Dec 06 '13 at 15:06
  • 1
    I use a C++ compiler, that's why I need to cast here. I would not do it otherwise. – tyrana4 Dec 06 '13 at 15:10

2 Answers2

3

It is correct to do the checks, some would say pedantic. Some thoughts

#include <stdint.h>
...
// MAX_CHAIN > SIZE_MAX/sizeof(char*)
MAX_CHAIN > (SIZE_MAX - sizeof(char*) +1)/sizeof(char*)
// This deals with SIZE_MAX/sizeof(char*) rounding toward 0

A return value of NULL is valid if memory allocation size is 0.

Watch for attempted negative memory allocation requests as many requests are formed with int (signed) even though the request takes size_t (unsigned).


Memory allocation is limited to SIZE_MAX as in typical systems cannot allocate (size_t)-1 or any where new that. Look at the C standard definition of RSZIE_MAX which is closely related to your concerns.

C11dr §K.3.4 3
Extremely large object sizes are frequently a sign that an object’s size was calculated incorrectly. For example, negative numbers appear as very large positive numbers when converted to an unsigned type like size_t. Also, some implementations do not support objects as large as the maximum value that can be represented by type size_t.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thanks for the clear answer, one last thing: How would you take care of the negative allocation (like in the example you give)? What do you mean by not "getting the answer one expect"? `MAX_CHAIN` could be interpreted as a negative value? – tyrana4 Dec 06 '13 at 15:29
  • 1
    @aeqr as given `MAX_CHAIN (50)` is a signed integer with a value of `50`. Your usage of it does not appear to be a problem. But since it is a signed integer, it may infect `signed-ness` into various other calculations that you think are being done unsigned. It would have been safer to use `MAX_CHAIN ((size_t)50)`. It is this subtle effect that messes integer calculation at the extreme integer range. That is where your code is trying to operate, at the extreme ends. Note: integer math via the pre-processor works with, I think, with intmax_t and uintmax_t. Walk carefully at the fringe. – chux - Reinstate Monica Dec 06 '13 at 15:48
  • 1
    @aeqr To "take care of the negative allocation" consider `RSIZE_MAX`. I think it is just what you want. C11 K,3,4 "recommended that RSIZE_MAX be defined as the smaller of the size of the largest object supported or (SIZE_MAX >> 1)", 1), even if this limit is smaller than the size of some legitimate, but very large, objects. Implementations targeting machines with small address spaces may wish to define RSIZE_MAX as SIZE_MAX, which means that there is no object size that is considered a runtime-constraint violation. – chux - Reinstate Monica Dec 06 '13 at 15:52
  • Just to be completely clear about it, is this correct?: `#ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #ifndef RSIZE_MAX #define RSIZE_MAX (SIZE_MAX >> 1) #endif` – tyrana4 Dec 06 '13 at 16:18
  • And then of course I would use `RSIZE_MAX` in the allocation, instead of `SIZE_MAX`. – tyrana4 Dec 06 '13 at 16:19
  • 1
    @aeqr Looks good except _maybe_ `(SIZE_MAX >> 1)` does a type conversion? Hmm maybe `(SIZE_MAX >> 1u)`? Always a bit fuzzy about shifts and the effect of the type of that pesky `1`. Be sure to #include first, as you are able. – chux - Reinstate Monica Dec 06 '13 at 16:32
  • @aeqr Parting comment: Having a central function that calls `malloc()` and checks the size is a good idea. But I'd even campaign to limit `malloc()` to even a fraction of `RSIZE_MAX` (e.g. RSIZE_MAX/2). There are just so many potential potholes with code and arrays near the limits of the system, I'd like to have a throttle on that beast by a good margin. This tighter limit may be practical in a program, but not in a supporting library. TTFN – chux - Reinstate Monica Dec 06 '13 at 16:44
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/42670/discussion-between-chux-and-aeqr) – chux - Reinstate Monica Dec 06 '13 at 18:15
-1

To check that the result is ok just see if it's equal to NULL

Angus Connell
  • 419
  • 3
  • 6
  • That doesn't tell you if the the amount of memory you requested overflowed. – John Kugelman Dec 06 '13 at 14:52
  • It sort of does, I would say that the requested data may fail for a number of reasons, but as you have written in your code you check for NULL already, the realloc could fail because of mem fragmentation etc. It's implementation specific. Microsoft give a whole load of debugging tools that allow you to walk the heap etc – Angus Connell Dec 06 '13 at 14:56
  • I Guess I don't know what you mean by overflowed? – Angus Connell Dec 06 '13 at 14:57
  • @Angus Connell With 32-bit math, 4-byte pointers and `MAX_CHAIN =0x4000_0001`, then `MAX_CHAIN * sizeof(char*)` is 0x0000_0004 (overflow). A `malloc(4)` will nicely allocate, but not provide the 0x1_0000_0004 bytes needed. – chux - Reinstate Monica Dec 06 '13 at 16:01