Skip to content

Commit 8ffd7c1

Browse files
authored
Merge pull request #2222 from geoffw0/libraryperf
CPP: Improvements for ConditionallyInitializedVariable.ql
2 parents 5d1d7ea + 7456a92 commit 8ffd7c1

File tree

8 files changed

+202
-40
lines changed

8 files changed

+202
-40
lines changed

cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ int notify(int deviceNumber) {
1919
DeviceConfig config;
2020
initDeviceConfig(&config, deviceNumber);
2121
// BAD: Using config without checking the status code that is returned
22-
if (config->isEnabled) {
23-
notifyChannel(config->channel);
22+
if (config.isEnabled) {
23+
notifyChannel(config.channel);
2424
}
25-
}
25+
}

cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ void notify(int deviceNumber) {
2020
int statusCode = initDeviceConfig(&config, deviceNumber);
2121
if (statusCode == 0) {
2222
// GOOD: Status code returned by initialization function is checked, so this is safe
23-
if (config->isEnabled) {
24-
notifyChannel(config->channel);
23+
if (config.isEnabled) {
24+
notifyChannel(config.channel);
2525
}
2626
}
27-
}
27+
}

cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -620,32 +620,47 @@ Function getAPossibleDefinition(Function undefinedFunction) {
620620
}
621621

622622
/**
623-
* Gets a possible target for the Call, using the name and parameter matching if we did not associate
623+
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
624+
*
625+
* If there is at least one defined target after performing some simple virtual dispatch
626+
* resolution, then the result is all the defined targets.
627+
*/
628+
private Function getTarget1(Call c) {
629+
result = VirtualDispatch::getAViableTarget(c) and
630+
result.isDefined()
631+
}
632+
633+
/**
634+
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
635+
*
636+
* If we can use the heuristic matching of functions to find definitions for some of the viable
637+
* targets, return those.
638+
*/
639+
private Function getTarget2(Call c) {
640+
not exists(getTarget1(c)) and
641+
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
642+
}
643+
644+
/**
645+
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
646+
*
647+
* Otherwise, the result is the undefined `Function` instances.
648+
*/
649+
private Function getTarget3(Call c) {
650+
not exists(getTarget1(c)) and
651+
not exists(getTarget2(c)) and
652+
result = VirtualDispatch::getAViableTarget(c)
653+
}
654+
655+
/**
656+
* Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
624657
* this call with a specific definition at link or compile time, and performing simple virtual
625658
* dispatch resolution.
626659
*/
627660
Function getTarget(Call c) {
628-
if VirtualDispatch::getAViableTarget(c).isDefined()
629-
then
630-
/*
631-
* If there is at least one defined target after performing some simple virtual dispatch
632-
* resolution, then the result is all the defined targets.
633-
*/
634-
635-
result = VirtualDispatch::getAViableTarget(c) and
636-
result.isDefined()
637-
else
638-
if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)))
639-
then
640-
/*
641-
* If we can use the heuristic matching of functions to find definitions for some of the viable
642-
* targets, return those.
643-
*/
644-
645-
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
646-
else
647-
// Otherwise, the result is the undefined `Function` instances
648-
result = VirtualDispatch::getAViableTarget(c)
661+
result = getTarget1(c) or
662+
result = getTarget2(c) or
663+
result = getTarget3(c)
649664
}
650665

651666
/**

cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ module VirtualDispatch {
2828
not result.hasName("IUnknown")
2929
}
3030

31+
/**
32+
* Helper predicate for `getAViableTarget`, which computes the viable targets for
33+
* virtual calls based on the qualifier type.
34+
*/
35+
private Function getAViableVirtualCallTarget(Class qualifierType, MemberFunction staticTarget) {
36+
exists(Class qualifierSubType |
37+
result = getAPossibleImplementation(staticTarget) and
38+
qualifierType = qualifierSubType.getABaseClass*() and
39+
mayInherit(qualifierSubType, result) and
40+
not cannotInherit(qualifierSubType, result)
41+
)
42+
}
43+
3144
/**
3245
* Gets a viable target for the given function call.
3346
*
@@ -42,18 +55,9 @@ module VirtualDispatch {
4255
* If `c` is not a virtual call, the result will be `c.getTarget()`.
4356
*/
4457
Function getAViableTarget(Call c) {
45-
exists(Function staticTarget | staticTarget = c.getTarget() |
46-
if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction
47-
then
48-
exists(Class qualifierType, Class qualifierSubType |
49-
result = getAPossibleImplementation(staticTarget) and
50-
qualifierType = getCallQualifierType(c) and
51-
qualifierType = qualifierSubType.getABaseClass*() and
52-
mayInherit(qualifierSubType, result) and
53-
not cannotInherit(qualifierSubType, result)
54-
)
55-
else result = staticTarget
56-
)
58+
if c.(FunctionCall).isVirtual() and c.getTarget() instanceof MemberFunction
59+
then result = getAViableVirtualCallTarget(getCallQualifierType(c), c.getTarget())
60+
else result = c.getTarget()
5761
}
5862

