brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.8 KiB · aa2e529 Raw
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