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 | ||
ECLAIR | 1.2 | CC2.EXP09 | Can 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 |
R2024a | CERT C: Rec. EXP09-C | Checks for hard-coded object size used to manipulate memory (rec. fully covered) | |
RuleChecker | 24.04 | alloc-without-sizeof | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | VOID EXP09-CPP. Use sizeof to determine the size of a type or variable |
MITRE CWE | CWE-805, Buffer access with incorrect length value |
13 Comments
Stephen Friedl
I'm a big fan of getting *all* the size information from sizeof:
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.
kowsik
I use this a lot too, unless of course you get the number of '*'s wrong. 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.
Alex Volkovitsky
we should either implement this in the solution or add it as a sidebar
Robert Seacord (Manager)
which part? do you want to go ahead and try to modify the rule?
Alex Volkovitsky
the part about
sizeof(*matrix)
vssizeof(int*)
, i moved it into the codeRobert Seacord (Manager)
Why is one of the following approaches better than the other?
Alex Volkovitsky
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, andi
should have no effect on**matrix
, it shouldn't matter which element we're dealing with, the type is inherent in the variablematrix
Aaron Ballman
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.
Robert Seacord
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?
If so, I think this should is a false positive.
David Svoboda
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.Leslie Satenstein
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
Robert Seacord (Manager)
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.
Robert Seacord (Manager)
Would this checker:
be a better match for this rule:
EXP34-C. Do not dereference null pointers ?