Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aef26cc

Browse files
author
AndreiDiaconu1
committed
C# IR: Fix Load inconsistencies, in, out, ref
Fixed a bug where assignments of the form `Object obj1 = obj2` would not generate a load instruction for `obj2` (see `raw_ir.expected`). Added an extra `Load` for object creations that involve structs. This is because the variable that represents the struct should hold the actual struct, not a reference to it. Refactored the piece of code that decided if a particular expr needs a load instruction and improved the code sharing between `TranslatedExpr.qll` and `TranslatedElement.qll` by creating 2 predicates that tell if a certain expr does or does not need a load. Added support for `in`, `out` and `ref` parameters.
1 parent 9ac0527 commit aef26cc

4 files changed

Lines changed: 374 additions & 84 deletions

File tree

csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,58 @@ private predicate usedAsCondition(Expr expr) {
174174
)
175175
}
176176

177+
/**
178+
* Holds if we should have a `Load` instruction for `expr` when generating the IR.
179+
*/
180+
predicate needsLoad(Expr expr) {
181+
expr instanceof AssignableRead
182+
or
183+
// We need an extra load for the `PointerIndirectionExpr`
184+
expr instanceof PointerIndirectionExpr and
185+
// If the dereferencing happens on the lhs of an
186+
// assignment we shouldn't have a load instruction
187+
not exists(Assignment a | a.getLValue() = expr)
188+
}
189+
190+
/**
191+
* Holds if we should ignore the `Load` instruction for `expr` when generating IR.
192+
*/
193+
predicate ignoreLoad(Expr expr) {
194+
// No load needed for an array access
195+
expr.getParent() instanceof ArrayAccess
196+
or
197+
// No load is needed for the lvalue in an assignment such as:
198+
// Eg. `Object obj = oldObj`;
199+
expr.getParent().(Assignment).getLValue() = expr and
200+
expr.getType() instanceof RefType
201+
or
202+
// Since the loads for a crement operation is handled by the translation
203+
// of the operation, we ignore the load here
204+
expr.getParent() instanceof MutatorOperation
205+
or
206+
// The `&` operator does not need a load, since the
207+
// address is the final value of the expression
208+
expr.getParent() instanceof AddressOfExpr
209+
or
210+
// If expr is a variable access used as the qualifier for a field access and
211+
// its target variable is a value type variable,
212+
// ignore the load since the address of a variable that is a value type is
213+
// given by a single `VariableAddress` instruction.
214+
expr.getParent().(FieldAccess).getQualifier() = expr and
215+
expr.(VariableAccess).getType().isValueType() and
216+
not (
217+
expr.(VariableAccess).getTarget().(Parameter).isOutOrRef() or
218+
expr.(VariableAccess).getTarget().(Parameter).isIn()
219+
)
220+
or
221+
// If expr is passed as an `out,`ref` or `in` argument,
222+
// no load should take place since we pass the address, not the
223+
// value of the variable
224+
expr.(AssignableAccess).isOutOrRefArgument()
225+
or
226+
expr.(AssignableAccess).isInArgument()
227+
}
228+
177229
newtype TTranslatedElement =
178230
// An expression that is not being consumed as a condition
179231
TTranslatedValueExpr(Expr expr) {
@@ -189,17 +241,9 @@ newtype TTranslatedElement =
189241
// A separate element to handle the lvalue-to-rvalue conversion step of an
190242
// expression.
191243
TTranslatedLoad(Expr expr) {
192-
// TODO: Revisit and make sure Loads are only used when needed
193244
not ignoreExpr(expr) and
194-
expr instanceof AssignableRead and
195-
not expr.getParent() instanceof ArrayAccess and
196-
not (
197-
expr.getParent() instanceof Assignment and
198-
expr.getType() instanceof RefType
199-
) and
200-
// Ignore loads for reads in `++` and `--` since their
201-
// translated elements handle them
202-
not expr.getParent() instanceof MutatorOperation
245+
not ignoreLoad(expr) and
246+
needsLoad(expr)
203247
} or
204248
// An expression most naturally translated as control flow.
205249
TTranslatedNativeCondition(Expr expr) {

csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 117 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,12 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
9292
* then a load.
9393
*/
9494
final override predicate producesExprResult() {
95-
// Reads need loads
96-
not expr instanceof AssignableRead
95+
// If the expr needs a load, its translation does not produce the final value.
96+
not needsLoad(expr)
9797
or
98-
// No load needed for an array access
99-
expr.getParent() instanceof ArrayAccess
100-
or
101-
// No load is needed for `RefTypes` in assignments
102-
// Eg. `Object obj = oldObj`;
103-
expr.getParent() instanceof Assignment and
104-
expr.getType() instanceof RefType
105-
or
106-
// Since the loads for a crement operation is handled by the
107-
// expression itself, we ignore the default load
108-
expr.getParent() instanceof MutatorOperation
98+
// If we're supposed to ignore the load on this expression, then this
99+
// expression produces the final value.
100+
ignoreLoad(expr)
109101
}
110102

111103
/**
@@ -771,16 +763,63 @@ abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr {
771763
result = getTranslatedExpr(expr.(QualifiableExpr).getQualifier())
772764
}
773765

774-
override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
766+
override Instruction getResult() {
767+
if this.needsExtraLoad()
768+
then result = this.getInstruction(LoadTag())
769+
else result = this.getInstruction(AddressTag())
770+
}
771+
772+
override predicate hasInstruction(
773+
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
774+
) {
775+
this.needsExtraLoad() and
776+
tag = LoadTag() and
777+
opcode instanceof Opcode::Load and
778+
resultType = this.getResultType() and
779+
isLValue = false
780+
}
775781

776782
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
777-
tag = OnlyInstructionTag() and
778-
result = this.getParent().getChildSuccessor(this) and
783+
tag = AddressTag() and
784+
(
785+
if this.needsExtraLoad()
786+
then result = this.getInstruction(LoadTag())
787+
else result = this.getParent().getChildSuccessor(this)
788+
) and
779789
kind instanceof GotoEdge
790+
or
791+
this.needsExtraLoad() and
792+
tag = LoadTag() and
793+
kind instanceof GotoEdge and
794+
result = this.getParent().getChildSuccessor(this)
795+
}
796+
797+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
798+
this.needsExtraLoad() and
799+
tag = LoadTag() and
800+
(
801+
operandTag instanceof AddressOperandTag and
802+
result = this.getInstruction(AddressTag())
803+
or
804+
operandTag instanceof LoadOperandTag and
805+
result = this.getEnclosingFunction().getUnmodeledDefinitionInstruction()
806+
)
780807
}
781808

782809
final override Instruction getChildSuccessor(TranslatedElement child) {
783-
child = this.getQualifier() and result = this.getInstruction(OnlyInstructionTag())
810+
child = this.getQualifier() and result = this.getInstruction(AddressTag())
811+
}
812+
813+
/**
814+
* Some variable accesses need an extra load, eg. ref parameters,
815+
* out parameters
816+
*/
817+
final predicate needsExtraLoad() {
818+
(
819+
expr.getTarget().(Parameter).isOutOrRef() or
820+
expr.getTarget().(Parameter).isIn()
821+
) and
822+
(expr.getParent() instanceof FieldAccess implies expr.getType().isRefType())
784823
}
785824
}
786825

@@ -801,22 +840,26 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
801840
override Instruction getFirstInstruction() {
802841
if exists(this.getQualifier())
803842
then result = this.getQualifier().getFirstInstruction()
804-
else result = this.getInstruction(OnlyInstructionTag())
843+
else result = this.getInstruction(AddressTag())
805844
}
806845

807-
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
846+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
847+
result = TranslatedVariableAccess.super.getInstructionOperand(tag, operandTag)
848+
}
808849

