Skip to content

Commit 102703a

Browse files
committed
Rule 18.5.2: AvoidProgramTerminatingFunctions.ql
Add a query to identify uses of terminating program functions. [a]
1 parent 1844339 commit 102703a

File tree

6 files changed

+160
-7
lines changed

6 files changed

+160
-7
lines changed

cpp/common/test/includes/standard-library/cstdlib.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
#define _GHLIBCPP_CSTDLIB
33
#include "stdlib.h"
44
namespace std {
5-
[[noreturn]] void _Exit(int status) noexcept;
6-
[[noreturn]] void abort(void) noexcept;
7-
[[noreturn]] void quick_exit(int status) noexcept;
8-
extern "C++" int atexit(void (*f)(void)) noexcept;
9-
extern "C++" int at_quick_exit(void (*f)(void)) noexcept;
5+
using ::_Exit;
6+
using ::abort;
7+
using ::at_quick_exit;
8+
using ::atexit;
109
using ::atof;
1110
using ::atoi;
1211
using ::atol;
1312
using ::atoll;
13+
using ::exit;
14+
using ::free;
15+
using ::malloc;
16+
using ::quick_exit;
1417
using ::rand;
1518
} // namespace std
1619
#endif // _GHLIBCPP_CSTDLIB

cpp/common/test/includes/standard-library/stdlib.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ void free(void *ptr);
88
void *malloc(size_t size);
99
void *realloc(void *ptr, size_t size);
1010

11-
void abort();
11+
[[noreturn]] void _Exit(int status) noexcept;
12+
[[noreturn]] void abort(void) noexcept;
13+
[[noreturn]] void quick_exit(int status) noexcept;
14+
extern "C++" int atexit(void (*f)(void)) noexcept;
15+
extern "C++" int at_quick_exit(void (*f)(void)) noexcept;
1216

1317
void exit(int code);
1418
int system(const char *command);
1519

1620
char *getenv(const char *name);
1721

18-
int setenv (const char *, const char *, int);
22+
int setenv(const char *, const char *, int);
1923

