Do not hard code the size of a type into an application. Because of alignment, padding, and differences in basic types (e.g., 32-bit versus 64-bit pointers), the size of most types can vary between compilers and even versions of the same compiler. Using the sizeof operator to determine sizes improves the clarity of what is meant and ensures that changes between compilers or versions will not affect the code.

Type alignment requirements can also affect the size of structures. For example, the size of the following structure is implementation-defined:

struct s {
  int i;
  double d;
};

Assuming 32-bit integers and 64-bit doubles, for example, the size can range from 12 to 16 bytes, depending on alignment rules.

Noncompliant Code Example

This noncompliant code example attempts to declare a two-dimensional array of integers with variable length rows. On a platform with 64-bit integers, the loop will access memory outside the allocated memory section.

int f(void) { /* Assuming 32-bit pointer, 32-bit integer */
  size_t i;
  int **matrix = (int **)calloc(100, 4);
  if (matrix == NULL) {
    return -1; /* Indicate calloc() failure */
  }

  for (i = 0; i < 100; i++) {
    matrix[i] = (int *)calloc(i, 4);
    if (matrix[i] == NULL) {
      return -1; /* Indicate calloc() failure */
    }
  }
 return 0;
}

Compliant Solution

This compliant solution replaces the hard-coded value 4 with sizeof(int *):

int f(void) {
  size_t i;
  int **matrix = (int **)calloc(100, sizeof(*matrix));
  if (matrix == NULL) {
    return -1; /* Indicate calloc() failure */
  }

  for (i = 0; i < 100; i++) {
    matrix[i] = (int *)calloc(i, sizeof(**matrix));
    if (matrix[i] == NULL) {
      return -1; /* Indicate calloc() failure */
    }
  }

  return 0;
}

Also see MEM02-C. Immediately cast the result of a memory allocation function call into a pointer to the allocated type for a discussion on the use of the sizeof operator with memory allocation functions.

Exceptions

EXP09-C-EX1: The C Standard explicitly declares sizeof(char) == 1, so any sizes based on characters or character arrays may be evaluated without using sizeof. This does not apply to char* or any other data types.

Risk Assessment

Porting code with hard-coded sizes can result in a buffer overflow or related vulnerability.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

EXP09-C

High

Unlikely

Medium

P6

L2

Automated Detection

Tool

Version

Checker

Description

Astrée
24.04
alloc-without-sizeof
Partially checked
Compass/ROSE



Can detect violations of this recommendation. In particular, it looks for the size argument of malloc(), calloc(), or realloc() and flags when it does not find a sizeof operator in the argument expression. It does not flag if the return value is assigned to a char *; in this case a string is being allocated, and sizeof is unnecessary because sizeof(char) == 1

ECLAIR
1.2
CC2.EXP09Can detect violations of this recommendation. In particular, it considers when the size of a type is used by malloc(), calloc() or realloc() and flags these functions if either the size argument does not use a sizeof operator, or the size argument uses sizeof, but the type of the returned value is not a pointer to the type of the argument to sizeof. It does not flag if the returned value is assigned to a char *
LDRA tool suite
9.7.1

201 S

Partially implemented

Polyspace Bug Finder

R2023b

CERT C: Rec. EXP09-CChecks for hard-coded object size used to manipulate memory (rec. fully covered)
RuleChecker

24.04

alloc-without-sizeofPartially checked

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines



13 Comments

  1. I'm a big fan of getting *all* the size information from sizeof:

    int **triarray = calloc(100, sizeof(int *)); // ok
    int **triarray = calloc(100, sizeof(*triarray)); // better
    

    By extracting the type from the variable itself, it means that if the type is changed someday (say, to long**, then the allocation works automatically.

    1. I use this a lot too, unless of course you get the number of '*'s wrong. (sad) For example in the above example instead of a sizeof *triarray, the common typo is sizeof triarray. In general, if possible, I tend to use the variable array initializers (GCC extension) instead of this to avoid typos.

      1. we should either implement this in the solution or add it as a sidebar

        1. which part? do you want to go ahead and try to modify the rule?

          1. the part about sizeof(*matrix) vs sizeof(int*), i moved it into the code

            1. Why is one of the following approaches better than the other?

              matrix[i] = (int *)calloc(i, sizeof(*matrix[i]));
              matrix[i] = (int *)calloc(i, sizeof(**matrix));
              
              1. don't know if either is, but I wanted to remove the reference to i because it felt unnecessary... here's my reasoning: we're dealing with types, and i should have no effect on **matrix, it shouldn't matter which element we're dealing with, the type is inherent in the variable matrix

              2. sizeof(int *) can cause bugs when the programmer changes the declaration from int * to double * (as an example).  By using sizeof(*variable), the sizeof will always follow the correct type, presuming the programmer has the correct number of dereferences for what they're after.

                That being said, sizeof(*some_ptr) can look terrifying because it looks like you are dereferencing a null pointer prior to the allocation happening.  So I can understand not wanting to use it.

  2. I think we should provide an explicit exception to this recommendation in the case an array of char, signed char, or unsigned char is being allocated and sizeof is unnecessary because sizeof(char) == 1 as guaranteed by the standard.

    Also, would rose flag the following code?

    size_t matrix_size = sizeof(sizeof(**matrix));
    matrix[i] = (int *)calloc(i, matrix_size);
    

    If so, I think this should is a false positive.

    1. Rose already has an exception when it detects that space is being allocated for character arrays, but this rule does not discuss sizeof(char) == 1. Added this as an exception.

  3. I would like very much to have a Cert approved method to know at compile time, the sizeof a word, long, and long long, as well as the byte ordering for binary data stored in memory or on a file.   I need this as preprocessor information.

    In searching through the GCC include file  <limits.h>, I came across  the #define  __WORDSIZE.  In Linux for Intel/AMD/Atom, this presents 64 or 32, corresponding to compiler.  The depending is if the compiler and library are for 32bits or for 64 bit architectures.

    The code that I write is based on the same size variable, be it on a 32bit system or 64 bit system.  Here is a snippet of what I did.

    My code has the following


    #ifndef  __WORDSIZE
    #include <limits.h>
    #endif

    #if  __WORDSIZE == 64
      typedef unsigned int uint32;
    #else
      typedef unsigned long int uint32;
    #endif

    My stored values are always 4 eight bit bytes in size.   By the way, a long long int in both cases appears to be 8 bytes.  The byte order is quire weird..

     

    I have not found a Standard define for littleEndian  BigEndian.  Intel for a the number (0x1234) in binary  stores the word as   byte zero  contains 0x12 byte one contains 0x34     1234

    A long is stored in memory as nibble (half byte) positions as 12345678   while a long long int is stored in successive nibbles as  12349abc 5678defg   

    ld like to have a recommandation as to library include file that I may use for both instances.

    Leslie Satenstein 

    lsatenstein@yahoo.com

     

     

    1. I only have a partial answer.

      The CHAR_BIT values defined in <limits.h> defines the number of bits for smallest object that is not a bit-field (byte)

      It is also defined by a constant expressions suitable for use in

      #if

      preprocessing directives.

       

  4. Would this checker:

    PRQA QA·C8.1 Can detect for apparent null pointer operations.

    be a better match for this rule:

    EXP34-C. Do not dereference null pointers ?