5963
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| examples.cpp:38:3:38:18 | call to initDeviceConfig | The status of this call to $@ is not checked, potentially leaving $@ uninitialized. | examples.cpp:13:5:13:20 | initDeviceConfig | initDeviceConfig | examples.cpp:37:16:37:21 | config | config |
2+
| test.cpp:22:2:22:17 | call to maybeInitialize1 | The status of this call to $@ is not checked, potentially leaving $@ uninitialized. | test.cpp:4:5:4:20 | maybeInitialize1 | maybeInitialize1 | test.cpp:19:6:19:6 | a | a |
3+
| test.cpp:68:2:68:17 | call to maybeInitialize2 | The status of this call to $@ is not checked, potentially leaving $@ uninitialized. | test.cpp:51:6:51:21 | maybeInitialize2 | maybeInitialize2 | test.cpp:66:6:66:6 | a | a |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// based on the qhelp
2+
3+
int getMaxDevices();
4+
bool fetchIsDeviceEnabled(int deviceNumber);
5+
int fetchDeviceChannel(int deviceNumber);
6+
void notifyChannel(int channel);
7+
8+
struct DeviceConfig {
9+
bool isEnabled;
10+
int channel;
11+
};
12+
13+
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
14+
if (deviceNumber >= getMaxDevices()) {
15+
// No device with that number, return -1 to indicate failure
16+
return -1;
17+
}
18+
// Device with that number, fetch parameters and initialize struct
19+
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
20+
ref->channel = fetchDeviceChannel(deviceNumber);
21+
// Return 0 to indicate success
22+
return 0;
23+
}
24+
25+
void notifyGood(int deviceNumber) {
26+
DeviceConfig config;
27+
int statusCode = initDeviceConfig(&config, deviceNumber);
28+
if (statusCode == 0) {
29+
// GOOD: Status code returned by initialization function is checked, so this is safe
30+
if (config.isEnabled) {
31+
notifyChannel(config.channel);
32+
}
33+
}
34+
}
35+
36+
int notifyBad(int deviceNumber) {
37+
DeviceConfig config;
38+
initDeviceConfig(&config, deviceNumber);
39+
// BAD: Using config without checking the status code that is returned
40+
if (config.isEnabled) {
41+
notifyChannel(config.channel);
42+
}
43+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
void use(int i);
3+
4+
int maybeInitialize1(int *v)
5+
{
6+
static int resources = 100;
7+
8+
if (resources == 0)
9+
{
10+
return 0; // FAIL
11+
}
12+
13+
*v = resources--;
14+
return 1; // SUCCESS
15+
}
16+
17+
void test1()
18+
{
19+
int a, b, c, d, e, f;
20+
int result1, result2;
21+
22+
maybeInitialize1(&a); // BAD (initialization not checked)
23+
use(a);
24+
25+
if (maybeInitialize1(&b) == 1) // GOOD
26+
{
27+
use(b);
28+
}
29+
30+
if (maybeInitialize1(&c) == 0) // BAD (initialization check is wrong) [NOT DETECTED]
31+
{
32+
use(c);
33+
}
34+
35+
result1 = maybeInitialize1(&d); // BAD (initialization stored but not checked) [NOT DETECTED]
36+
use(d);
37+
38+
result2 = maybeInitialize1(&e); // GOOD
39+
if (result2 == 1)
40+
{
41+
use(e);
42+
}
43+
44+
if (maybeInitialize1(&f) == 0) // GOOD
45+
{
46+
return;
47+
}
48+
use(f);
49+
}
50+
51+
bool maybeInitialize2(int *v)
52+
{
53+
static int resources = 100;
54+
55+
if (resources > 0)
56+
{
57+
*v = resources--;
58+
return true; // SUCCESS
59+
}
60+
61+
return false; // FAIL
62+
}
63+
64+
void test2()
65+
{
66+
int a, b;
67+
68+
maybeInitialize2(&a); // BAD (initialization not checked)
69+
use(a);
70+
71+
if (maybeInitialize2(&b)) // GOOD
72+
{
73+
use(b);
74+
}
75+
}
76+
77+
int alwaysInitialize(int *v)
78+
{
79+
static int resources = 0;
80+
81+
*v = resources++;
82+
return 1; // SUCCESS
83+
}
84+
85+
void test3()
86+
{
87+
int a, b;
88+
89+
alwaysInitialize(&a); // GOOD (initialization never fails)
90+
use(a);
91+
92+
if (alwaysInitialize(&b) == 1) // GOOD
93+
{
94+
use(b);
95+
}
96+
}

0 commit comments

Comments
 (0)