Skip to content

Commit b343c9d

Browse files
authored
Bring several memory and access safety queries in from internal repo. (#208)
* Bring several memory queries+libraries into public repo. * Add qhelp and md files for the new memory queries. * Adjust IDs for new memory queries * Move the MS libraries to a public folder. * Update QLPack version and changelog. * Standardize copyright headers and emails. * Fix filenames for ConditionallyUninitializedVariable
1 parent aeb6376 commit b343c9d

41 files changed

Lines changed: 4453 additions & 92 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
# Change Log
33
All notable changes to this project will be documented in this file.
44

5+
## [1.9.0] - 2026-02-27
6+
7+
### Added
8+
- Added five new queries in the Microsoft subfolder. These queries are now part of our recommended and must-run sets.
9+
- ConditionallyUninitializedVariableAutomation.ql: Flags calls to initialization functions whose return status is not checked, potentially leaving a local variable uninitialized.
10+
- UnprobedDereference.ql: Detects dereferences of user-provided pointers that haven't been probed first, which could cause access violations.
11+
- UserModeMemoryOutsideTry.ql: Finds reads of user-mode memory that occur outside a try/catch block, where unexpected exceptions from changed memory protections could crash the kernel.
12+
- UserModeMemoryReadMultipleTimes.ql: identifies double-fetch vulnerabilities where user-mode memory is read more than once without being copied to kernel memory first.
13+
- UnguardedNullReturnDereference.ql: Reports dereferences of return values from calls that may return NULL (e.g. heap allocations) without a preceding null check.
14+
15+
### Changed
16+
- Standardized the rule ID of UninitializedPtrField.ql to "cpp/microsoft/public/likely-bugs/uninitializedptrfield" and updated accuracy.
17+
518
## [1.8.3] - 2026-02-25
619

720
### Changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Conditionally uninitialized variable
2+
A common pattern is to initialize a local variable by calling another function (an "initialization" function) with the address of the local variable as a pointer argument. That function is then responsible for writing to the memory location referenced by the pointer.
3+
4+
In some cases, the called function may not always write to the memory pointed to by the pointer argument. In such cases, the function will typically return a "status" code, informing the caller as to whether the initialization succeeded or not. If the caller does not check the status code before reading the local variable, it may read uninitialized memory, which can result in unexpected behavior.
5+
6+
7+
## Recommendation
8+
When using an initialization function that does not guarantee to initialize the memory pointed to by the passed pointer, and returns a status code to indicate whether such initialization occurred, the status code should be checked before reading from the local variable.
9+
10+
11+
## Example
12+
In this hypothetical example we have code for managing a series of devices. The code includes a `DeviceConfig` struct that can represent properties about each device. The `initDeviceConfig` function can be called to initialize one of these structures, by providing a "device number", which can be used to look up the appropriate properties in some data store. If an invalid device number is provided, the function returns a status code of `-1`, and does not initialize the provided pointer.
13+
14+
In the first code sample below, the `notify` function calls the `initDeviceConfig` function with a pointer to the local variable `config`, which is then subsequently accessed to fetch properties of the device. However, the code does not check the return value from the function call to `initDeviceConfig`. If the device number passed to the `notify` function was invalid, the `initDeviceConfig` function will leave the `config` variable uninitialized, which will result in the `notify` function accessing uninitialized memory.
15+
16+
17+
```c
18+
struct DeviceConfig {
19+
bool isEnabled;
20+
int channel;
21+
};
22+
23+
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
24+
if (deviceNumber >= getMaxDevices()) {
25+
// No device with that number, return -1 to indicate failure
26+
return -1;
27+
}
28+
// Device with that number, fetch parameters and initialize struct
29+
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
30+
ref->channel = fetchDeviceChannel(deviceNumber);
31+
// Return 0 to indicate success
32+
return 0;
33+
}
34+
35+
int notify(int deviceNumber) {
36+
DeviceConfig config;
37+
initDeviceConfig(&config, deviceNumber);
38+
// BAD: Using config without checking the status code that is returned
39+
if (config.isEnabled) {
40+
notifyChannel(config.channel);
41+
}
42+
}
43+
44+
```
45+
To fix this, the code needs to check that the return value of the call to `initDeviceConfig` is zero. If that is true, then the calling code can safely assume that the local variable has been initialized.
46+
47+
48+
```c
49+
struct DeviceConfig {
50+
bool isEnabled;
51+
int channel;
52+
};
53+
54+
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
55+
if (deviceNumber >= getMaxDevices()) {
56+
// No device with that number, return -1 to indicate failure
57+
return -1;
58+
}
59+
// Device with that number, fetch parameters and initialize struct
60+
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
61+
ref->channel = fetchDeviceChannel(deviceNumber);
62+
// Return 0 to indicate success
63+
return 0;
64+
}
65+
66+
void notify(int deviceNumber) {
67+
DeviceConfig config;
68+
int statusCode = initDeviceConfig(&config, deviceNumber);
69+
if (statusCode == 0) {
70+
// GOOD: Status code returned by initialization function is checked, so this is safe
71+
if (config.isEnabled) {
72+
notifyChannel(config.channel);
73+
}
74+
}
75+
}
76+
77+
```
78+
79+
## References
80+
* Wikipedia: [Uninitialized variable](https://en.wikipedia.org/wiki/Uninitialized_variable).
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>A common pattern is to initialize a local variable by calling another function (an
8+
"initialization" function) with the address of the local variable as a pointer argument. That
9+
function is then responsible for writing to the memory location referenced by the pointer.</p>
10+
11+
<p>In some cases, the called function may not always write to the memory pointed to by the
12+
pointer argument. In such cases, the function will typically return a "status" code, informing the
13+
caller as to whether the initialization succeeded or not. If the caller does not check the status
14+
code before reading the local variable, it may read uninitialized memory, which can result in
15+
unexpected behavior.</p>
16+
17+
</overview>
18+
<recommendation>
19+
20+
<p>When using an initialization function that does not guarantee to initialize the memory pointed to
21+
by the passed pointer, and returns a status code to indicate whether such initialization occurred,
22+
the status code should be checked before reading from the local variable.</p>
23+
24+
</recommendation>
25+
<example>
26+
27+
<p>In this hypothetical example we have code for managing a series of devices. The code
28+
includes a <code>DeviceConfig</code> struct that can represent properties about each device.
29+
The <code>initDeviceConfig</code> function can be called to initialize one of these structures, by
30+
providing a "device number", which can be used to look up the appropriate properties in some data
31+
store. If an invalid device number is provided, the function returns a status code of
32+
<code>-1</code>, and does not initialize the provided pointer.</p>
33+
34+
<p>In the first code sample below, the <code>notify</code> function calls the
35+
<code>initDeviceConfig</code> function with a pointer to the local variable <code>config</code>,
36+
which is then subsequently accessed to fetch properties of the device. However, the code does not
37+
check the return value from the function call to <code>initDeviceConfig</code>. If the
38+
device number passed to the <code>notify</code> function was invalid, the
39+
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
40+
which will result in the <code>notify</code> function accessing uninitialized memory.</p>
41+
42+
<sample src="ConditionallyUninitializedVariableBad.c" />
43+
44+
<p>To fix this, the code needs to check that the return value of the call to
45+
<code>initDeviceConfig</code> is zero. If that is true, then the calling code can safely assume
46+
that the local variable has been initialized.</p>
47+
48+
<sample src="ConditionallyUninitializedVariableGood.c" />
49+
50+
</example>
51+
<references>
52+
53+
<li>
54+
Wikipedia:
55+
<a href="https://en.wikipedia.org/wiki/Uninitialized_variable">Uninitialized variable</a>.
56+
</li>
57+
58+
</references>
59+
</qhelp>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
/**
4+
* @id cpp/microsoft/public/likely-bugs/memory-management/v2/conditionally-uninitialized-variable
5+
* @name Conditionally uninitialized variable
6+
* @description When an initialization function is used to initialize a local variable,
7+
* but the returned status code is not checked, reading the variable may
8+
* result in undefined behaviour.
9+
* @platform Desktop
10+
* @security.severity Low
11+
* @impact Insecure Coding Practice
12+
* @feature.area Multiple
13+
* @repro.text The following code locations potentially contain uninitialized variables
14+
* @owner.email sdat@microsoft.com
15+
* @kind problem
16+
* @problem.severity error
17+
* @query-version 2.0
18+
**/
19+
20+
import cpp
21+
private import UninitializedVariables
22+
23+
from ConditionallyInitializedVariable v, ConditionalInitializationFunction f, ConditionalInitializationCall call, Evidence e
24+
where exists(v.getARiskyAccess(f, call, e))
25+
and e = DefinitionInSnapshot()
26+
select call, "$@: The status of this call to $@ is not checked, potentially leaving $@ uninitialized.",
27+
call.getEnclosingFunction(),
28+
call.getEnclosingFunction().toString(),
29+
f, f.getName(),
30+
v, v.getName()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct DeviceConfig {
2+
bool isEnabled;
3+
int channel;
4+
};
5+
6+
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
7+
if (deviceNumber >= getMaxDevices()) {
8+
// No device with that number, return -1 to indicate failure
9+
return -1;
10+
}
11+
// Device with that number, fetch parameters and initialize struct
12+
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
13+
ref->channel = fetchDeviceChannel(deviceNumber);
14+
// Return 0 to indicate success
15+
return 0;
16+
}
17+
18+
int notify(int deviceNumber) {
19+
DeviceConfig config;
20+
initDeviceConfig(&config, deviceNumber);
21+
// BAD: Using config without checking the status code that is returned
22+
if (config.isEnabled) {
23+
notifyChannel(config.channel);
24+
}
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
struct DeviceConfig {
2+
bool isEnabled;
3+
int channel;
4+
};
5+
6+
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
7+
if (deviceNumber >= getMaxDevices()) {
8+
// No device with that number, return -1 to indicate failure
9+
return -1;
10+
}
11+
// Device with that number, fetch parameters and initialize struct
12+
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
13+
ref->channel = fetchDeviceChannel(deviceNumber);
14+
// Return 0 to indicate success
15+
return 0;
16+
}
17+
18+
void notify(int deviceNumber) {
19+
DeviceConfig config;
20+
int statusCode = initDeviceConfig(&config, deviceNumber);
21+
if (statusCode == 0) {
22+
// GOOD: Status code returned by initialization function is checked, so this is safe
23+
if (config.isEnabled) {
24+
notifyChannel(config.channel);
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)