⬆️ ⬇️

Why is it important to check that the malloc function returned

malloc


We bring to your attention a series of articles devoted to recommendations for writing high-quality code using the example of errors found in the Chromium project. This is the sixth part that will be devoted to the malloc function. Rather, why should be sure to check the pointer returned by this function. Most likely, you can not guess what the trick is associated with malloc, because we recommend to get acquainted with this article.



Note. In the article, the malloc function will often mean that it is not only about this function, but also calloc , realloc , _aligned_malloc , _recalloc , strdup, and so on. I do not want to clutter the text of the article, constantly repeating the names of all these functions. What they have in common is that they can return a null pointer.



malloc



If the malloc function could not allocate a memory buffer, then it returns NULL . Any normal program should check the pointers that the malloc function returns , and handle the situation when the memory failed to appropriately.



Unfortunately, many programmers are careless about checking pointers, and sometimes deliberately do not check whether it was possible to allocate memory or not. Their logic is as follows:

If the malloc function could not allocate memory, it is unlikely that my program will continue to function properly. Most likely, the memory will not be enough for other operations, so you can never bother about memory allocation errors. The first access to the memory by the null pointer will lead to the generation of a Structured Exception in Windows, or the process will receive a SIGSEGV signal, if we are talking about Unix-like systems. As a result, the program will fall, which suits me. Once there is no memory, then there is nothing to suffer. Alternatively, it is possible to intercept the structural exception / signal and process the dereferencing of the null pointer more centrally. This is more convenient than writing thousands of checks.

I do not invent it, I have often communicated with people who consider this approach appropriate and deliberately never verify the result that the malloc function returns.

')

By the way, there is another excuse for developers why they do not verify that the malloc function returned. The malloc function only reserves memory, but there is no guarantee at all that there will be enough physical memory when we start using the allocated memory buffer. Therefore, since there is still no guarantee, there is no need to check. For example, this is how Carsten Haitzler, one of the developers of the EFL Core library, explained why I counted over 500 places in the library code where checks are missing. Here is his comment on the article :

It’s not always a good idea. Linux overcommits memory by default. This is not what you have been assigned. Only virtual space. Not until you touch it. It seems like a valid pointer. It is low. Sometimes we do it ... sometimes not. It can be used for a small number of people. It can be tune. Secondly, it is a list of chunk of memory - eg a linked list node ... realistically if NULL is returned ... you can do. If you can not allocate 20-40 bytes ... you can’t allocate 20–40 bytes ... But there are few megabytes of memory etc. which has been our target. I can see why PVS-Studio doesn't like this. It wasn’t been a reality that it wasn’t spent on it. I'll get more into that later.

The above reasoning programmers are wrong, and I will explain in detail why below. But first you need to answer the question: "and where is Chromium?".



Chromium



Chromium is here despite the fact that the libraries used in it have at least 70 errors related to the lack of verification after calling functions such as malloc , calloc , realloc . Yes, in Chromium itself, these functions are almost never used. In Chromium, only containers or operator new are used . However, since there are errors in the libraries used, it means that we can say that they exist in Chromium. Of course, some parts of the libraries may not be used when running Chromium, but this is difficult and unnecessary to determine. Anyway, you need to edit all the errors.



I will not give a lot of code fragments with errors in the article, since they are of the same type. I will cite for example only one error found in the Yasm library:



static SubStr *
SubStr_new_u(unsigned char *s, unsigned int l)
{
    SubStr *r = malloc(sizeof(SubStr));
    r->str = (char*)s;
    r->len = l;
    return r;
}


PVS-Studio: V522 CWE-690 There might be dereferencing of a potential null pointer 'r'. Check lines: 52, 51. substr.h 52



. Chromium : chromium_malloc.txt. 72 , . , .



Common Weakness Enumeration PVS-Studio :



  1. CWE-690: Unchecked Return Value to NULL Pointer Dereference.
  2. CWE-628: Function Call with Incorrectly Specified Arguments.
  3. CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer


, Chromium, , . , .





4 , , malloc. - , .



, , , . .



. , , 0.



. . - . - , , Linux 64 .



, , - , «» - . - - , / .



, !





C C++ . — . , , , nullptr. , , .



— . .



, , (SEH Windows) ( UNIX-like ). , , . , SEH- ..



