CWE-121

Stack-based Buffer Overflow. [Improper-Control-Of-A-Resource-Through-Its-Lifetime]

Required inputs: IR, StaticSemanticAnalysis

A stack-based buffer overflow condition is a condition where the buffer being overwritten is allocated on the stack (i.e., is a local variable or, rarely, a parameter to a function).
Background Details Demonstrative Examples
There are generally several security-critical data on an execution stack that can lead to arbitrary code execution. The most prominent is the stored return address, the memory address at which execution should continue once the current function is finished executing. The attacker can overwrite this value with some memory address to which the attacker also has write access, into which they place arbitrary code to be run with the full privileges of the vulnerable program. Alternately, the attacker can supply the address of an important call, for instance the POSIX system() call, leaving arguments to the call on the stack. This is often called a return into libc exploit, since the attacker generally forces the program to jump at return time into an interesting routine in the C standard library (libc). Other important data commonly on the stack include the stack pointer and frame pointer, two values that indicate offsets for computing memory addresses. Modifying those values can often be leveraged into a "write-what-where" condition.
Background Details Demonstrative Examples
Example 1

While buffer overflow examples can be rather complex, it is possible to have very simple, yet still exploitable, stack-based buffer overflows:

Example Language:C
    #define BUFSIZE 256
    int main(int argc, char **argv) {
        char buf[BUFSIZE];
        strcpy(buf, argv[1]);
    }

The buffer size is fixed, but there is no guarantee the string in argv[1] will not exceed this size and cause an overflow.

Example 2

This example takes an IP address from a user, verifies that it is well formed and then looks up the hostname and copies it into a buffer.

Example Language:C
    void host_lookup(char *user_supplied_addr){
        struct hostent *hp;
        in_addr_t *addr;
        char hostname[64];
        in_addr_t inet_addr(const char *cp);

        /*routine that ensures user_supplied_addr is in the right format for conversion */

        validate_addr_form(user_supplied_addr);
        addr = inet_addr(user_supplied_addr);
        hp = gethostbyaddr( addr, sizeof(struct in_addr), AF_INET);
        strcpy(hostname, hp->h_name);
    }

This function allocates a buffer of 64 bytes to store the hostname, however there is no guarantee that the hostname will not be larger than 64 bytes. If an attacker specifies an address which resolves to a very large hostname, then the function may overwrite sensitive data or even relinquish control flow to the attacker.

Note that this example also contains an unchecked return value (CWE-252) that can lead to a NULL pointer dereference (CWE-476).

Excerpts from CWE [https://cwe.mitre.org], Copyright (C) 2006-2026, the MITRE Corporation. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

arithmetic_out_of_bounds

Pointer arithmetic on {node0} might create pointer outside array bounds of {name0}

None

False

out_of_bounds

Access into array is out of bounds

None

False

possible_indirect_out_of_bounds

Pointer-indirect access through {node0} might be out of bounds accessing {name0}

None

False

possible_out_of_bounds

Access into array might be out of bounds

None

False

possible_write_beyond_argument

Call to {} might result in a write access beyond the bounds of argument {}, since argument {} might be too large.

None

False

undereferenced_arithmetic_out_of_bounds

Pointer arithmetic on {node0} might create pointer one past the end of {name0} (but not dereferenced)

None

False

undereferenced_out_of_bounds

Access is one past the end of the array (but not dereferenced)

None

False

undereferenced_possible_indirect_out_of_bounds

Pointer-indirect access through {node0} might be one past the end accessing {name0} (but not dereferenced)

None

False

undereferenced_possible_out_of_bounds

Access might be one past the end of the array (but not dereferenced)

None

False

Options

abstract_interpretation_out_of_bounds

abstract_interpretation_out_of_bounds : bool = False

Use additional "symbolic expression analysis" as postprocessing step. This can remove false positives, but might require more time. Option is automatically active if option StaticSemanticAnalysis/performance.general.enhanced_analysis is active.
 

concat_operations

concat_operations

Type: dict[bauhaus.analysis.config.QualifiedName, typing.Tuple[int, int]]

Default:

{
   'strcat': (0, 1)
}
Names of buffer-concatenating functions being relevant as call targets for this check, with the position of the argument pointing to the destination buffer, and the position of the argument that references the buffer that should be appended at the end of the destination buffer.
 

copy_operations

copy_operations

Type: dict[bauhaus.analysis.config.QualifiedName, typing.Tuple[int, int]]

Default:

{
   'strcpy': (0, 1)
}
Names of buffer copy functions being relevant as call targets for this check, with the position of the destination argument and the source argument of the buffer copy operation.
 

delimiter_of_arguments

delimiter_of_arguments

Type: dict[bauhaus.analysis.config.QualifiedName, set[int]]

Default:

{
   'strcat': {0, 1},
   'strchr': {0},
   'strcmp': {0, 1},
   'strcoll': {0, 1},
   'strcpy': {1},
   'strcspn': {0, 1},
   'strlen': {0},
   'strpbrk': {0, 1},
   'strrchr': {0},
   'strspn': {0, 1},
   'strstr': {0, 1},
   'strtok': {0, 1}
}
Names of functions being relevant as call targets for this check, with the position of parameters whose referenced buffers should be checked for being properly terminated by a null terminator.
 

exclude_very_high_indices

exclude_very_high_indices : bool = True

Enables heuristic to detect false positives: When index used for array access is very high in comparison to the array's size, assume false positive.
 

exclude_warnings_for_unknown_arguments

exclude_warnings_for_unknown_arguments : bool = False

Exclude warnings for cases where nothing at all is known about the arguments of an operation, caused e.g. by using return values of external routines.
 

ignore_calls_in_functions

ignore_calls_in_functions : set[bauhaus.analysis.config.QualifiedName] = set()

Qualified names of function definitions in which calls to relevant functions are ignored for this check.
 

report_unbounded_arrays

report_unbounded_arrays : bool = False

If true, accesses into arrays with unknown bound are reported as being potentially outside the allowed range. This affects arrays like extern char buf[];.
 

report_undereferenced_one_past_the_end

report_undereferenced_one_past_the_end : bool = False

If true, report accesses one past the end of an array even if there is no dereference of the resulting pointer.
 

report_unknown_index

report_unknown_index : bool = False

If false, do not report possible out-of-bound findings for which the analysis was not able to infer any restricting information about the array index (this can lead to excluding both false positives and true findings).