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