size_t *ptr = (size_t *)malloc(sizeof(size_t) * N * 2);
for (size_t i = 0; i != N; ++i)
{
  ptr[i] = i;
  ptr[N * 2 - i - 1] = i;
}


. . 1 , , - . . , - . , , 4- .



:



ptr[i] = i;
ptr[N * 2 - i - 1] = i;


, ptr[0], /. , .



- . . , , nullptr. , , .



, , :



ptr[N * 2 - i - 1] = i;
ptr[i] = i;


((size_t *)nullptr)[N * 2 — 0 — 1]. N , «» i - , . , - .



((size_t *)nullptr)[0]. /.



/. . - . , !



, ? . . , - .







: — . «» . .



, malloc , . - . if.





, , . .



, - - / .



, « » . , , , , . , . , CAD , - , .



. , Ytnef, TNEF , , Outlook. , calloc CVE-2017-6298.



, , :



vl->data = calloc(vl->size, sizeof(WORD));
temp_word = SwapWord((BYTE*)d, sizeof(WORD));
memcpy(vl->data, &temp_word, vl->size);






, , , .



, - , ! , , . , .



, , Carsten Haitzler, EFL Core ( ). .



, , , — . .



, ?



, , - , . , . ? , .



, . , , LLVM-subzero, Chromium. , , Chromium LLVM, .



void StringMapImpl::init(unsigned InitSize) {
  assert((InitSize & (InitSize-1)) == 0 &&
         "Init Size must be a power of 2 or zero!");
  NumBuckets = InitSize ? InitSize : 16;
  NumItems = 0;
  NumTombstones = 0;
  
  TheTable = (StringMapEntryBase **)
             calloc(NumBuckets+1,
                    sizeof(StringMapEntryBase **) + 
                    sizeof(unsigned));

  // Allocate one extra bucket, set it to look filled
  // so the iterators stop at end.
  TheTable[NumBuckets] = (StringMapEntryBase*)2;
}


PVS-Studio: V522 CWE-690 There might be dereferencing of a potential null pointer 'TheTable'. Check lines: 65, 59. stringmap.cpp 65



TheTable[NumBuckets]. NumBuckets , - . , . .



:





, , , - .



Carsten Haitzler. , , , malloc. , . , , EFL:



static void
st_collections_group_parts_part_description_filter_data(void)
{
  ....
   filter->data_count++;
   array = realloc(filter->data,
     sizeof(Edje_Part_Description_Spec_Filter_Data) *
     filter->data_count);
   array[filter->data_count - 1].name = name;
   array[filter->data_count - 1].value = value;
   filter->data = array;
}


PVS-Studio: V522 There might be dereferencing of a potential null pointer 'array'. edje_cc_handlers.c 14249



. EFL Core Libraries, . , . .



: , . realloc, NULL.



, / - . :



array[filter->data_count - 1].name = name;
array[filter->data_count - 1].value = value;


filter->data_count , - .



- , . , .



, EFL Core Libraries, . , realloc - .







: « , ?». . , , . - , .



— , malloc. .



, memset ?



-, - :

realloc , . , , memset. , , . malloc .

. , calloc. , . , WebRTC, Chromium:



int Resampler::Reset(int inFreq, int outFreq, size_t num_channels) {
  ....
  state1_ = malloc(8 * sizeof(int32_t));
  memset(state1_, 0, 8 * sizeof(int32_t));
  ....
}


, . , , , , calloc. .



, ! memset .



memset . , , - . , , memset , , /. . , .



, . , memset , .



, memset , StackOverflow . :



The Linux kernel's memset for the SuperH architecture has this property: link.



, , . . :



void *memset(void *dest, int c, size_t n)
{
  unsigned char *s = dest;
  size_t k;
  if (!n) return dest;
  s[0] = c;
  s[n-1] = c;
  ....
}


:



s[0] = c;
s[n-1] = c;


N1 « — ». , . , n , - . .



? , :



void *memset(void *dest, int c, size_t n)
{
  size_t k;
  if (!n) return dest;
  s[0] = s[n-1] = c;
  if (n <= 2) return dest;
  ....
}






memset. , . , , . . , , malloc . .





, malloc .



, PVS-Studio , malloc. , . .



, - malloc, PVS-Studio. PVS-Studio. .





, : Andrey Karpov. Why it is important to check what the malloc function returned.

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



All Articles