Main Content

CWE Rule 188

Reliance on Data/Memory Layout

Since R2023a

Description

Rule Description

The software makes invalid assumptions about how protocol data or memory is organized at a lower level, resulting in unintended program behavior.

Polyspace Implementation

The rule checker checks for these issues:

  • Invalid assumptions about memory organization

  • Memory comparison of padding data

  • Missing byte reordering when transferring data

Examples

expand all

Issue

This issue occurs when you compute the address of a variable in the stack by adding or subtracting from the address of another non-array variable.

Risk

When you compute the address of a variable in the stack by adding or subtracting from the address of another variable, you assume a certain memory organization. If your assumption is incorrect, accessing the computed address can be invalid.

Fix

Do not perform an access that relies on assumptions about memory organization.

Example — Reliance on Memory Organization
void func(void) {
    int var1 = 0x00000011, var2;
    *(&var1 + 1) = 0; //Noncompliant
}

In this example, the programmer relies on the assumption that &var1 + 1 provides the address of var2. Therefore, an Invalid assumptions about memory organization appears on the + operation. In addition, a Pointer access out of bounds error also appears on the dereference.

Correction — Do Not Rely on Memory Organization

One possible correction is not perform direct computation on addresses to access separately declared variables.

Issue

This issue occurs when you use the memcmp function to compare two structures as a whole. In the process, you compare meaningless data stored in the structure padding.

For instance:

struct structType {
    char member1;
    int member2;
    .
    .
};

structType var1;
structType var2;
.
.
if(memcmp(&var1,&var2,sizeof(var1))) 
{...}

Risk

If members of a structure have different data types, your compiler introduces additional padding for data alignment in memory. For an example of padding, see Higher Estimate of Size of Local Variables (Polyspace Code Prover).

The content of these extra padding bytes is meaningless. The C Standard allows the content of these bytes to be indeterminate, giving different compilers latitude to implement their own padding. If you perform a byte-by-byte comparison of structures with memcmp, you compare even the meaningless data stored in the padding. You might reach the false conclusion that two data structures are not equal, even if their corresponding members have the same value.

Fix

Instead of comparing two structures in one attempt, compare the structures member by member.

For efficient code, write a function that does the comparison member by member. Use this function for comparing two structures.

You can use memcmp for byte-by-byte comparison of structures only if you know that the structures do not contain padding. Typically, to prevent padding, you use specific attributes or pragmas such as #pragma pack. However, these attributes or pragmas are not supported by all compilers and make your code implementation-dependent. If your structures contain bit-fields, using these attributes or pragmas cannot prevent padding.

Example — Structures Compared with memcmp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define fatal_error() abort()

typedef struct s_padding
{
    char c;            
    int i;
    unsigned int bf1:1;    
    unsigned int bf2:2;
    unsigned char buffer[20];
} S_Padding ;

/* Function that guarantees safe access to the input memory */
extern int trusted_memory_zone(void *ptr, size_t sz); 

int func(const S_Padding *left, const S_Padding *right)
{

    if (!trusted_memory_zone((void *)left, sizeof(S_Padding)) ||
        !trusted_memory_zone((void *)right, sizeof(S_Padding))) {
        fatal_error();
    }

    if (0 == memcmp(left, right, sizeof(S_Padding)))  //Noncompliant
    {
        return 1;
    }
    else
        return 0;
}

In this example, memcmp compares byte-by-byte the two structures that left and right point to. Even if the values stored in the structure members are the same, the comparison can show an inequality if the meaningless values in the padding bytes are not the same.

Correction — Compare Structures Member by Member

One possible correction is to compare individual structure members.

Note

You can compare entire arrays by using memcmp. All members of an array have the same data type. Padding bytes are not required to store arrays.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define fatal_error() abort()

typedef struct s_padding
{
    char c;         
    int i;
    unsigned int bf1:1;    
    unsigned int bf2:2;
    unsigned char buffer[20];
} S_Padding ;

/* Function that guarantees safe access to the input memory */
extern int trusted_memory_zone(void *ptr, size_t sz); 

int func(const S_Padding *left, const S_Padding *right)
{
    if (!trusted_memory_zone((void *)left, sizeof(S_Padding)) ||
        !trusted_memory_zone((void *)right, sizeof(S_Padding))) {
        fatal_error();
    }

    return ((left->c == right->c) &&                
            (left->i == right->i) &&
            (left->bf1 == right->bf1) &&
            (left->bf2 == right->bf2) &&
            (memcmp(left->buffer, right->buffer, 20) == 0)); 
}
Issue

This issue occurs when you do not use a byte ordering function:

  • Before sending data to a network socket.

  • After receiving data from a network socket.

Risk

Some system architectures implement little endian byte ordering (least significant byte first), and other systems implement big endian (most significant byte first). If the endianness of the sent data does not match the endianness of the receiving system, the value returned when reading the data is incorrect.

Fix

After receiving data from a socket, use a byte ordering function such as ntohl(). Before sending data to a socket, use a byte ordering function such as htonl().

Example — Data Transferred Without Byte Reordering
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <byteswap.h>
#include <unistd.h>
#include <string.h>


unsigned int func(int sock, int server)
{
    unsigned int num;   /* assume int is 32-bits */
    if (server)
    {
        /* Server side */
        num = 0x17;
		/* Endianness of server host may not match endianness of network. */
        if (send(sock, (void *)&num, sizeof(num), 0) < (int)sizeof(num))  //Noncompliant
        {
            /* Handle error */
        }
        return 0;
    }
    else {
        /* Endianness of client host may not match endianness of network. */
        if (recv (sock, (void *)&num, sizeof(num), 0) < (int) sizeof(num))  
        {
            /* Handle error */
        }
		
		/* Comparison may be inaccurate */
        if (num> 255)  //Noncompliant
        {
            return 255;
        }
        else
        {
            return num;
        }
    }
}
        
      

In this example, variable num is assigned hexadecimal value 0x17 and is sent over a network to the client from the server. If the server host is little endian and the network is big endian, num is transferred as 0x17000000. The client then reads an incorrect value for num and compares it to a local numeric value.

Correction — Use Byte Ordering Function

Before sending num from the server host, use htonl() to convert from host to network byte ordering. Similarly, before reading num on the client host, use ntohl() to convert from network to host byte ordering.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <byteswap.h>
#include <unistd.h>
#include <string.h>

unsigned int func(int sock, int server)
{
    unsigned int num;   /* assume int is 32-bits */
    if (server)
    {
        /* Server side */
        num = 0x17;
		
		/* Convert to network byte order. */
        num = htonl(num); 
        if (send(sock, (void *)&num, sizeof(num), 0) < (int)sizeof(num)) 
        {
            /* Handle error */
        }
        return 0;
    }
    else {
        if (recv (sock, (void *)&num, sizeof(num), 0) < (int) sizeof(num)) 
        {
            /* Handle error */
        }
		
		/* Convert to host byte order. */
        num = ntohl(num); 
        if (num > 255) 
        {
            return 255;
        }
        else
        {
            return num;
        }
    }
} 

Check Information

Category: Data Neutralization Issues

Version History

Introduced in R2023a