CWE Rule 676
Description
Rule Description
The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely.
Polyspace Implementation
The rule checker checks for these issues:
Buffer overflow from incorrect string format specifier
Data race through standard library function call
Destination buffer overflow in string manipulation
Unsafe call to a system function
Unsafe conversion from string to numerical value
Use of dangerous standard function
Use of rand() for generating pseudorandom number
Examples
Buffer overflow from incorrect string format specifier
This issue occurs when the format specifier argument
for functions such as sscanf
leads to an overflow
or underflow in the memory buffer argument.
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.
Use a format specifier that is compatible with the memory buffer size.
#include <stdio.h> void func (char *str[]) { char buf[32]; sscanf(str[1], "%33c", buf); //Noncompliant }
In this example, buf
can contain 32 char
elements.
Therefore, the format specifier %33c
causes a buffer
overflow.
One possible correction is to read a smaller number of elements into the buffer.
#include <stdio.h> void func (char *str[]) { char buf[32]; sscanf(str[1], "%32c", buf); }
Data race through standard library function call
This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).
This issue occurs when:
Multiple tasks call the same standard library function.
For instance, multiple tasks call the
strerror
function.The calls are not protected using a common protection.
For instance, the calls are not protected by the same critical section.
Functions flagged by this defect are not guaranteed to be reentrant. A function is reentrant if it can be interrupted and safely called again before its previous invocation completes execution. If a function is not reentrant, multiple tasks calling the function without protection can cause concurrency issues. For the list of functions that are flagged, see CON33-C: Avoid race conditions when using library functions.
To find this defect, you must specify the multitasking options before analysis. To specify these options, on the Configuration pane, select Multitasking. For more information, see Configuring Polyspace Multitasking Analysis Manually.
The functions flagged by this defect are nonreentrant because their implementations can use global or static variables. When multiple tasks call the function without protection, the function call from one task can interfere with the call from another task. The two invocations of the function can concurrently access the global or static variables and cause unpredictable results.
The calls can also cause more serious security vulnerabilities, such as abnormal termination, denial-of-service attack, and data integrity violations.
To fix this defect, do one of the following:
Use a reentrant version of the standard library function if it exists.
For instance, instead of
strerror()
, usestrerror_r()
orstrerror_s()
. For alternatives to functions flagged by this defect, see the documentation for CON33-C.Protect the function calls using common critical sections or temporal exclusion.
See
Critical section details (-critical-section-begin -critical-section-end)
andTemporally exclusive tasks (-temporal-exclusions-file)
.To identify existing protections that you can reuse, see the table and graphs associated with the result. The table shows each pair of conflicting calls. The Access Protections column shows existing protections on the calls. To see the function call sequence leading to the conflicts, click the icon. For an example, see below.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char *errmsg = strerror(errno); //Noncompliant printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); func(fptr1); } void task2(void) { FILE* fptr2 = getFilePointer(); func(fptr2); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
In this example, to emulate multitasking behavior, specify the following options:
Option | Specification | |
---|---|---|
Configure multitasking manually | ||
Tasks (-entry-points) |
| |
Critical section details (-critical-section-begin -critical-section-end) | Starting routine | Ending routine |
begin_critical_section | end_critical_section |
On the command-line, you can use the following:
polyspace-bug-finder -entry-points task1,task2,task3 -critical-section-begin begin_critical_section:cs1 -critical-section-end end_critical_section:cs1
In this example, the tasks, task1
, task2
and task3
,
call the function func
. func
calls
the nonreentrant standard library function, strerror
.
Though task3
calls func
inside
a critical section, other tasks do not use the same critical section.
Operations in the critical section of task3
are
not mutually exclusive with operations in other tasks.
These three tasks are calling a nonreentrant standard library function without common protection. In your result details, you see each pair of conflicting function calls.
If you click the icon, you see the function call sequence starting from the
entry point to the standard library function call. You also see that the call starting from
task3
is in a critical section. The Access Protections
entry shows the lock and unlock function that begin and end the critical section. In this
example, you see the functions begin_critical_section
and
end_critical_section
.
One possible correction is to use a reentrant version of the
standard library function strerror
. You can use
the POSIX® version strerror_r
which has the
same functionality but also guarantees thread-safety.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); enum { BUFFERSIZE = 64 }; void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char errmsg[BUFFERSIZE]; if (strerror_r(errno, errmsg, BUFFERSIZE) != 0) { /* Handle error */ } printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); func(fptr1); } void task2(void) { FILE* fptr2 = getFilePointer(); func(fptr2); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
One possible correction is to place the call to strerror
in
critical section. You can implement the critical section in multiple
ways.
For instance, you can place the call to the intermediate function func
in
the same critical section in the three tasks. When task1
enters
its critical section, the other tasks cannot enter their critical
sections until task1
leaves its critical section.
The calls to func
and therefore the calls to strerror
from
the three tasks cannot interfere with each other.
To implement the critical section, in each of the three tasks,
call func
between calls to begin_critical_section
and end_critical_section
.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char *errmsg = strerror(errno); printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); begin_critical_section(); func(fptr1); end_critical_section(); } void task2(void) { FILE* fptr2 = getFilePointer(); begin_critical_section(); func(fptr2); end_critical_section(); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
Another possible correction is to make the tasks, task1
, task2
and task3
,
temporally exclusive. Temporally exclusive tasks cannot execute concurrently.
On the Configuration pane, specify the following additional options:
Option | Value |
---|---|
Temporally exclusive tasks (-temporal-exclusions-file) |
|
On the command-line, you can use the following:
polyspace-bug-finder -temporal-exclusions-file "C:\exclusions_file.txt"
C:\exclusions_file.txt
has the following line:
task1 task2 task3
Destination buffer overflow in string manipulation
This issue 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
.
Buffer overflow can cause unexpected behavior such as memory corruption or stopping your system. Buffer overflow also introduces the risk of code injection.
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, usesnprintf
,_snprintf
orsprintf_s
instead to enforce length control. Alternatively, useasprintf
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, usevsnprintf
orvsprintf_s
instead to enforce length control.If you use
wcscpy
to copy a wide string, usewcsncpy
,wcslcpy
, orwcscpy_s
instead to enforce length control.
Another possible solution is to increase the buffer size.
#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); //Noncompliant }
In this example, buffer
can contain 20 char
elements
but fmt_string
has a greater size.
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); }
Unsafe call to a system function
This issue occurs when you use a function that invokes an implementation-defined command processor. These functions include:
The C standard
system()
function.The POSIX
popen()
function.The Windows®
_popen()
and_wpopen()
functions.
If the argument of a function that invokes a command processor is not sanitized, it can cause exploitable vulnerabilities. An attacker can execute arbitrary commands or read and modify data anywhere on the system.
Do not use a system
-family function to invoke a command processor.
Instead, use safer functions such as POSIX execve()
and WinAPI
CreateProcess()
.
# include <string.h> # include <stdlib.h> # include <stdio.h> # include <unistd.h> enum { SIZE512=512, SIZE3=3}; void func(char *arg) { char buf[SIZE512]; int retval=snprintf(buf, sizeof(buf), "/usr/bin/any_cmd %s", arg); if (retval<=0 || retval>SIZE512){ /* Handle error */ abort(); } /* Use of system() to pass any_cmd with unsanitized argument to command processor */ if (system(buf) == -1) { //Noncompliant /* Handle error */ } }
In this example, system()
passes its argument to the host environment
for the command processor to execute. This code is vulnerable to an attack by
command-injection.
execve()
In the following code, the argument of any_cmd
is sanitized, and
then passed to execve()
for execution. exec
-family
functions are not vulnerable to command-injection attacks.
# include <string.h> # include <stdlib.h> # include <stdio.h> # include <unistd.h> enum { SIZE512=512, SIZE3=3}; void func(char *arg) { char *const args[SIZE3] = {"any_cmd", arg, NULL}; char *const env[] = {NULL}; /* Sanitize argument */ /* Use execve() to execute any_cmd. */ if (execve("/usr/bin/time", args, env) == -1) { /* Handle error */ } }
Unsafe conversion from string to numerical value
This issue occurs when you perform conversions from strings to integer or floating-point values and your conversion method does not include robust error handling.
Converting a string to numerical value can cause data loss or misinterpretation. Without validation of the conversion or error handling, your program continues with invalid values.
Add additional checks to validate the numerical value.
Use a more robust string-to-numeric conversion function such as
strtol
,strtoll
,strtoul
, orstrtoull
.
#include <stdio.h> #include <stdlib.h> #include <string.h> static int demo_check_string_not_empty(char *s) { if (s != NULL) return strlen(s) > 0; /* check string null-terminated and not empty */ else return 0; } int unsafestrtonumeric(char* argv1) { int s = 0; if (demo_check_string_not_empty(argv1)) { s = atoi(argv1); //Noncompliant } return s; }
In this example, argv1
is converted to an
integer with atoi
. atoi
does
not provide errors for an invalid integer string. The conversion can
fail unexpectedly.
strtol
insteadOne possible correction is to use strtol
to
validate the input string and the converted integer.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <errno.h> static int demo_check_string_not_empty(char *s) { if (s != NULL) return strlen(s) > 0; /* check string null-terminated and not empty */ else return 0; } int unsafestrtonumeric(char *argv1) { char *c_str = argv1; char *end; long sl; if (demo_check_string_not_empty(c_str)) { errno = 0; /* set errno for error check */ sl = strtol(c_str, &end, 10); if (end == c_str) { (void)fprintf(stderr, "%s: not a decimal number\n", c_str); } else if ('\0' != *end) { (void)fprintf(stderr, "%s: extra characters: %s\n", c_str, end); } else if ((LONG_MIN == sl || LONG_MAX == sl) && ERANGE == errno) { (void)fprintf(stderr, "%s out of range of type long\n", c_str); } else if (sl > INT_MAX) { (void)fprintf(stderr, "%ld greater than INT_MAX\n", sl); } else if (sl < INT_MIN) { (void)fprintf(stderr, "%ld less than INT_MIN\n", sl); } else { return (int)sl; } } return 0; }
Use of dangerous standard function
This issue occurs when your code uses standard functions that write data to a buffer in a way that can result in buffer overflows.
The following table lists dangerous standard functions, the risks of using each function, and what function to use instead. The checker flags:
Any use of an inherently dangerous function.
An use of a possibly dangerous function only if the size of the buffer to which data is written can be determined at compile time. The checker does not flag an use of such a function with a dynamically allocated buffer.
Dangerous Function | Risk Level | Safer Function |
---|---|---|
gets | Inherently dangerous — You cannot control the length of input from the console. | fgets |
std::cin::operator>> and
std::wcin::operator>> | Inherently dangerous — You cannot control the length of input from the console. | Preface calls to To avoid potential
buffer overflow and truncated input, use
|
strcpy | Possibly dangerous — If the size of the destination buffer is too small to accommodate the source buffer and a null terminator, a buffer overflow might occur. | Use the function strlen() to determine the size of the source buffer, and allocate sufficient memory so that the destination buffer can accommodate the source buffer and a null terminator. Instead of strcpy , use the function strncpy . |
stpcpy | Possibly dangerous — If the source length is greater than the destination, buffer overflow can occur. | stpncpy |
lstrcpy or StrCpy | Possibly dangerous — If the source length is greater than the destination, buffer overflow can occur. | StringCbCopy , StringCchCopy ,
strncpy , strcpy_s , or
strlcpy |
strcat | Possibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur. | strncat , strlcat , or
strcat_s |
lstrcat or StrCat | Possibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur. | StringCbCat , StringCchCat ,
strncay , strcat_s , or
strlcat |
wcpcpy | Possibly dangerous — If the source length is greater than the destination, buffer overflow can occur. | wcpncpy |
wcscat | Possibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur. | wcsncat , wcslcat , or
wcncat_s |
wcscpy | Possibly dangerous — If the source length is greater than the destination, buffer overflow can occur. | wcsncpy |
sprintf | Possibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur. | snprintf |
vsprintf | Possibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur. | vsnprintf |
These functions can cause buffer overflow, which attackers can use to infiltrate your program.
The fix depends on the root cause of the defect. See fixes in the table above and code examples with fixes below.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See:
Address Results in Polyspace User Interface Through Bug Fixes or Justifications if you review results in the Polyspace user interface.
Address Results in Polyspace Access Through Bug Fixes or Justifications (Polyspace Access) if you review results in a web browser.
Annotate Code and Hide Known or Acceptable Results if you review results in an IDE.
#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) //Noncompliant { 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.
snprintf
with Buffer
SizeOne 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; }
Use of rand()
for Generating Pseudorandom Number
This issue occurs when you use the function rand
for
generating pseudorandom numbers.
The function rand
is cryptographically weak. That is, the
numbers generated by rand
can be predictable. Do not use
pseudorandom numbers generated from rand
for security
purposes. When a predictable random value controls the execution flow, your
program is vulnerable to attacks.
Use more cryptographically sound pseudorandom number generators (PRNG), such
as CryptGenRandom
(Windows),
OpenSSL/RAND_bytes
(Linux/UNIX), or
random
(POSIX).
#include <stdio.h> #include <stdlib.h> volatile int rd = 1; int main(int argc, char *argv[]) { int j, r, nloops; struct random_data buf; int i = 0; nloops = rand(); //Noncompliant for (j = 0; j < nloops; j++) { i = rand(); //Noncompliant printf("random_r: %ld\n", (long)i); } return 0; }
This example uses rand
to generate random numbers
nloops
and i
. The predictability of
these variables makes these function vulnerable to attacks.
One possible correction is to replace the vulnerable PRNG with a stronger
random number generator. For instance, this code uses the PRNG
random()
from POSIX library. random
is
a much stronger PRNG because it can be seeded by a different number every time
it is called.
#include <stdio.h> #include <stdlib.h> #include <time.h> #define TIME_UTC 1 volatile int rd = 1; int randomWrapper(){ struct timespec ts; if (timespec_get(&ts, TIME_UTC) == 0) { /* Handle error */ } srandom(ts.tv_nsec ^ ts.tv_sec); /* Seed the PRNG */ return random(); } int main(int argc, char *argv[]) { int j, r, nloops; struct random_data buf; int i = 0; nloops = randomWrapper(); for (j = 0; j < nloops; j++) { i = randomWrapper(); printf("random_r: %ld\n", (long)i); } return 0; }
Check Information
Category: API / Function Errors |
Version History
Introduced in R2023a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)