Main Content

ISO/IEC TS 17961 [taintformatio]

Using a tainted value to write to an object using a formatted input or output function

Description

Rule Definition

Using a tainted value to write to an object using a formatted input or output function.1

Polyspace Implementation

This checker checks for these issues:

  • Buffer overflow from incorrect string format specifier.

  • Destination buffer overflow in string manipulation.

  • Invalid use of standard library routine.

  • Invalid use of standard library string routine.

  • Tainted NULL or non-null-terminated string.

  • Tainted string format specifier.

  • Invalid use of standard library string routine.

  • Use of dangerous standard function.

  • Insufficient destination buffer size

Extend Checker

A default Bug Finder analysis might not flag a Tainted NULL or non-null-terminated string or Tainted string format specifier issue for certain inputs that originate outside of the current analysis boundary. 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.

Examples

expand all

Issue

Buffer overflow from incorrect string format specifier occurs when the format specifier argument for functions such as sscanf leads to an overflow or underflow in the memory buffer argument.

Risk

If the format specifier specifies a precision that is greater than the memory buffer size, an overflow occurs. Overflows can cause unexpected behavior such as memory corruption.

Fix

Use a format specifier that is compatible with the memory buffer size.

Example - Memory Buffer Overflow
#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%33c", buf);
}

In this example, buf can contain 32 char elements. Therefore, the format specifier %33c causes a buffer overflow.

Correction — Use Smaller Precision in Format Specifier

One possible correction is to use a smaller precision in the format specifier.

#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%32c", buf);
}
Issue

Destination buffer overflow in string manipulation occurs when certain string manipulation functions write to their destination buffer argument at an offset greater than the buffer size.

For instance, when calling the function sprintf(char* buffer, const char* format), you use a constant string format of greater size than buffer.

Risk

Buffer overflow can cause unexpected behavior such as memory corruption or stopping your system. Buffer overflow also introduces the risk of code injection.

Fix

One possible solution is to use alternative functions to constrain the number of characters written. For instance:

  • If you use sprintf to write formatted data to a string, use snprintf, _snprintf or sprintf_s instead to enforce length control. Alternatively, use asprintf to automatically allocate the memory required for the destination buffer.

  • If you use vsprintf to write formatted data from a variable argument list to a string, use vsnprintf or vsprintf_s instead to enforce length control.

  • If you use wcscpy to copy a wide string, use wcsncpy, wcslcpy, or wcscpy_s instead to enforce length control.

Another possible solution is to increase the buffer size.

Example - Buffer Overflow in sprintf Use
#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    sprintf(buffer, fmt_string);
}

In this example, buffer can contain 20 char elements but fmt_string has a greater size.

Correction — Use snprintf Instead of sprintf

One possible correction is to use the snprintf function to enforce length control.

#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    snprintf(buffer, 20, fmt_string);
}
Issue

This issue occurs when you use invalid arguments with a function from the standard library. This defect picks up errors related to other functions not covered by float, integer, memory, or string standard library routines.

Risk

Invalid arguments to a standard library function result in undefined behavior.

Fix

The fix depends on the root cause of the defect. For instance, the argument to a printf function can be NULL because a pointer was initialized with NULL and the initialization value was not overwritten along a specific execution path.

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 – Calling printf Without a String
#include <stdio.h>
#include <stdlib.h>

void print_null(void) {

  printf(NULL); 
}

The function printf takes only string input arguments or format specifiers. In this function, the input value is NULL, which is not a valid string.

Correction — Use Compatible Input Arguments

One possible correction is to change the input arguments to fit the requirements of the standard library routine. In this example, the input argument was changed to a character.

#include <stdio.h>

void print_null(void) {
    char zero_val = '0';
    printf((const char*)zero_val); 
}
Issue

