Skip to content

Commit 6cfde70

Browse files
authored
Merge branch 'main' into experimental-strong-params
2 parents 0c0ba92 + 0a35f97 commit 6cfde70

File tree

28 files changed

+1364
-814
lines changed

28 files changed

+1364
-814
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: fix
3+
---
4+
* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`.

cpp/ql/lib/semmle/code/cpp/Element.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import semmle.code.cpp.Location
77
private import semmle.code.cpp.Enclosing
88
private import semmle.code.cpp.internal.ResolveClass
9+
private import semmle.code.cpp.internal.ResolveGlobalVariable
910

1011
/**
1112
* Get the `Element` that represents this `@element`.
@@ -28,9 +29,12 @@ Element mkElement(@element e) { unresolveElement(result) = e }
2829
pragma[inline]
2930
@element unresolveElement(Element e) {
3031
not result instanceof @usertype and
32+
not result instanceof @variable and
3133
result = e
3234
or
3335
e = resolveClass(result)
36+
or
37+
e = resolveGlobalVariable(result)
3438
}
3539

3640
/**

cpp/ql/lib/semmle/code/cpp/Variable.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import semmle.code.cpp.Element
66
import semmle.code.cpp.exprs.Access
77
import semmle.code.cpp.Initializer
88
private import semmle.code.cpp.internal.ResolveClass
9+
private import semmle.code.cpp.internal.ResolveGlobalVariable
910

1011
/**
1112
* A C/C++ variable. For example, in the following code there are four
@@ -32,6 +33,8 @@ private import semmle.code.cpp.internal.ResolveClass
3233
* can have multiple declarations.
3334
*/
3435
class Variable extends Declaration, @variable {
36+
Variable() { isVariable(underlyingElement(this)) }
37+
3538
override string getAPrimaryQlClass() { result = "Variable" }
3639

3740
/** Gets the initializer of this variable, if any. */
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
private predicate hasDefinition(@globalvariable g) {
2+
exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd))
3+
}
4+
5+
pragma[noinline]
6+
private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) {
7+
strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1
8+
}
9+
10+
/** Holds if `g` is a unique global variable with a definition named `name`. */
11+
pragma[noinline]
12+
private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) {
13+
hasDefinition(g) and
14+
mangled_name(g, name) and
15+
onlyOneCompleteGlobalVariableExistsWithMangledName(name)
16+
}
17+
18+
/** Holds if `g` is a global variable without a definition named `name`. */
19+
pragma[noinline]
20+
private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) {
21+
not hasDefinition(g) and
22+
mangled_name(g, name)
23+
}
24+
25+
/**
26+
* Holds if `incomplete` is a global variable without a definition, and there exists
27+
* a unique global variable `complete` with the same name that does have a definition.
28+
*/
29+
private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) {
30+
exists(@mangledname name |
31+
not variable_instantiation(incomplete, complete) and
32+
isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and
33+
isGlobalWithMangledNameAndWithDefinition(name, complete)
34+
)
35+
}
36+
37+
import Cached
38+
39+
cached
40+
private module Cached {
41+
/**
42+
* If `v` is a global variable without a definition, and there exists a unique
43+
* global variable with the same name that does have a definition, then the
44+
* result is that unique global variable. Otherwise, the result is `v`.
45+
*/
46+
cached
47+
@variable resolveGlobalVariable(@variable v) {
48+
hasTwinWithDefinition(v, result)
49+
or
50+
not hasTwinWithDefinition(v, _) and
51+
result = v
52+
}
53+
54+
cached
55+
predicate isVariable(@variable v) {
56+
not v instanceof @globalvariable
57+
or
58+
v = resolveGlobalVariable(_)
59+
}
60+
}

cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,12 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
7474

7575
from
7676
MustFlowPathNode source, MustFlowPathNode sink, VariableAddressInstruction var,
77-
ReturnStackAllocatedMemoryConfig conf, Function f
77+
ReturnStackAllocatedMemoryConfig conf
7878
where
79-
conf.hasFlowPath(source, sink) and
79+
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
8080
source.getNode().asInstruction() = var and
8181
// Only raise an alert if we're returning from the _same_ callable as the on that
8282
// declared the stack variable.
83-
var.getEnclosingFunction() = pragma[only_bind_into](f) and
84-
sink.getNode().getEnclosingCallable() = pragma[only_bind_into](f)
83+
var.getEnclosingFunction() = sink.getNode().getEnclosingCallable()
8584
select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(),
8685
var.getAst().toString()

cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class ExecState extends DataFlow::FlowState {
7777
ExecState() {
7878
this =
7979
"ExecState (" + fst.getLocation() + " | " + fst + ", " + snd.getLocation() + " | " + snd + ")" and
80-
interestingConcatenation(fst, snd)
80+
interestingConcatenation(pragma[only_bind_into](fst), pragma[only_bind_into](snd))
8181
}
8282

8383
DataFlow::Node getFstNode() { result = fst }

cpp/ql/test/library-tests/variables/global/variables.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44
| c.c:6:5:6:6 | ls | array of 4 {int} | 1 |
55
| c.c:8:5:8:7 | iss | array of 4 {array of 2 {int}} | 1 |
66
| c.c:12:11:12:11 | i | typedef {int} as "int_alias" | 1 |
7-
| c.h:4:12:4:13 | ks | array of {int} | 1 |
8-
| c.h:8:12:8:14 | iss | array of {array of 2 {int}} | 1 |
9-
| c.h:10:12:10:12 | i | int | 1 |
107
| d.cpp:3:7:3:8 | xs | array of {int} | 1 |
11-
| d.h:3:14:3:15 | xs | array of 2 {int} | 1 |
128
| file://:0:0:0:0 | (unnamed parameter 0) | reference to {const {struct __va_list_tag}} | 1 |
139
| file://:0:0:0:0 | (unnamed parameter 0) | rvalue reference to {struct __va_list_tag} | 1 |
1410
| file://:0:0:0:0 | fp_offset | unsigned int | 1 |

csharp/tools/tracing-config.lua

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,75 @@ function RegisterExtractorPack(id)
22
local extractor = GetPlatformToolsDirectory() ..
33
'Semmle.Extraction.CSharp.Driver'
44
if OperatingSystem == 'windows' then extractor = extractor .. '.exe' end
5+
6+
function DotnetMatcherBuild(compilerName, compilerPath, compilerArguments,
7+
_languageId)
8+
if compilerName ~= 'dotnet' and compilerName ~= 'dotnet.exe' then
9+
return nil
10+
end
11+
12+
-- The dotnet CLI has the following usage instructions:
13+
-- dotnet [sdk-options] [command] [command-options] [arguments]
14+
-- we are interested in dotnet build, which has the following usage instructions:
15+
-- dotnet [options] build [<PROJECT | SOLUTION>...]
16+
-- For now, parse the command line as follows:
17+
-- Everything that starts with `-` (or `/`) will be ignored.
18+
-- The first non-option argument is treated as the command.
19+
-- if that's `build`, we append `/p:UseSharedCompilation=false` to the command line,
20+
-- otherwise we do nothing.
21+
local match = false
22+
local argv = compilerArguments.argv
23+
if OperatingSystem == 'windows' then
24+
-- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments
25+
-- or, at least, that it is close enough
26+
argv =
27+
NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer)
28+
end
29+
for i, arg in ipairs(argv) do
30+
-- dotnet options start with either - or / (both are legal)
31+
local firstCharacter = string.sub(arg, 1, 1)
32+
if not (firstCharacter == '-') and not (firstCharacter == '/') then
33+
Log(1, 'Dotnet subcommand detected: %s', arg)
34+
if arg == 'build' then match = true end
35+
break
36+
end
37+
end
38+
if match then
39+
return {
40+
order = ORDER_REPLACE,
41+
invocation = BuildExtractorInvocation(id, compilerPath,
42+
compilerPath,
43+
compilerArguments, nil, {
44+
'/p:UseSharedCompilation=false'
45+
})
46+
}
47+
end
48+
return nil
49+
end
50+
551
local windowsMatchers = {
52+
DotnetMatcherBuild,
653
CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor, {
754
prepend = {'--dotnetexec', '--cil'},
855
order = ORDER_BEFORE
956
}),
1057
CreatePatternMatcher({'^csc.*%.exe$'}, MatchCompilerName, extractor, {
1158
prepend = {'--compiler', '"${compiler}"', '--cil'},
1259
order = ORDER_BEFORE
13-
1460
}),
1561
CreatePatternMatcher({'^fakes.*%.exe$', 'moles.*%.exe'},
1662
MatchCompilerName, nil, {trace = false})
1763
}
1864
local posixMatchers = {
19-
CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName,
65+
DotnetMatcherBuild,
66+
CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName,
2067
extractor, {
21-
prepend = {'--compiler', '"${compiler}"', '--cil'},
68+
prepend = {'--dotnetexec', '--cil'},
2269
order = ORDER_BEFORE
23-
2470
}),
25-
CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName,
71+
CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName,
2672
extractor, {
27-
prepend = {'--dotnetexec', '--cil'},
73+
prepend = {'--compiler', '"${compiler}"', '--cil'},
2874
order = ORDER_BEFORE
2975
}), function(compilerName, compilerPath, compilerArguments, _languageId)
3076
if MatchCompilerName('^msbuild$', compilerName, compilerPath,
@@ -49,7 +95,6 @@ function RegisterExtractorPack(id)
4995
else
5096
return posixMatchers
5197
end
52-
5398
end
5499

55100
-- Return a list of minimum supported versions of the configuration file format

0 commit comments

Comments
 (0)