PC-LINT Warning while Copying Struct into array in C

65 Views Asked by At

This is my first question on Stack Exchange so be with me. In one of my embedded C projects, I am trying to copy a portion of structure data into an array. Ex:

typedef struct
{
  uint16_t Length_ui16;
  uint8_t  Message_Id_ui8;
  uint8_t  SensorPosition_ui8;
  uint8_t  SenType_ui8a[9];
  uint8_t  SenArticleNo_ui8a[9];
  uint8_t  SenSerialNo_ui8a[9];
  uint8_t  SenSoftVer_ui8a[6];
}data;

memcpy(destArr_ui8, &data.SensorPosition_ui8, 34);      

Explanation: I am trying to copy data from SensorPosition onwards (34 bytes). NOTE: structure padding and destArr size is already taken into consideration Code is working as per my expectations. But my PC-LINT got with me on this.

PC-LINT Warning 662: Possible creation of out-of-bounds pointer (33 beyond end of data) by operator '['

My analysis is that PC_LINT thinks that the source is just a single byte variable (SensorPosition ), so giving 33 bytes outbound warning.

So, the real question is that,

  1. Is there any other elegant or commonly used method which is satisfy PC_LINT also to achieve the same thing.
  2. If, no than how do I change my code to satisfy the PC_LINT on this.
3

There are 3 best solutions below

0
nielsen On BEST ANSWER

If I understand correctly, the "payload" of the data structure needs to be copied to a byte array.

The following creates a pointer to the payload viewed as bytes and calculates the payload size to do the copying:

  const uint8_t *Payload = (const uint8_t *)data + offsetof(data, SensorPosition_ui8);
  size_t PayloadSize = offsetof(data, SenSoftVer_ui8a) + sizeof(data.SenSoftVer_ui8a) - offsetof(data, SensorPosition_ui8);
  memcpy(destArr_ui8, Payload, PayloadSize);

Notice that this calculation of PayloadSize excludes any padding which the compiler may have inserted at the end of the structure. It is highly unlikely that padding will be inserted between members in this structure. To make absolutely sure, a static assertion may be added to check that the expression for PayloadSize is actually 34.

If it has already been checked that there is no padding (between or after members) in the struct, then the calculation of PayloadSize can be simplified to sizeof(data) - offsetof(data, SensorPosition_ui8).

1
mediocrevegetable1 On

I'm pretty sure that's undefined behaviour; yes, you know what comes after SensorPosition_ui8, but when you call memcpy you pass a pointer specifically to that one single member, and you are going out of bounds of that one member.

Even if you think that the UB doesn't really matter here, there's still an issue of there potentially being padding between the members involved in the memcpy. That not only means SensorPosition_ui8 to SenSoftVer_ui8 occupies more than 34 bytes, but destArr_ui8 could very well have random padding bytes after the memcpy for which you don't know their placement or values.

I'd suggest using a union here. In addition to fixing your problem, it could also make the source code a bit cleaner.

typedef struct
{
  uint16_t Length_ui16;
  uint8_t  Message_Id_ui8;

  union
  {
    uint8_t Sensor_ui8a[34];
    struct
    {
      uint8_t  SensorPosition_ui8;
      uint8_t  SenType_ui8a[9];
      uint8_t  SenArticleNo_ui8a[9];
      uint8_t  SenSerialNo_ui8a[9];
      uint8_t  SenSoftVer_ui8a[6];
    };
  };
} data;
// ...
memcpy(destArr_ui8, data.Sensor_ui8a, sizeof data.Sensor_ui8a);

Still, if it's important that your struct contain no bytes of padding, you should specify the struct as packed; different compilers have different ways to go about this, so look into it for your specific compiler.

0
Lundin On

Out of bounds (informal term) is generally something associated with pointer arithmetic. Because C specifies that pointer arithmetic can only be done within the bounds of an array.

We can use either *(ptr + n) or ptr[n], they are equivalent. The latter boils down to the former and in either case we end up in the rules for "the additive operators" (C17 6.5.6). The relevant parts there is what specifies "out of bounds":

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

memcpy may be implemented internally in whatever way that suits the library implementation. It need not use pointer arithmetic nor does it need to be written in conforming C. So there's not really anything undefined about your code. Except there may be potential padding bytes between struct members.

Now of course the purpose of static analyzers is to to be picky about smelly code and what you are doing here does look quite fishy.

As for better solutions, the union version in the answer by @mediocrevegetable1 is sound. Alternatively you could have a raw byte array corresponding to the whole struct and then copy from one offset in that byte array:

#include <stddef.h>

#define DATA_SIZE (2+1+1+9+9+9+6)

typedef union
{
  struct
  {
    uint16_t Length_ui16;
    uint8_t  Message_Id_ui8;
    uint8_t  SensorPosition_ui8;
    uint8_t  SenType_ui8a[9];
    uint8_t  SenArticleNo_ui8a[9];
    uint8_t  SenSerialNo_ui8a[9];
    uint8_t  SenSoftVer_ui8a[6];
  };

  uint8_t bytes [DATA_SIZE];
} data;

...
data d;
uint8_t* start = &d.bytes[offsetof(data, SensorPosition_ui8)];
size_t   size  = sizeof(d) - offsetof(data, SensorPosition_ui8);
memcpy(somewhere, start, size);