Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Some compiler optimization modes can 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.

Code Block
bgColor#FFCCCC
langc
void getPassword(void) {
  char pwd[64];
  if (GetPassword(pwd, sizeof(pwd))) {
    /* checking of password, secure operations, etc */
  }
  memset(pwd, 0, sizeof(pwd));
}

...

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.

Code Block
bgColor#FFCCCC
langc
void getPassword(void) {
  char pwd[64];
  if (retrievePassword(pwd, sizeof(pwd))) {
    /*checking of password, secure operations, etc */
  }
  memset(pwd, 0, sizeof(pwd));
  *(volatile char*)pwd= *(volatile char*)pwd;
}

...

This noncompliant code example uses the ZeroMemory() function provided by many versions of the Microsoft Visual Studio compiler.

Code Block
bgColor#FFCCCC
langc
void getPassword(void) {
  char pwd[64];
  if (retrievePassword(pwd, sizeof(pwd))) {
    /* checking of password, secure operations, etc */
  }
  ZeroMemory(pwd, sizeof(pwd));
}

...

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.

Code Block
bgColor#ccccff
langc
void getPassword(void) {
  char pwd[64];
  if (retrievePassword(pwd, sizeof(pwd))) {
    /* checking of password, secure operations, etc */
  }
  SecureZeroMemory(pwd, sizeof(pwd));
}

...

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 could be supported on other compilers. Check compiler documentation to ensure its availability and its optimization guarantees.

Code Block
bgColor#ccccff
langc
void getPassword(void) {
  char pwd[64];
  if (retrievePassword(pwd, sizeof(pwd))) {
    /* checking of password, secure operations, etc */
  }
#pragma optimize("", off)
  memset(pwd, 0, sizeof(pwd));
#pragma optimize("", on)
}

...

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 can result in less efficient code. Check compiler documentation and the assembly output from the compiler.

Code Block
bgColor#ccccff
langc
/* memset_s.c */
errno_t memset_s(void *v, rsize_t smax, int c, rsize_t n) {
  if (v == NULL) return EINVAL;
  if (smax > RSIZE_MAX) return EINVAL;
  if (n > smax) return EINVAL;

  volatile unsigned char *p = v;
  while (smax-- && n--) {
    *p++ = c;
  }

  return 0;
}

/* getPassword.c */
extern errno_t memset_s(void *v, rsize_t smax, int c, rsize_t n);

void getPassword(void) {
  char pwd[64];

  if (retrievePassword(pwd, sizeof(pwd))) {
     /*checking of password, secure operations, etc */
  }
  if (memset_s(pwd, sizeof(pwd), 0, sizeof(pwd)) != 0) {
    /* Handle error */
  }
}

...

Unlike memset, any call to the memset_s function shall be evaluated strictly according to the rules of the abstract machine as described in (5.1.2.3). That is, any call to the memset_s function shall assume that the memory indicated by s and n may be accessible in the future and thus must contain the values indicated by c.

Code Block
bgColor#ccccff
langc
void getPassword(void) {
  char pwd[64];

  if (retrievePassword(pwd, sizeof(pwd))) {
     /* checking of password, secure operations, etc */
  }
  memset_s(pwd, 0, sizeof(pwd));
}

...