CERT
Skip to end of metadata
Go to start of metadata

This guideline has not been reviewed recently and may be outdated. Please review it and comment to reflect any newly available information.

The C99 standards states that:

In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

This clause gives compilers the leeway to remove code deemed unused or unneeded when building a program. While this is usually beneficial, sometimes the compiler removes code that it thinks is not needed but that has been added with security in mind. An example of this is overwriting the memory of a buffer that is used to store sensitive data. As a result, care must always be taken when dealing with sensitive data to ensure that operations on it always execute as intended.

Noncompliant Code Example (memset())

Some compiler optimization modes may remove code sections if the optimizer determines that doing so will not alter the behavior of the program. In this noncompliant code example, optimization may remove the call to memset() (which the programmer had hoped would clear sensitive memory) because the variable is not accessed following the write. Check compiler documentation for information about this compiler-specific behavior and which optimization levels can cause this behavior to occur.

For all of the compliant solutions provided for this recommendation, it is strongly recommended that the programmer inspect the generated assembly code in the optimized release build to ensure that memory is actually cleared and none of the function calls are optimized out.

Noncompliant Code Example (Touching Memory)

This noncompliant code example accesses the buffer again after the call to memset(). This prevents some compilers from optimizing out the call to memset() but does not work for all implementations. For example, the MIPSpro compiler and versions 3 and later of GCC cleverly nullify only the first byte and leave the rest intact. Check compiler documentation to guarantee this behavior for a specific platform.

Noncompliant Code Example (Windows)

This compliant solution uses the ZeroMemory() function provided by many versions of the Microsoft Visual Studio compiler.

A call to ZeroMemory() may be optimized out in a similar manner as a call to memset().

Compliant Code Example (Windows)

This compliant solution uses a SecureZeroMemory() function provided by many versions of the Microsoft Visual Studio compiler. The documentation for the SecureZeroMemory() function guarantees that the compiler does not optimize out this call when zeroing memory.

Compliant Solution (Windows)

The #pragma directives in this compliant solution instruct the compiler to avoid optimizing the enclosed code. This #pragma directive is supported on some versions of Microsoft Visual Studio and may be supported on other compilers. Check compiler documentation to ensure its availability and its optimization guarantees.

Compliant Solution

This compliant solution uses the volatile type qualifier to inform the compiler that the memory should be overwritten and that the call to the memset_s() function should not be optimized out. Unfortunately, this compliant solution may not be as efficient as possible due to the nature of the volatile type qualifier preventing the compiler from optimizing the code at all. Typically, some compilers are smart enough to replace calls to memset() with equivalent assembly instructions that are much more efficient than the memset() implementation. Implementing a memset_s() function as shown in the example may prevent the compiler from using the optimal assembly instructions and may result in less efficient code. Check compiler documentation and the assembly output from the compiler.

However, it should be noted that both calling functions and accessing volatile qualified objects can still be optimized out (while maintaining strict conformance to the standard), so the above may still not work.

Risk Assessment

If the compiler optimizes out memory-clearing code, an attacker can gain access to sensitive data.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC06-CPP

medium

probable

medium

P8

L2

Automated Detection

Tool

Version

Checker

Description

CodeSonar4.4

BADFUNC.MEMSET

Use of memset
LDRA tool suite9.5.8

 

35 S, 57 S, 8 D, 65 D, 76 D, 105 D, 1J, 3 J

Partially implemented

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Other Languages

This rule appears in the C Secure Coding Standard as MSC06-C. Beware of compiler optimizations.

Bibliography

[ISO/IEC 9899:1999] Section 6.7.3, "Type qualifiers"
[US-CERT] "MEMSET"
[MITRE 07] CWE ID 14, "Compiler Removal of Code to Clear Buffers"
[MSDN] "SecureZeroMemory"
[MSDN] "Optimize (C/C++)"
[Wheeler 03] Section 11.4, "Specially Protect Secrets (Passwords and Keys) in User Memory"


            

1 Comment

  1. Need a CS that showcases the memset_s() function from C11.

    The current memset_s CS is fine, but should use a different name for its implementation, to distinguish it from the standard memset_s.