329 lines · plain
1.. title:: clang-tidy - bugprone-sizeof-expression2 3bugprone-sizeof-expression4==========================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 be9an expression or the parenthesized name of a type. Misuse of this operator may10be 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 is16equivalent to query the size of its type (probably ``int``). The intent of the17programmer was probably to simply get the integer and not its size.18 19.. code-block:: c++20 21 #define BUFLEN 4222 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 common29mistake is to query the ``sizeof`` on the integer or enum that represents the30type that should be used by ``sizeof``. This results in the size of the integer31and not of the type the integer represents:32 33.. code-block:: c++34 35 enum data_type {36 FLOAT_TYPE,37 DOUBLE_TYPE38 };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. The60programmer most likely wanted the size of the object and not the size of the61pointer.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 with75``char* A = ""`` and ``char A[] = ""``. The first case has the type ``char*``76instead of the aggregate type ``char[]``. Using ``sizeof`` on an object77declared with ``char*`` type is returning the size of a pointer instead of78the number of characters (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 of105elements 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 the108type ``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) => 10113 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 the118size of ``char*``. One can easily be fooled by its declaration, but in parameter119declaration 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' expression129------------------------------------------130 131Multiplying ``sizeof`` expressions typically makes no sense and is probably a132logic error. In the following example, the programmer used ``*`` instead of133``/``.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 is143working 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 error157hidden 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 arithmetic168--------------------------------------------------------169 170Arithmetic operators on pointers automatically scale the result with the size171of the pointed typed.172Further use of ``sizeof`` around pointer arithmetic will typically result in an173unintended result.174 175Scaling the result of pointer difference176^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^177 178Subtracting two pointers results in an integer expression (of type179``ptrdiff_t``) which expresses the distance between the two pointed objects in180"number of objects between".181A common mistake is to think that the result is "number of bytes between", and182scale the difference with ``sizeof``, such as ``P1 - P2 == N * sizeof(T)``183(instead of ``P1 - P2 == N``) or ``(P1 - P2) / sizeof(T)`` instead of184``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 integer212^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^213 214Conversely, when performing pointer arithmetics to add or subtract from a215pointer, the arithmetic operator implicitly scales the value actually added to216the pointer with the size of the pointee, as ``Ptr + N`` expects ``N`` to be217"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 an225element or the whole buffer is suspicious, because the dimensional connection226between the numeric value and the actual ``sizeof`` result can not always be227deduced.228While scaling an integer up (multiplying) with ``sizeof`` is likely **always**229an issue, a scaling down (division) is not always inherently dangerous, in case230the developer is aware that the division happens between an appropriate number231of _bytes_ and a ``sizeof`` value.232Turning :option:`WarnOnOffsetDividedBySizeOf` off will restrict the233warnings to the multiplication case.234 235This case also checks suspicious ``alignof`` and ``offsetof`` usages in236pointer 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 of263this check.264 265This check corresponds to the CERT C Coding Standard rule266`ARR39-C. Do not add or subtract a scaled integer to a pointer267<http://wiki.sei.cmu.edu/confluence/display/c/ARR39-C.+Do+not+add+or+subtract+a+scaled+integer+to+a+pointer>`_.268 269 270Limitations271-----------272 273Cases where the pointee type has a size of `1` byte (such as, and most274importantly, ``char``) are excluded.275 276Options277-------278 279.. option:: WarnOnSizeOfConstant280 281 When `true`, the check will warn on an expression like282 ``sizeof(CONSTANT)``. Default is `true`.283 284.. option:: WarnOnSizeOfIntegerExpression285 286 When `true`, the check will warn on an expression like ``sizeof(expr)``287 where the expression results in an integer. Default is `false`.288 289.. option:: WarnOnSizeOfThis290 291 When `true`, the check will warn on an expression like ``sizeof(this)``.292 Default is `true`.293 294.. option:: WarnOnSizeOfCompareToConstant295 296 When `true`, the check will warn on an expression like297 ``sizeof(expr) <= k`` for a suspicious constant `k` while `k` is `0` or298 greater than `0x8000`. Default is `true`.299 300.. option:: WarnOnSizeOfPointerToAggregate301 302 When `true`, the check will warn when the argument of ``sizeof`` is either a303 pointer-to-aggregate type, an expression returning a pointer-to-aggregate304 value or an expression that returns a pointer from an array-to-pointer305 conversion (that may be implicit or explicit, for example ``array + 2`` or306 ``(int *)array``). Default is `true`.307 308.. option:: WarnOnSizeOfPointer309 310 When `true`, the check will report all expressions where the argument of311 ``sizeof`` is an expression that produces a pointer (except for a few312 idiomatic expressions that are probably intentional and correct).313 This detects occurrences of CWE 467. Default is `false`.314 315.. option:: WarnOnOffsetDividedBySizeOf316 317 When `true`, the check will warn on pointer arithmetic where the318 element count is obtained from a division with ``sizeof(...)``,319 e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.320 321.. option:: WarnOnSizeOfInLoopTermination322 323 When `true`, the check will warn about incorrect use of sizeof expression324 in loop termination condition. The warning triggers if the ``sizeof``325 expression appears to be incorrectly used to determine the number of326 array/buffer elements.327 e.g, ``long arr[10]; for(int i = 0; i < sizeof(arr); i++) { ... }``. Default328 is `true`.329