Main Content

CWE Rule 129

Improper Validation of Array Index

Since R2023a

Description

Rule Description

The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array.

Polyspace Implementation

The rule checker checks for these issues:

  • Array access with tainted index

  • Invalid use of standard library string routine

  • Pointer dereference with tainted offset

  • Tainted division operand

  • Tainted modulo operand

Examples

expand all

Issue

This issue occurs when you access an array by using an index that is obtained from unsecure sources and which has not been validated.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite — writing to memory before the beginning of the buffer.

  • Buffer overflow — writing to memory after the end of a buffer.

  • Over-reading a buffer — accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write operation create to problems in your program.

Fix

Before using the index to access the array, validate the index value to make sure that it is inside the array range.

Extend Checker

By default, Polyspace® assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Use Index to Return Buffer Value
#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
  return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
    return tab[num];//Noncompliant  
}

In this example, the index num accesses the array tab. The index num is obtained from an unsecure source and the function taintedarrayindex does not check to see if num is inside the range of tab.

Correction — Check Range Before Use

One possible correction is to check that num is in range before using it.

#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
	return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
	if (num >= 0 && num < SIZE100) {
		return tab[num]; 
	} else {
		return -1;
	}
}

Issue

This issue occurs when a string library function is called with invalid arguments.

Risk

The risk depends on the type of invalid arguments. For instance, using the strcpy function with a source argument larger than the destination argument can result in buffer overflows.

Fix

The fix depends on the standard library function involved in the defect. In some cases, you can constrain the function arguments before the function call. For instance, if the strcpy function:

char * strcpy(char * destination, const char* source)
tries to copy too many bytes into the destination argument compared to the available buffer, constrain the source argument before the call to strcpy. In some cases, you can use an alternative function to avoid the error. For instance, instead of strcpy, you can use strncpy to control the number of bytes copied.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Example — Invalid Use of Standard Library String Routine Error
 #include <string.h>
 #include <stdio.h>
 
 char* Copy_String(void)
 {
  char *res;
  char gbuffer[5],text[20]="ABCDEFGHIJKL";

  res=strcpy(gbuffer,text); //Noncompliant
  /* Error: Size of text is less than gbuffer */

  return(res);
 }

The string text is larger in size than gbuffer. Therefore, the function strcpy cannot copy text into gbuffer.

Correction — Use Valid Arguments

One possible correction is to declare the destination string gbuffer with equal or larger size than the source string text.

#include <string.h>
 #include <stdio.h>
 
 char* Copy_String(void)
 {
  char *res;
  /*Fix: gbuffer has equal or larger size than text */
  char gbuffer[20],text[20]="ABCDEFGHIJKL";

  res=strcpy(gbuffer,text);

  return(res);
 }
Issue

This issue occurs when a pointer dereference uses an offset variable from an unknown or unsecure source.

This check focuses on dynamically allocated buffers. For static buffer offsets, see Array access with tainted index.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite, or writing to memory before the beginning of the buffer.

  • Buffer overflow, or writing to memory after the end of a buffer.

  • Over reading a buffer, or accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write to compromise your program.

Fix

Validate the index before you use the variable to access the pointer. Check to make sure that the variable is inside the valid range and does not overflow.

Extend Checker

By default, Polyspace assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Dereference Pointer Array
#include <stdio.h>
#include <stdlib.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if(pint) {
        /* Filling array */
        read_pint(pint);
        c = pint[offset];//Noncompliant
        free(pint);
    }
    return c;
}

In this example, the function initializes an integer pointer pint. The pointer is dereferenced using the input index offset. The value of offset could be outside the pointer range, causing an out-of-range error.

Correction — Check Index Before Dereference

One possible correction is to validate the value of offset. Continue with the pointer dereferencing only if offset is inside the valid range.

#include <stdlib.h>
#include <stdio.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if (pint) {
        /* Filling array */
        read_pint(pint);
        if (offset>0 && offset<SIZE10) {
            c = pint[offset];
        }
        free(pint);
    }
    return c;
}
Issue

This issue occurs when one or both integer operands in a division operation comes from unsecure sources.

Risk

  • If the numerator is the minimum possible value and the denominator is -1, your division operation overflows because the result cannot be represented by the current variable size.

  • If the denominator is zero, your division operation fails possibly causing your program to crash.

These risks can be used to execute arbitrary code. This code is usually outside the scope of a program's implicit security policy.

Fix

Before performing the division, validate the values of the operands. Check for denominators of 0 or -1, and numerators of the minimum integer value.

Extend Checker

By default, Polyspace assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Division of Function Arguments
#include <limits.h>
#include <stdio.h>

extern void print_int(int);

int taintedintdivision(void) {
    long num, denum;
    scanf("%lf %lf", &num, &denum);
    int r =  num/denum; //Noncompliant
    print_int(r);
    return r;
}

This example function divides two argument variables, then prints and returns the result. The argument values are unknown and can cause division by zero or integer overflow.

Correction — Check Values

One possible correction is to check the values of the numerator and denominator before performing the division.

#include <limits.h>
#include <stdio.h>

extern void print_long(long);

int taintedintdivision(void) {
    long num, denum;
    scanf("%lf %lf", &num, &denum);
    long res= 0;
    if (denum!=0 && !(num==INT_MIN && denum==-1)) {
        res = num/denum;
    }
    print_long(res);
    return res;
}
Issue

This issue occurs when one or both integer operands in a remainder operation (%) comes from unsecure sources.

Risk

  • If the second remainder operand is zero, your remainder operation fails, causing your program to crash.

  • If the second remainder operand is -1, your remainder operation can overflow if the remainder operation is implemented based on the division operation that can overflow.

  • If one of the operands is negative, the operation result is uncertain. For C89, the modulo operation is not standardized, so the result from negative operands is implementation-defined.

These risks can be exploited by attackers to gain access to your program or the target in general.

Fix

Before performing the modulo operation, validate the values of the operands. Check the second operand for values of 0 and -1. Check both operands for negative values.

Extend Checker

By default, Polyspace assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Modulo with User Input
#include <stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem =  128%userden; //Noncompliant
    print_int(rem);
    return rem;
}

In this example, the function performs a modulo operation by using a user input. The input is not checked before calculating the remainder for values that can crash the program, such as 0 and -1.

Correction — Check Operand Values

One possible correction is to check the values of the operands before performing the modulo operation. In this corrected example, the modulo operation continues only if the second operand is greater than zero.

#include<stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem = 0;
    if (userden > 0 ) { 
        rem = 128 % userden; 
    }
    print_int(rem);
    return rem;
}

Check Information

Category: Data Validation Issues

Version History

Introduced in R2023a