809850
override predicate hasInstruction(
810851
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
811852
) {
812-
tag = OnlyInstructionTag() and
853+
TranslatedVariableAccess.super.hasInstruction(opcode, tag, resultType, isLValue)
854+
or
855+
tag = AddressTag() and
813856
opcode instanceof Opcode::VariableAddress and
814857
resultType = this.getResultType() and
815858
isLValue = true
816859
}
817860

818861
override IRVariable getInstructionVariable(InstructionTag tag) {
819-
tag = OnlyInstructionTag() and
862+
tag = AddressTag() and
820863
result = getIRUserVariable(expr.getEnclosingCallable(), expr.getTarget())
821864
}
822865
}
@@ -831,11 +874,13 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
831874
else
832875
// it means that the access is part of an `ObjectInitializer` expression
833876
// so the instructions for the qualifier have been generated previously.
834-
result = this.getInstruction(OnlyInstructionTag())
877+
result = this.getInstruction(AddressTag())
835878
}
836879

837880
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
838-
tag = OnlyInstructionTag() and
881+
result = TranslatedVariableAccess.super.getInstructionOperand(tag, operandTag)
882+
or
883+
tag = AddressTag() and
839884
operandTag instanceof UnaryOperandTag and
840885
// A normal field access always has a qualifier
841886
if exists(this.getQualifier())
@@ -851,14 +896,14 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
851896
override predicate hasInstruction(
852897
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
853898
) {
854-
tag = OnlyInstructionTag() and
899+
tag = AddressTag() and
855900
opcode instanceof Opcode::FieldAddress and
856901
resultType = this.getResultType() and
857902
isLValue = true
858903
}
859904

