xref: /llvm-project/clang-tools-extra/docs/clang-tidy/checks/bugprone/sizeof-expression.rst (revision 1c38c46b083315e3a621267c9a90e8a7750f3700)
1.. title:: clang-tidy - bugprone-sizeof-expression
2
3bugprone-sizeof-expression
4==========================
5
6The check finds usages of ``sizeof`` expressions which are most likely errors.
7
8The ``sizeof`` operator yields the size (in bytes) of its operand, which may be
9an expression or the parenthesized name of a type. Misuse of this operator may
10be leading to errors and possible software vulnerabilities.
11
12Suspicious usage of 'sizeof(K)'
13-------------------------------
14
15A common mistake is to query the ``sizeof`` of an integer literal. This is
16equivalent to query the size of its type (probably ``int``). The intent of the
17programmer was probably to simply get the integer and not its size.
18
19.. code-block:: c++
20
21  #define BUFLEN 42
22  char buf[BUFLEN];
23  memset(buf, 0, sizeof(BUFLEN));  // sizeof(42) ==> sizeof(int)
24
25Suspicious usage of 'sizeof(expr)'
26----------------------------------
27
28In cases, where there is an enum or integer to represent a type, a common
29mistake is to query the ``sizeof`` on the integer or enum that represents the
30type that should be used by ``sizeof``. This results in the size of the integer
31and not of the type the integer represents:
32
33.. code-block:: c++
34
35  enum data_type {
36    FLOAT_TYPE,
37    DOUBLE_TYPE
38  };
39
40  struct data {
41    data_type type;
42    void* buffer;
43    data_type get_type() {
44      return type;
45    }
46  };
47
48  void f(data d, int numElements) {
49    // should be sizeof(float) or sizeof(double), depending on d.get_type()
50    int numBytes = numElements * sizeof(d.get_type());
51    ...
52  }
53
54
55Suspicious usage of 'sizeof(this)'
56----------------------------------
57
58The ``this`` keyword is evaluated to a pointer to an object of a given type.
59The expression ``sizeof(this)`` is returning the size of a pointer. The
60programmer most likely wanted the size of the object and not the size of the
61pointer.
62
63.. code-block:: c++
64
65  class Point {
66    [...]
67    size_t size() { return sizeof(this); }  // should probably be sizeof(*this)
68    [...]
69  };
70
71Suspicious usage of 'sizeof(char*)'
72-----------------------------------
73
74There is a subtle difference between declaring a string literal with
75``char* A = ""`` and ``char A[] = ""``. The first case has the type ``char*``
76instead of the aggregate type ``char[]``. Using ``sizeof`` on an object declared
77with ``char*`` type is returning the size of a pointer instead of the number of
78characters (bytes) in the string literal.
79
80.. code-block:: c++
81
82  const char* kMessage = "Hello World!";      // const char kMessage[] = "...";
83  void getMessage(char* buf) {
84    memcpy(buf, kMessage, sizeof(kMessage));  // sizeof(char*)
85  }
86
87Suspicious usage of 'sizeof(A*)'
88--------------------------------
89
90A common mistake is to compute the size of a pointer instead of its pointee.
91These cases may occur because of explicit cast or implicit conversion.
92
93.. code-block:: c++
94
95  int A[10];
96  memset(A, 0, sizeof(A + 0));
97
98  struct Point point;
99  memset(point, 0, sizeof(&point));
100
101Suspicious usage of 'sizeof(...)/sizeof(...)'
102---------------------------------------------
103
104Dividing ``sizeof`` expressions is typically used to retrieve the number of
105elements of an aggregate. This check warns on incompatible or suspicious cases.
106
107In the following example, the entity has 10-bytes and is incompatible with the
108type ``int`` which has 4 bytes.
109
110.. code-block:: c++
111
112  char buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  // sizeof(buf) => 10
113  void getMessage(char* dst) {
114    memcpy(dst, buf, sizeof(buf) / sizeof(int));  // sizeof(int) => 4  [incompatible sizes]
115  }
116
117In the following example, the expression ``sizeof(Values)`` is returning the
118size of ``char*``. One can easily be fooled by its declaration, but in parameter
119declaration the size '10' is ignored and the function is receiving a ``char*``.
120
121.. code-block:: c++
122
123  char OrderedValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
124  return CompareArray(char Values[10]) {
125    return memcmp(OrderedValues, Values, sizeof(Values)) == 0;  // sizeof(Values) ==> sizeof(char*) [implicit cast to char*]
126  }
127
128Suspicious 'sizeof' by 'sizeof' expression
129------------------------------------------
130
131Multiplying ``sizeof`` expressions typically makes no sense and is probably a
132logic error. In the following example, the programmer used ``*`` instead of
133``/``.
134
135.. code-block:: c++
136
137  const char kMessage[] = "Hello World!";
138  void getMessage(char* buf) {
139    memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char));  //  sizeof(kMessage) / sizeof(char)
140  }
141
142This check may trigger on code using the arraysize macro. The following code is
143working correctly but should be simplified by using only the ``sizeof``
144operator.
145
146.. code-block:: c++
147
148  extern Object objects[100];
149  void InitializeObjects() {
150    memset(objects, 0, arraysize(objects) * sizeof(Object));  // sizeof(objects)
151  }
152
153Suspicious usage of 'sizeof(sizeof(...))'
154-----------------------------------------
155
156Getting the ``sizeof`` of a ``sizeof`` makes no sense and is typically an error
157hidden through macros.
158
159.. code-block:: c++
160
161  #define INT_SZ sizeof(int)
162  int buf[] = { 42 };
163  void getInt(int* dst) {
164    memcpy(dst, buf, sizeof(INT_SZ));  // sizeof(sizeof(int)) is suspicious.
165  }
166
167Suspicious usages of 'sizeof(...)' in pointer arithmetic
168--------------------------------------------------------
169
170Arithmetic operators on pointers automatically scale the result with the size
171of the pointed typed.
172Further use of ``sizeof`` around pointer arithmetic will typically result in an
173unintended result.
174
175Scaling the result of pointer difference
176^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
177
178Subtracting two pointers results in an integer expression (of type
179``ptrdiff_t``) which expresses the distance between the two pointed objects in
180"number of objects between".
181A common mistake is to think that the result is "number of bytes between", and
182scale the difference with ``sizeof``, such as ``P1 - P2 == N * sizeof(T)``
183(instead of ``P1 - P2 == N``) or ``(P1 - P2) / sizeof(T)`` instead of
184``P1 - P2``.
185
186.. code-block:: c++
187
188  void splitFour(const Obj* Objs, size_t N, Obj Delimiter) {
189    const Obj *P = Objs;
190    while (P < Objs + N) {
191      if (*P == Delimiter) {
192        break;
193      }
194    }
195
196    if (P - Objs != 4 * sizeof(Obj)) { // Expecting a distance multiplied by sizeof is suspicious.
197      error();
198    }
199  }
200
201.. code-block:: c++
202
203  void iterateIfEvenLength(int *Begin, int *End) {
204    auto N = (Begin - End) / sizeof(int); // Dividing by sizeof() is suspicious.
205    if (N % 2)
206      return;
207
208    // ...
209  }
210
211Stepping a pointer with a scaled integer
212^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
213
214Conversely, when performing pointer arithmetics to add or subtract from a
215pointer, the arithmetic operator implicitly scales the value actually added to
216the pointer with the size of the pointee, as ``Ptr + N`` expects ``N`` to be
217"number of objects to step", and not "number of bytes to step".
218
219Seeing the calculation of a pointer where ``sizeof`` appears is suspicious,
220and the result is typically unintended, often out of bounds.
221``Ptr + sizeof(T)`` will offset the pointer by ``sizeof(T)`` elements,
222effectively exponentiating the scaling factor to the power of 2.
223
224Similarly, multiplying or dividing a numeric value with the ``sizeof`` of an
225element or the whole buffer is suspicious, because the dimensional connection
226between the numeric value and the actual ``sizeof`` result can not always be
227deduced.
228While scaling an integer up (multiplying) with ``sizeof`` is likely **always**
229an issue, a scaling down (division) is not always inherently dangerous, in case
230the developer is aware that the division happens between an appropriate number
231of _bytes_ and a ``sizeof`` value.
232Turning :option:`WarnOnOffsetDividedBySizeOf` off will restrict the
233warnings to the multiplication case.
234
235This case also checks suspicious ``alignof`` and ``offsetof`` usages in
236pointer arithmetic, as both return the "size" in bytes and not elements,
237potentially resulting in doubly-scaled offsets.
238
239.. code-block:: c++
240
241  void printEveryEvenIndexElement(int *Array, size_t N) {
242    int *P = Array;
243    while (P <= Array + N * sizeof(int)) { // Suspicious pointer arithmetic using sizeof()!
244      printf("%d ", *P);
245
246      P += 2 * sizeof(int); // Suspicious pointer arithmetic using sizeof()!
247    }
248  }
249
250.. code-block:: c++
251
252  struct Message { /* ... */; char Flags[8]; };
253  void clearFlags(Message *Array, size_t N) {
254    const Message *End = Array + N;
255    while (Array < End) {
256      memset(Array + offsetof(Message, Flags), // Suspicious pointer arithmetic using offsetof()!
257             0, sizeof(Message::Flags));
258      ++Array;
259    }
260  }
261
262For this checked bogus pattern, `cert-arr39-c` redirects here as an alias of
263this check.
264
265This check corresponds to the CERT C Coding Standard rule
266`ARR39-C. Do not add or subtract a scaled integer to a pointer
267<http://wiki.sei.cmu.edu/confluence/display/c/ARR39-C.+Do+not+add+or+subtract+a+scaled+integer+to+a+pointer>`_.
268
269Limitations
270"""""""""""
271
272Cases where the pointee type has a size of `1` byte (such as, and most
273importantly, ``char``) are excluded.
274
275Options
276-------
277
278.. option:: WarnOnSizeOfConstant
279
280   When `true`, the check will warn on an expression like
281   ``sizeof(CONSTANT)``. Default is `true`.
282
283.. option:: WarnOnSizeOfIntegerExpression
284
285   When `true`, the check will warn on an expression like ``sizeof(expr)``
286   where the expression results in an integer. Default is `false`.
287
288.. option:: WarnOnSizeOfThis
289
290   When `true`, the check will warn on an expression like ``sizeof(this)``.
291   Default is `true`.
292
293.. option:: WarnOnSizeOfCompareToConstant
294
295   When `true`, the check will warn on an expression like
296   ``sizeof(expr) <= k`` for a suspicious constant `k` while `k` is `0` or
297   greater than `0x8000`. Default is `true`.
298
299.. option:: WarnOnSizeOfPointerToAggregate
300
301   When `true`, the check will warn when the argument of ``sizeof`` is either a
302   pointer-to-aggregate type, an expression returning a pointer-to-aggregate
303   value or an expression that returns a pointer from an array-to-pointer
304   conversion (that may be implicit or explicit, for example ``array + 2`` or
305   ``(int *)array``). Default is `true`.
306
307.. option:: WarnOnSizeOfPointer
308
309   When `true`, the check will report all expressions where the argument of
310   ``sizeof`` is an expression that produces a pointer (except for a few
311   idiomatic expressions that are probably intentional and correct).
312   This detects occurrences of CWE 467. Default is `false`.
313
314.. option:: WarnOnOffsetDividedBySizeOf
315
316   When `true`, the check will warn on pointer arithmetic where the
317   element count is obtained from a division with ``sizeof(...)``,
318   e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.
319