2024
int atoi(const char *str);
2125
long int atol(const char *str);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @id cpp/misra/avoid-program-terminating-functions
3+
* @name RULE-18-5-2: Program-terminating functions should not be used
4+
* @description Using program-terminating functions like abort, exit, _Exit, quick_exit or terminate
5+
* causes the stack to not be unwound and object destructors to not be called,
6+
* potentially leaving the environment in an undesirable state.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-18-5-2
11+
* scope/single-translation-unit
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/advisory
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
19+
predicate isTerminatingFunction(Function f, string functionName) {
20+
functionName = f.getName() and
21+
(
22+
functionName in ["abort", "exit", "_Exit", "quick_exit"] and
23+
(f.hasQualifiedName("", functionName) or f.hasQualifiedName("std", functionName))
24+
or
25+
// std::terminate does not occur in the global namespace.
26+
functionName = "terminate" and f.hasQualifiedName("std", functionName)
27+
)
28+
}
29+
30+
predicate isAssertMacroCall(FunctionCall call) {
31+
exists(MacroInvocation mi |
32+
mi.getMacroName() = "assert" and
33+
mi.getAnExpandedElement() = call
34+
)
35+
}
36+
37+
from Expr e, Function f, string functionName, string action
38+
where
39+
not isExcluded(e, BannedAPIsPackage::avoidProgramTerminatingFunctionsQuery()) and
40+
isTerminatingFunction(f, functionName) and
41+
(
42+
// Direct function calls (excluding assert macro calls)
43+
e instanceof FunctionCall and
44+
f = e.(FunctionCall).getTarget() and
45+
not isAssertMacroCall(e) and
46+
action = "Call to"
47+
or
48+
// Function access
49+
e instanceof FunctionAccess and
50+
f = e.(FunctionAccess).getTarget() and
51+
action = "Address taken for"
52+
)
53+
select e, action + " program-terminating function '" + functionName + "'."
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
| test.cpp:7:3:7:7 | call to abort | Call to program-terminating function 'abort'. |
2+
| test.cpp:8:3:8:6 | call to exit | Call to program-terminating function 'exit'. |
3+
| test.cpp:9:3:9:7 | call to _Exit | Call to program-terminating function '_Exit'. |
4+
| test.cpp:10:3:10:12 | call to quick_exit | Call to program-terminating function 'quick_exit'. |
5+
| test.cpp:15:3:15:12 | call to abort | Call to program-terminating function 'abort'. |
6+
| test.cpp:16:3:16:11 | call to exit | Call to program-terminating function 'exit'. |
7+
| test.cpp:17:3:17:12 | call to _Exit | Call to program-terminating function '_Exit'. |
8+
| test.cpp:18:3:18:17 | call to quick_exit | Call to program-terminating function 'quick_exit'. |
9+
| test.cpp:19:3:19:16 | call to terminate | Call to program-terminating function 'terminate'. |
10+
| test.cpp:24:14:24:18 | abort | Address taken for program-terminating function 'abort'. |
11+
| test.cpp:25:14:25:17 | exit | Address taken for program-terminating function 'exit'. |
12+
| test.cpp:26:14:26:18 | _Exit | Address taken for program-terminating function '_Exit'. |
13+
| test.cpp:27:14:27:23 | quick_exit | Address taken for program-terminating function 'quick_exit'. |
14+
| test.cpp:28:14:28:23 | abort | Address taken for program-terminating function 'abort'. |
15+
| test.cpp:29:14:29:22 | exit | Address taken for program-terminating function 'exit'. |
16+
| test.cpp:30:14:30:23 | _Exit | Address taken for program-terminating function '_Exit'. |
17+
| test.cpp:31:14:31:28 | quick_exit | Address taken for program-terminating function 'quick_exit'. |
18+
| test.cpp:32:14:32:27 | terminate | Address taken for program-terminating function 'terminate'. |
19+
| test.cpp:37:18:37:22 | abort | Address taken for program-terminating function 'abort'. |
20+
| test.cpp:38:21:38:24 | exit | Address taken for program-terminating function 'exit'. |
21+
| test.cpp:39:21:39:25 | _Exit | Address taken for program-terminating function '_Exit'. |
22+
| test.cpp:40:21:40:30 | quick_exit | Address taken for program-terminating function 'quick_exit'. |
23+
| test.cpp:41:18:41:27 | abort | Address taken for program-terminating function 'abort'. |
24+
| test.cpp:42:21:42:29 | exit | Address taken for program-terminating function 'exit'. |
25+
| test.cpp:43:21:43:30 | _Exit | Address taken for program-terminating function '_Exit'. |
26+
| test.cpp:44:21:44:35 | quick_exit | Address taken for program-terminating function 'quick_exit'. |
27+
| test.cpp:45:18:45:31 | terminate | Address taken for program-terminating function 'terminate'. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-5-2/AvoidProgramTerminatingFunctions.ql
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <cassert>
2+
#include <cstdlib>
3+
#include <stdexcept>
4+
5+
void test_direct_calls_to_terminating_functions() {
6+
// Direct calls to program-terminating functions
7+
abort(); // NON_COMPLIANT
8+
exit(0); // NON_COMPLIANT
9+
_Exit(1); // NON_COMPLIANT
10+
quick_exit(2); // NON_COMPLIANT
11+
}
12+
13+
void test_std_namespace_calls() {
14+
// Calls to functions in std namespace
15+
std::abort(); // NON_COMPLIANT
16+
std::exit(0); // NON_COMPLIANT
17+
std::_Exit(1); // NON_COMPLIANT
18+
std::quick_exit(2); // NON_COMPLIANT
19+
std::terminate(); // NON_COMPLIANT
20+
}
21+
22+
void test_taking_addresses_of_terminating_functions() {
23+
// Taking addresses of program-terminating functions
24+
auto l1 = &abort; // NON_COMPLIANT
25+
auto l2 = &exit; // NON_COMPLIANT
26+
auto l3 = &_Exit; // NON_COMPLIANT
27+
auto l4 = &quick_exit; // NON_COMPLIANT
28+
auto l5 = &std::abort; // NON_COMPLIANT
29+
auto l6 = &std::exit; // NON_COMPLIANT
30+
auto l7 = &std::_Exit; // NON_COMPLIANT
31+
auto l8 = &std::quick_exit; // NON_COMPLIANT
32+
auto l9 = &std::terminate; // NON_COMPLIANT
33+
}
34+
35+
void test_function_pointers_to_terminating_functions() {
36+
// Function pointers to program-terminating functions
37+
void (*l1)() = abort; // NON_COMPLIANT
38+
void (*l2)(int) = exit; // NON_COMPLIANT
39+
void (*l3)(int) = _Exit; // NON_COMPLIANT
40+
void (*l4)(int) = quick_exit; // NON_COMPLIANT
41+
void (*l5)() = std::abort; // NON_COMPLIANT
42+
void (*l6)(int) = std::exit; // NON_COMPLIANT
43+
void (*l7)(int) = std::_Exit; // NON_COMPLIANT
44+
void (*l8)(int) = std::quick_exit; // NON_COMPLIANT
45+
void (*l9)() = std::terminate; // NON_COMPLIANT
46+
}
47+
48+
void test_assert_macro_exception() {
49+
// The call to abort via assert macro is compliant by exception
50+
assert(true); // COMPLIANT
51+
assert(1 == 1); // COMPLIANT
52+
}
53+
54+
void f1() {
55+
// Valid alternative: normal function return
56+
return; // COMPLIANT
57+
}
58+
59+
void test_compliant_alternatives() {
60+
// Using normal control flow instead of terminating functions
61+
f1(); // COMPLIANT
62+
63+
// Using exceptions for error handling
64+
throw std::runtime_error("error"); // COMPLIANT
65+
}

0 commit comments

Comments
 (0)