860905
override Field getInstructionField(InstructionTag tag) {
861-
tag = OnlyInstructionTag() and
906+
tag = AddressTag() and
862907
result = expr.getTarget()
863908
}
864909
}
@@ -1992,35 +2037,75 @@ abstract class TranslatedCreation extends TranslatedCoreExpr, TTranslatedCreatio
19922037
opcode instanceof Opcode::NewObj and
19932038
resultType = expr.getType() and
19942039
isLValue = false
2040+
or
2041+
this.needsLoad() and
2042+
tag = LoadTag() and
2043+
opcode instanceof Opcode::Load and
2044+
resultType = expr.getType() and
2045+
isLValue = false
19952046
}
19962047

19972048
final override Instruction getFirstInstruction() { result = this.getInstruction(NewObjTag()) }
19982049

1999-
override Instruction getResult() { result = getInstruction(NewObjTag()) }
2050+
override Instruction getResult() {
2051+
if not this.needsLoad()
2052+
then result = this.getInstruction(NewObjTag())
2053+
else result = this.getInstruction(LoadTag())
2054+
}
20002055

20012056
override Instruction getReceiver() { result = getInstruction(NewObjTag()) }
20022057

20032058
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
20042059
kind instanceof GotoEdge and
20052060
tag = NewObjTag() and
20062061
result = this.getConstructorCall().getFirstInstruction()
2062+
or
2063+
this.needsLoad() and
2064+
kind instanceof GotoEdge and
2065+
tag = LoadTag() and
2066+
result = this.getParent().getChildSuccessor(this)
2067+
}
2068+
2069+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
2070+
this.needsLoad() and
2071+
tag = LoadTag() and
2072+
(
2073+
operandTag instanceof AddressOperandTag and
2074+
result = this.getInstruction(NewObjTag())
2075+
or
2076+
operandTag instanceof LoadOperandTag and
2077+
result = this.getEnclosingFunction().getUnmodeledDefinitionInstruction()
2078+
)
20072079
}
20082080

20092081
override Instruction getChildSuccessor(TranslatedElement child) {
20102082
(
20112083
child = this.getConstructorCall() and
20122084
if exists(this.getInitializerExpr())
20132085
then result = this.getInitializerExpr().getFirstInstruction()
2014-
else result = this.getParent().getChildSuccessor(this)
2086+
else result = getLoadOrChildSuccessor()
20152087
)
20162088
or
20172089
child = this.getInitializerExpr() and
2018-
result = this.getParent().getChildSuccessor(this)
2090+
result = getLoadOrChildSuccessor()
2091+
}
2092+
2093+
private Instruction getLoadOrChildSuccessor() {
2094+
if not this.needsLoad()
2095+
then result = this.getParent().getChildSuccessor(this)
2096+
else result = this.getInstruction(LoadTag())
20192097
}
20202098

20212099
abstract TranslatedElement getConstructorCall();
20222100

20232101
abstract TranslatedExpr getInitializerExpr();
2102+
2103+
/**
2104+
* If the newly allocated object is a value type, then we need
2105+
* to load the newly allocated object before storing it in the variable,
2106+
* since `NewObj` returns an address.
2107+
*/
2108+
abstract predicate needsLoad();
20242109
}
20252110

20262111
/**
@@ -2037,6 +2122,8 @@ class TranslatedObjectCreation extends TranslatedCreation {
20372122
// also return `this`).
20382123
result.getAST() = this.getAST()
20392124
}
2125+
2126+
override predicate needsLoad() { expr.getObjectType().isValueType() }
20402127
}
20412128

20422129
/**
@@ -2050,4 +2137,6 @@ class TranslatedDelegateCreation extends TranslatedCreation {
20502137
override TranslatedElement getConstructorCall() {
20512138
result = DelegateElements::getConstructor(expr)
20522139
}
2140+
2141+
override predicate needsLoad() { none() }
20532142
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
class MyClass {
2+
public int fld;
3+
}
4+
5+
struct MyStruct {
6+
public int fld;
7+
}
8+
9+
class InOutRef
10+
{
11+
static void set(ref MyClass o1, MyClass o2)
12+
{
13+
o1 = o2;
14+
}
15+
16+
static void F(ref int a, ref MyStruct b, in MyStruct b1, ref MyClass c, in MyClass c1)
17+
{
18+
b.fld = 0;
19+
a = b.fld;
20+
21+
c.fld = 10;
22+
a = c.fld;
23+
24+
b = b1;
25+
26+
set(ref c, c1);
27+
}
28+
29+
static void Main()
30+
{
31+
int a = 0;
32+
MyStruct b = new MyStruct();
33+
MyClass c = new MyClass();
34+
F(ref a, ref b, in b, ref c, in c);
35+
36+
int x = b.fld;
37+
}
38+
}

0 commit comments

Comments
 (0)