Skip to content

Commit c0417ac

Browse files
authored
Merge pull request #2740 from dbartol/dbartol/InitializeNonLocal
C++: Prevent `AliasedVirtualVariable` from overlapping string literals
2 parents 5125dc7 + e06f468 commit c0417ac

File tree

15 files changed

+4212
-3595
lines changed

15 files changed

+4212
-3595
lines changed

cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private newtype TOpcode =
6363
TUnmodeledDefinition() or
6464
TUnmodeledUse() or
6565
TAliasedDefinition() or
66+
TInitializeNonLocal() or
6667
TAliasedUse() or
6768
TPhi() or
6869
TBuiltIn() or
@@ -596,6 +597,14 @@ module Opcode {
596597
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
597598
}
598599

600+
class InitializeNonLocal extends Opcode, TInitializeNonLocal {
601+
final override string toString() { result = "InitializeNonLocal" }
602+
603+
final override MemoryAccessKind getWriteMemoryAccess() {
604+
result instanceof NonLocalMemoryAccess
605+
}
606+
}
607+
599608
class AliasedUse extends Opcode, TAliasedUse {
600609
final override string toString() { result = "AliasedUse" }
601610

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
298298

299299
final override string toStringInternal() { result = "{AllNonLocal}" }
300300

301-
final override VirtualVariable getVirtualVariable() { result = TAllAliasedMemory(irFunc, false) }
301+
final override AliasedVirtualVariable getVirtualVariable() { result.getIRFunction() = irFunc }
302302

303303
final override Language::LanguageType getType() {
304304
result = any(IRUnknownType type).getCanonicalLanguageType()
@@ -311,6 +311,14 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
311311
final override string getUniqueId() { result = "{AllNonLocal}" }
312312

313313
final override predicate isMayAccess() { isMayAccess = true }
314+
315+
override predicate canDefineReadOnly() {
316+
// A "must" access that defines all non-local memory appears only on the `InitializeNonLocal`
317+
// instruction, which provides the initial definition for all memory outside of the current
318+
// function's stack frame. This memory includes string literals and other read-only globals, so
319+
// we allow such an access to be the definition for a use of a read-only location.
320+
not isMayAccess()
321+
}
314322
}
315323

316324
/**
@@ -341,16 +349,6 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
341349

342350
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
343351
AliasedVirtualVariable() { not isMayAccess() }
344-
345-
override predicate canDefineReadOnly() {
346-
// A must-def of all aliased memory is only used in two places:
347-
// 1. In the prologue of the function, to provide a definition for all memory defined before the
348-
// function was called. In this case, it needs to provide a definition even for read-only
349-
// non-local variables.
350-
// 2. As the result of a `Chi` instruction. These don't participate in overlap analysis, so it's
351-
// OK if we let this predicate hold in that case.
352-
any()
353-
}
354352
}
355353

356354
/**
@@ -405,10 +403,16 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
405403
use instanceof AllNonLocalMemory and
406404
result instanceof MustExactlyOverlap
407405
or
408-
// AllNonLocalMemory may partially overlap any location within the same virtual variable,
409-
// except a local variable.
410-
result instanceof MayPartiallyOverlap and
411-
not use.isAlwaysAllocatedOnStack()
406+
not use instanceof AllNonLocalMemory and
407+
not use.isAlwaysAllocatedOnStack() and
408+
if use instanceof VariableMemoryLocation
409+
then
410+
// AllNonLocalMemory totally overlaps any non-local variable.
411+
result instanceof MustTotallyOverlap
412+
else
413+
// AllNonLocalMemory may partially overlap any other location within the same virtual
414+
// variable, except a stack variable.
415+
result instanceof MayPartiallyOverlap
412416
)
413417
or
414418
def.getVirtualVariable() = use.getVirtualVariable() and

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,21 @@ module DefUse {
759759
then defLocation = useLocation
760760
else (
761761
definitionHasPhiNode(defLocation, block) and
762-
defLocation = useLocation.getVirtualVariable()
762+
defLocation = useLocation.getVirtualVariable() and
763+
// Handle the unusual case where a virtual variable does not overlap one of its member
764+
// locations. For example, a definition of the virtual variable representing all aliased
765+
// memory does not overlap a use of a string literal, because the contents of a string
766+
// literal can never be redefined. The string literal's location could still be a member of
767+
// the `AliasedVirtualVariable` due to something like:
768+
// ```
769+
// char s[10];
770+
// strcpy(s, p);
771+
// const char* p = b ? "SomeLiteral" : s;
772+
// return p[3];
773+
// ```
774+
// In the above example, `p[3]` may access either the string literal or the local variable
775+
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
776+
exists(Alias::getOverlap(defLocation, useLocation))
763777
)
764778
)
765779
or

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ newtype TInstructionTag =
2828
UnmodeledDefinitionTag() or
2929
UnmodeledUseTag() or
3030
AliasedDefinitionTag() or
31+
InitializeNonLocalTag() or
3132
AliasedUseTag() or
3233
SwitchBranchTag() or
3334
CallTargetTag() or
@@ -126,6 +127,8 @@ string getInstructionTagId(TInstructionTag tag) {
126127
or
127128
tag = AliasedDefinitionTag() and result = "AliasedDef"
128129
or
130+
tag = InitializeNonLocalTag() and result = "InitNonLocal"
131+
or
129132
tag = AliasedUseTag() and result = "AliasedUse"
130133
or
131134
tag = SwitchBranchTag() and result = "SwitchBranch"

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
7171
result = getInstruction(AliasedDefinitionTag())
7272
or
7373
tag = AliasedDefinitionTag() and
74+
result = getInstruction(InitializeNonLocalTag())
75+
or
76+
tag = InitializeNonLocalTag() and
7477
result = getInstruction(UnmodeledDefinitionTag())
7578
or
7679
(
@@ -144,6 +147,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
144147
opcode instanceof Opcode::AliasedDefinition and
145148
resultType = getUnknownType()
146149
or
150+
tag = InitializeNonLocalTag() and
151+
opcode instanceof Opcode::InitializeNonLocal and
152+
resultType = getUnknownType()
153+
or
147154
tag = InitializeThisTag() and
148155
opcode instanceof Opcode::InitializeThis and
149156
resultType = getTypeForGLValue(getThisType())

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,21 @@ module DefUse {
759759
then defLocation = useLocation
760760
else (
761761
definitionHasPhiNode(defLocation, block) and
762-
defLocation = useLocation.getVirtualVariable()
762+
defLocation = useLocation.getVirtualVariable() and
763+
// Handle the unusual case where a virtual variable does not overlap one of its member
764+
// locations. For example, a definition of the virtual variable representing all aliased
765+
// memory does not overlap a use of a string literal, because the contents of a string
766+
// literal can never be redefined. The string literal's location could still be a member of
767+
// the `AliasedVirtualVariable` due to something like:
768+
// ```
769+
// char s[10];
770+
// strcpy(s, p);
771+
// const char* p = b ? "SomeLiteral" : s;
772+
// return p[3];
773+
// ```
774+
// In the above example, `p[3]` may access either the string literal or the local variable
775+
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
776+
exists(Alias::getOverlap(defLocation, useLocation))
763777
)
764778
)
765779
or

0 commit comments

Comments
 (0)