CertC++-MSC37

Ensure that control never reaches the end of a non-void function

Required inputs: IR

If control reaches the closing curly brace ( }) of a non- void function without evaluating a return statement, using the return value of the function call is undefined behavior.(See undefined behavior 88.)

Noncompliant Code Example

In this noncompliant code example, control reaches the end of the checkpass() function when the two strings passed to strcmp() are not equal, resulting in undefined behavior. Many compilers will generate code for the checkpass() function, returning various values along the execution path where no return statement is defined.

#include <string.h>
#include <stdio.h>
 
int checkpass(const char *password) {
  if (strcmp(password, "pass") == 0) {
    return 1;
  }
}

void func(const char *userinput) {
  if (checkpass(userinput)) {
    printf("Success\n");
  }
}

This error is frequently diagnosed by compilers. (See MSC00-C. Compile cleanly at high warning levels.)

Compliant Solution

This compliant solution ensures that the checkpass() function always returns a value:

#include <string.h>
#include <stdio.h>
 
int checkpass(const char *password) {
  if (strcmp(password, "pass") == 0) {
    return 1;
  }
  return 0;
}

void func(const char *userinput) {
  if (checkpass(userinput)) {
    printf("Success!\n");
  }
}
Noncompliant Code Example

In this noncompliant code example, control reaches the end of the getlen() function when input does not contain the integer delim. Because the potentially undefined return value of getlen() is later used as an index into an array, a buffer overflow may occur.

#include <stddef.h>
 
size_t getlen(const int *input, size_t maxlen, int delim) {
  for (size_t i = 0; i < maxlen; ++i) {
    if (input[i] == delim) {
      return i;
    }
  }
}
 
void func(int userdata) {
  size_t i;
  int data[] = { 1, 1, 1 };
  i = getlen(data, sizeof(data), 0);
  data[i] = userdata;
}
Implementation Details (GCC)

Violating this rule can have unexpected consequences, as in the following example:

#include <stdio.h>

size_t getlen(const int *input, size_t maxlen, int delim) {
  for (size_t i = 0; i < maxlen; ++i) {
    if (input[i] == delim) {
      return i;
    }
  }
}

int main(int argc, char **argv) {
  size_t i;
  int data[] = { 1, 1, 1 };

  i = getlen(data, sizeof(data), 0);
  printf("Returned: %zu\n", i);
  data[i] = 0;

  return 0;
}

When this program is compiled with -Wall on most versions of the GCC compiler, the following warning is generated:

example.c: In function 'getlen':
example.c:12: warning: control reaches end of non-void function

None of the inputs to the function equal the delimiter, so when run with GCC 5.3 on Linux, control reaches the end of the getlen() function, which is undefined behavior and in this test returns 3, causing an out-of-bounds write to the data array.

Compliant Solution

This compliant solution changes the interface of getlen() to store the result in a user-provided pointer and returns a status indicator to report success or failure. The best method for handling this type of error is specific to the application and the type of error. (See ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy for more on error handling.)

#include <stddef.h>
 
int getlen(const int *input, size_t maxlen, int delim,
           size_t *result) {
  if (result == NULL) {
    return -1;
  }
  for (size_t i = 0; i < maxlen; ++i) {
    if (input[i] == delim) {
      *result = i;
      return 0;
    }
  }
  return -1;
}

void func(int userdata) {
  size_t i;
  int data[] = {1, 1, 1};
  if (getlen(data, sizeof(data), 0, &i) != 0) {
    /* Handle error */
  } else {
    data[i] = userdata;
  }
}
Exceptions

MSC37-C-EX1: According to the C Standard, 5.1.2.2.3, paragraph 1 [ ISO/IEC 9899:2011], "Reaching the  } that terminates the main function returns a value of 0." As a result, it is permissible for control to reach the end of the main() function without executing a return statement.

MSC37-C-EX2: It is permissible for a control path to not return a value if that code path is never taken and a function marked  _Noreturn is called as part of that code path. For example:

#include <stdio.h>
#include <stdlib.h>

_Noreturn void unreachable(const char *msg) {
  printf("Unreachable code reached: %s\n", msg);
  exit(1);
}

enum E {
  One,
  Two,
  Three
};

int f(enum E e) {
  switch (e) {
  case One: return 1;
  case Two: return 2;
  case Three: return 3;
  }
  unreachable("Can never get here");
}
Risk Assessment

Using the return value from a non- void function where control reaches the end of the function without evaluating a return statement can lead to buffer overflow vulnerabilities as well as other unexpected program behaviors.

Rule Severity Likelihood Remediation Cost Priority Level
MSC37-C High Unlikely Low P9 L2
Related Guidelines
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard MSC01-C. Strive for logical completeness Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-758 2017-07-07: CERT: Rule subset of CWE
Bibliography
[ ISO/IEC 9899:2011] 5.1.2.2.3, "Program Termination"
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/miscellaneous-msc/msc37-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

lambda_return_missing_value

Return without value in non-void lambda expression

None

False

missing_return

Ensure that control never reaches the end of a non-void function.

None

False

missing_return_in_lambda

Non-void lambda expression needs return with value at end.

None

False

return_missing_value

Return without value in non-void function

None

False

Options

exclude_missing_return_for_main

exclude_missing_return_for_main : bool = False

If True, no message for a missing return in main() will be reported.