Main Content

CWE Rule 123

Write-what-where Condition

Since R2023a

Description

Rule Description

Any condition where the attacker has the ability to write an arbitrary value to an arbitrary location, often as the result of a buffer overflow.

Polyspace Implementation

The rule checker checks for these issues:

  • Array access with tainted index

  • Invalid use of standard library memory routine

  • Pointer dereference with tainted offset

  • Tainted NULL or non-null-terminated string

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 memory library function is called with invalid arguments. For instance, the memcpy function copies to an array that cannot accommodate the number of bytes copied.

Risk

Use of a memory library function with invalid arguments can result in issues such as buffer overflow.

Fix

The fix depends on the root cause of the defect. Often the result details (or source code tooltips in Polyspace as You Code) show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show this event history, you can search for previous references of variables relevant to the defect using right-click options in the source code and find related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface or Interpret Bug Finder Results in Polyspace Access Web Interface (Polyspace Access).

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 Memory Routine Error
#include <string.h>
#include <stdio.h>

char* Copy_First_Six_Letters(void)
 {
  char str1[10],str2[5];

  printf("Enter string:\n");
  scanf("%s",str1);

  memcpy(str2,str1,6);  //Noncompliant
  /* Defect: Arguments of memcpy invalid: str2 has size < 6 */

  return str2;
 }

The size of string str2 is 5, but six characters of string str1 are copied into str2 using the memcpy function.

Correction — Call Function with Valid Arguments

One possible correction is to adjust the size of str2 so that it accommodates the characters copied with the memcpy function.

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

char* Copy_First_Six_Letters(void)
 {
  /* Fix: Declare str2 with size 6 */
  char str1[10],str2[6]; 

  printf("Enter string:\n");
  scanf("%s",str1);

  memcpy(str2,str1,6);
  return str2;
 }
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 strings from unsecure sources are used in string manipulation routines that implicitly dereference the string buffer, for instance, strcpy or sprintf.

Tainted NULL or non-null-terminated string raises no defect for a string returned from a call to scanf-family variadic functions. Similarly, no defect is raised when you pass the string with a %s specifier to printf-family variadic functions.

Risk

If a string is from an unsecure source, it is possible that an attacker manipulated the string or pointed the string pointer to a different memory location.

If the string is NULL, the string routine cannot dereference the string, causing the program to crash. If the string is not null-terminated, the string routine might not know when the string ends. This error can cause you to write out of bounds, causing a buffer overflow.

Fix

Validate the string before you use it. Check that:

  • The string is not NULL.

  • The string is null-terminated

  • The size of the string matches the expected size.

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 — Getting String from Input
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
void warningMsg(void)
{
	char userstr[MAX];
	read(0,userstr,MAX);
	char str[SIZE128] = "Warning: ";
	strncat(str, userstr, SIZE128-(strlen(str)+1));//Noncompliant
	print_str(str);
}


In this example, the string str is concatenated with the argument userstr. The value of userstr is unknown. If the size of userstr is greater than the space available, the concatenation overflows.

Correction — Validate the Data

One possible correction is to check the size of userstr and make sure that the string is null-terminated before using it in strncat. This example uses a helper function, sansitize_str, to validate the string. The defects are concentrated in this function.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
int sanitize_str(char* s) {
	int res = 0; 
	if (s && (strlen(s) > 0)) { // Noncompliant-TAINTED_STRING only flagged here
		// - string is not null
		// - string has a positive and limited size
		// - TAINTED_STRING on strlen used as a firewall
		res = 1;
	}
	return res; 
}
void warningMsg(void)
{
	char userstr[MAX];
	read(0,userstr,MAX);
	char str[SIZE128] = "Warning: ";
	if (sanitize_str(userstr))	
		strncat(str, userstr, SIZE128-(strlen(str)+1));
	print_str(str);
}
Correction — Validate the Data

Another possible correction is to call function errorMsg and warningMsg with specific strings.

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

#define SIZE128 128

extern void print_str(const char*);

void warningMsg(char* userstr)
{
    char str[SIZE128] = "Warning: ";
    strncat(str, userstr, SIZE128-(strlen(str)+1));
    print_str(str);
}

void errorMsg(char* userstr)
{
  char str[SIZE128] = "Error: ";
  strncat(str, userstr, SIZE128-(strlen(str)+1));
  print_str(str);
}

int manageSensorValue(int sensorValue) {
  int ret = sensorValue;
  if ( sensorValue < 0 ) {
    errorMsg("sensor value should be positive");
    exit(1);
  } else if ( sensorValue > 50 ) {
    warningMsg("sensor value greater than 50 (applying threshold)...");
    sensorValue = 50;
  }
  
  return sensorValue;
}

Check Information

Category: Memory Buffer Errors

Version History

Introduced in R2023a