Invalid use of standard library string routine 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 also Interpret Bug Finder Results in Polyspace Desktop User Interface.

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); 
  /* 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 strings from nonsecure sources are used in string manipulation routines that implicitly dereference the string buffer, for instance, strcpy or sprintf.

The checker 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.

Example – Getting String from Input Argument
#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));
	print_str(str);
}



void errorMsg(void)
{
	char userstr[MAX];
	read(0,userstr,MAX);
	char str[SIZE128] = "Error: ";
	strncat(str, userstr, SIZE128-(strlen(str)+1));
	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 1 — 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)) { // Defect only raised 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 2 — 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;
}
Issue

This issue occurs when printf-style functions use a format specifier constructed from nonsecure sources.

Risk

If you use externally controlled elements to format a string, you can cause buffer overflow or data-representation problems. An attacker can use these string formatting elements to view the contents of a stack using %x or write to a stack using %n.

Fix

Pass a static string to format string functions. This fix ensures that an external actor cannot control the string.

Another possible fix is to allow only the expected number of arguments. If possible, use functions that do not support the vulnerable %n operator in format strings.

Example – Get Elements from User Input
#include <stdio.h>
#include <unistd.h>
#define MAX 40
void taintedstringformat(void) {
	char userstr[MAX];
	read(0,userstr,MAX);
	printf(userstr);   
}

This example prints the input argument userstr. The string is unknown. If it contains elements such as %, printf can interpret userstr as a string format instead of a string, causing your program to crash.

Correction — Print as String

One possible correction is to print userstr explicitly as a string so that there is no ambiguity.

#include <stdio.h>
#include <unistd.h>
#define MAX 40
void taintedstringformat(void) {
	char userstr[MAX];
	read(0,userstr,MAX);
	printf("%.20s", userstr);;   
}
Issue

The Use of dangerous standard function check highlights uses of functions that are inherently dangerous or potentially dangerous given certain circumstances. The following table lists possibly dangerous functions, the risks of using each function, and what function to use instead.

Dangerous FunctionRisk LevelSafer Function
getsInherently dangerous — You cannot control the length of input from the console.fgets
cinInherently dangerous — You cannot control the length of input from the console.Avoid or prefaces calls to cin with cin.width.
strcpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.strncpy
stpcpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.stpncpy
lstrcpy or StrCpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.StringCbCopy, StringCchCopy, strncpy, strcpy_s, or strlcpy
strcatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.strncat, strlcat, or strcat_s
lstrcat or StrCatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.StringCbCat, StringCchCat, strncay, strcat_s, or strlcat
wcpcpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.wcpncpy
wcscatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.wcsncat, wcslcat, or wcncat_s
wcscpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.wcsncpy
sprintfPossibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur.snprintf
vsprintfPossibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur.vsnprintf
Risk

These functions can cause buffer overflow, which attackers can use to infiltrate your program.

Fix

The fix depends on the root cause of the defect. Often the result details 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 the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.

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 - Using sprintf
#include <stdio.h>
#include <string.h>
#include <iostream>

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (sprintf(dst, "%s", str) == 1)
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}

This example function uses sprintf to copy the string str to dst. However, if str is larger than the buffer, sprintf can cause buffer overflow.

Correction — Use snprintf with Buffer Size

One possible correction is to use snprintf instead and specify a buffer size.

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

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (snprintf(dst, sizeof(dst), "%s", str) == 1)
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}
Issue

Insufficient destination buffer size occurs when the destination buffer in a strcpy operation cannot accommodate the source buffer and a null terminator. This issue is reported if the size of the source buffer is unknown. Consider this code:

int main (int argc, char *argv[])
{
  const char *const input = ((argc && argv[0]) ? argv[0] : "");
  char str[100];
  strcpy(str, input); // Noncompliant
}
In this case, the size of the source buffer input is unknown. The size of the destination buffer str might be smaller than the value (strlen(input)+1). Polyspace® reports a violation on the strcpy operation.

Risk

Using a destination buffer of insufficient size might allow an attacker to cause a buffer overflow. In the preceding code example, if argv[0] contains 100 or more characters, the strcpy operation results in a buffer overflow.

Fix

Before calling the function strcpy(), allocate sufficient memory dynamically. For instance, use the function strlen() to determine the size of the source buffer and then allocate the destination buffer so that its size is greater than the value strlen(source) + 1.

Alternatively, use the function strncpy() instead of strcpy(). The function strncpy() copies a known number of characters from the source buffer to the destination buffer. Because the function strncpy() copies a known number of characters, it is a safer alternative to strcpy().

Example — Destination Buffer Too Small

In this example, the size of the source buffer is unknown, while the size of the destination buffer is fixed at 128. The size of the destination buffer might not be sufficient to accommodate the characters from the source buffer and terminate the buffer with a null. Polyspace reports a violation of the rule.

#include <string.h>
  
int main(int argc, char *argv[]) {
  const char *const source = (argc && argv[0]) ? argv[0] : "";
  char destination[128];
  strcpy(destination,source);//Noncompliant
  
  return 0;
}
Correction — Use strncpy instead of strcpy

To fix this issue, use strncpy() to copy a known number of characters from the source buffer to the destination buffer.

#include <string.h>
#define MAX_ARGS  128  
int main(int argc, char *argv[]) {
  const char *const source = (argc && argv[0]) ? argv[0] : "";
  char destination[MAX_ARGS];
  int num = (strlen(source)+1>MAX_ARGS)?MAX_ARGS:strlen(source)+1;
  strncpy(destination,source,num);//Compliant
  
  return 0;
}

Correction — Allocate Sufficient Memory for Destination Buffer

This violation is resolved by allocating sufficient memory for the destination buffer. For instance, use the function strlen() to calculate the size of the source buffer and allocate sufficient memory for the destination buffer so that it can accommodate all characters from the source buffer and the null terminator ('\0' ).

#include <string.h>

int main(int argc, char *argv[]) {
	const char *const source = (argc && argv[0]) ? argv[0] : "";
	char* destination = (char *)malloc(strlen(source)+ 1);
	if(destination!=NULL){
		strcpy(destination, source);//Compliant
	}else{
		/*Handle Error*/
	}
	//...
	free(destination);
	return 0;
}

Check Information

Decidability: Undecidable

Version History

Introduced in R2019a


1 Extracts from the standard "ISO/IEC TS 17961 Technical Specification - 2013-11-15" are reproduced with the agreement of AFNOR. Only the original and complete text of the standard, as published by AFNOR Editions - accessible via the website www.boutique.afnor.org - has normative value.