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

Skip to content

Commit bace0dc

Browse files
author
Dave Bartolomeo
committed
Handle more cases that require synthesizing temporary objects
- Parens around qualifier expressions - Inheritance conversions involving class prvalues
1 parent 9907248 commit bace0dc

7 files changed

Lines changed: 298 additions & 33 deletions

File tree

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

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,24 @@ private predicate usedAsCondition(Expr expr) {
227227
)
228228
}
229229

230+
/**
231+
* Holds if `conv` is an `InheritanceConversion` that requires a `TranslatedLoad`, despite not being
232+
* marked as having an lvalue-to-rvalue conversion.
233+
*
234+
* This is necessary for an `InheritanceConversion` that is originally modeled as a
235+
* prvalue-to-prvalue conversion, since we transform it into a glvalue-to-glvalue conversion. If it
236+
* is actually consumed as a prvalue, such as on the right hand side of an assignment, we need to
237+
* load the resulting glvalue.
238+
*/
239+
private predicate isInheritanceConversionWithImplicitLoad(InheritanceConversion conv) {
240+
// Must have originally been a prvalue-to-prvalue conversion.
241+
isClassPRValue(conv.getExpr()) and
242+
not conv.hasLValueToRValueConversion() and
243+
// Exclude that case where this will be consumed as a glvalue, such as when used as the qualifier
244+
// of a field access.
245+
not isPRValueConversionOnGLValue(conv)
246+
}
247+
230248
/**
231249
* Holds if `expr` is the result of a field access whose qualifier was a prvalue and whose result is
232250
* a prvalue. These accesses are not marked as having loads, but we do need a load in the IR.
@@ -241,35 +259,93 @@ private predicate isPRValueFieldAccessWithImplicitLoad(Expr expr) {
241259
}
242260

243261
/**
244-
* Holds if `expr` is a prvalue of class type that is used directly as the qualifier for a member
245-
* access, or is used after undergoing a prvalue adjustment conversion.
262+
* Holds if `expr` is a prvalue of class type.
263+
*
264+
* This same test is used in several places.
265+
*/
266+
pragma[inline]
267+
private predicate isClassPRValue(Expr expr) {
268+
expr.isPRValueCategory() and
269+
expr.getUnspecifiedType() instanceof Class
270+
}
271+
272+
/**
273+
* Holds if `expr` is consumed as a glvalue by its parent. If `expr` is actually a prvalue, it will
274+
* have any lvalue-to-rvalue conversion ignored. If it does not have an lvalue-to-rvalue conversion,
275+
* it will be materialized into a temporary object.
246276
*/
247-
private predicate isClassPRValueForMemberAccessQualifier(Expr expr) {
248-
exists(Expr qualifier |
249-
exists(FieldAccess access | qualifier = access.getQualifier().getFullyConverted())
277+
private predicate consumedAsGLValue(Expr expr) {
278+
isClassPRValue(expr) and
279+
(
280+
// Qualifier of a field access.
281+
expr = any(FieldAccess a).getQualifier().getFullyConverted()
250282
or
251-
exists(Call call | qualifier = call.getQualifier().getFullyConverted())
252-
|
253-
// The qualifier is a prvalue of class type.
254-
qualifier.getUnspecifiedType() instanceof Class and
255-
qualifier.isPRValueCategory() and
283+
// Qualifier of a member function call.
284+
expr = any(Call c).getQualifier().getFullyConverted()
285+
or
286+
// The operand of an inheritance conversion.
287+
expr = any(InheritanceConversion c).getExpr()
288+
)
289+
}
290+
291+
/**
292+
* Holds if `expr` is a conversion that is originally a prvalue-to-prvalue conversion, but which is
293+
* applied to a prvalue that will actually be consumed as a glvalue.
294+
*/
295+
predicate isPRValueConversionOnGLValue(Conversion conv) {
296+
exists(Expr consumed |
297+
consumedAsGLValue(consumed) and
298+
isClassPRValue(conv.getExpr()) and
299+
(
300+
// Example: The conversion of `std::string` to `const std::string` when evaluating
301+
// `std::string("foo").c_str()`.
302+
conv instanceof PrvalueAdjustmentConversion
303+
or
304+
// Parentheses are transparent.
305+
conv instanceof ParenthesisExpr
306+
or
307+
// Example: The base class conversion in `f().m()`, when `m` is member function of a base
308+
// class of the return type of `f()`.
309+
conv instanceof InheritanceConversion
310+
) and
256311
(
257-
expr = qualifier and not expr instanceof PrvalueAdjustmentConversion
312+
// Base case: The conversion is consumed directly.
313+
conv = consumed
258314
or
259-
// If the qualifier is a prvalue adjustment conversion, the actual object with be provided by
260-
// the operand of that conversion. For example:
261-
// ```c++
262-
// std::string("s").c_str();
263-
// ```
264-
// The object for the qualifier is a prvalue(load) of type `std::string`, but the actual
265-
// fully-converted qualifier of the call to `c_str()` is a prvalue adjustment conversion that
266-
// converts the type to `const std::string` to match the type of the `this` pointer of the
267-
// member function.
268-
expr = qualifier.(PrvalueAdjustmentConversion).getExpr()
315+
// Recursive case: The conversion is the operand of another prvalue conversion.
316+
isPRValueConversionOnGLValue(conv.getConversion())
269317
)
270318
)
271319
}
272320

321+
/**
322+
* Holds if `expr` is a prvalue of class type that is used in a context that requires a glvalue.
323+
*
324+
* Any conversions between `expr` and the ancestor that consumes the glvalue will also be treated
325+
* as glvalues, but are not part of this relation.
326+
*
327+
* For example:
328+
* ```c++
329+
* std::string("s").c_str();
330+
* ```
331+
* The object for the qualifier is a prvalue(load) of type `std::string`, but the actual
332+
* fully-converted qualifier of the call to `c_str()` is a prvalue adjustment conversion that
333+
* converts the type to `const std::string` to match the type of the `this` pointer of the
334+
* member function. In this case, `mustTransformToGLValue()` will hold for the temporary
335+
* `std::string` object, but not the prvalue adjustment on top of it.
336+
* `isPRValueConversionOnGLValue()` would hold for the prvalue adjustment.
337+
*/
338+
private predicate mustTransformToGLValue(Expr expr) {
339+
not isPRValueConversionOnGLValue(expr) and
340+
(
341+
// The expression is the fully converted qualifier, with no prvalue adjustments on top.
342+
consumedAsGLValue(expr)
343+
or
344+
// The expression has conversions on top, but they are all prvalue adjustments.
345+
isPRValueConversionOnGLValue(expr.getConversion())
346+
)
347+
}
348+
273349
/**
274350
* Holds if `expr` has an lvalue-to-rvalue conversion that should be ignored
275351
* when generating IR. This occurs for conversion from an lvalue of function type
@@ -294,7 +370,7 @@ predicate ignoreLoad(Expr expr) {
294370
// the temporary object if the original qualifier was a prvalue. For IR purposes, we always want
295371
// to use the address of the temporary object as the qualifier of a field access or the `this`
296372
// argument to a member function call.
297-
isClassPRValueForMemberAccessQualifier(expr)
373+
mustTransformToGLValue(expr)
298374
)
299375
}
300376

@@ -332,6 +408,8 @@ predicate hasTranslatedLoad(Expr expr) {
332408
needsLoadForParentExpr(expr)
333409
or
334410
isPRValueFieldAccessWithImplicitLoad(expr)
411+
or
412+
isInheritanceConversionWithImplicitLoad(expr)
335413
) and
336414
not ignoreExpr(expr) and
337415
not isNativeCondition(expr) and
@@ -344,7 +422,7 @@ predicate hasTranslatedLoad(Expr expr) {
344422
*/
345423
predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
346424
not ignoreExpr(expr) and
347-
isClassPRValueForMemberAccessQualifier(expr) and
425+
mustTransformToGLValue(expr) and
348426
// If it's a load, we'll just ignore the load in `ignoreLoad()`.
349427
not expr.hasLValueToRValueConversion()
350428
}

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
108108
// If this TranslatedExpr doesn't produce the result, then it must represent
109109
// a glvalue that is then loaded by a TranslatedLoad.
110110
hasTranslatedLoad(expr)
111+
or
112+
// The expression should be treated as a glvalue because its operand was forced to be a glvalue,
113+
// such as for the qualifier of a member access.
114+
isPRValueConversionOnGLValue(expr)
111115
}
112116

113117
final override predicate producesExprResult() {
@@ -1072,14 +1076,6 @@ class TranslatedSimpleConversion extends TranslatedSingleInstructionConversion {
10721076
}
10731077

10741078
override Opcode getOpcode() { result instanceof Opcode::Convert }
1075-
1076-
override predicate isResultGLValue() {
1077-
super.isResultGLValue()
1078-
or
1079-
// If this is a prvalue adjustment, the result is a treated as a glvalue if the source was also
1080-
// treated as a glvalue. See the comment in `ignoreLoad()` for more details.
1081-
expr instanceof PrvalueAdjustmentConversion and ignoreLoad(expr.getExpr())
1082-
}
10831079
}
10841080

10851081
/**

cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@
106106
| string.cpp:363:11:363:16 | string.cpp:358:18:358:23 | AST only |
107107
| string.cpp:382:8:382:14 | string.cpp:374:18:374:23 | IR only |
108108
| string.cpp:383:13:383:15 | string.cpp:374:18:374:23 | IR only |
109-
| string.cpp:396:8:396:8 | string.cpp:389:18:389:23 | AST only |
110-
| string.cpp:397:8:397:8 | string.cpp:389:18:389:23 | AST only |
109+
| string.cpp:396:8:396:15 | string.cpp:389:18:389:23 | IR only |
110+
| string.cpp:397:8:397:15 | string.cpp:389:18:389:23 | IR only |
111111
| string.cpp:399:8:399:8 | string.cpp:389:18:389:23 | AST only |
112112
| string.cpp:401:8:401:8 | string.cpp:389:18:389:23 | AST only |
113113
| string.cpp:404:8:404:11 | string.cpp:389:18:389:23 | IR only |

cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,10 @@
362362
| string.cpp:382:8:382:14 | (reference dereference) | string.cpp:374:18:374:23 | call to source |
363363
| string.cpp:383:13:383:13 | call to operator[] | string.cpp:374:18:374:23 | call to source |
364364
| string.cpp:383:13:383:15 | (reference dereference) | string.cpp:374:18:374:23 | call to source |
365+
| string.cpp:396:8:396:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
366+
| string.cpp:396:8:396:15 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
367+
| string.cpp:397:8:397:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
368+
| string.cpp:397:8:397:15 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
365369
| string.cpp:404:8:404:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
366370
| string.cpp:404:8:404:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
367371
| string.cpp:407:8:407:8 | call to operator* | string.cpp:389:18:389:23 | call to source |

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10650,6 +10650,10 @@ ir.cpp:
1065010650
# 1360| params:
1065110651
# 1360| 0: [Parameter] v
1065210652
# 1360| Type = [Class] destructor_only
10653+
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] POD_Derived returnValue<POD_Derived>()
10654+
# 1363| params:
10655+
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] POD_Middle returnValue<POD_Middle>()
10656+
# 1363| params:
1065310657
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] Point returnValue<Point>()
1065410658
# 1363| params:
1065510659
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] String returnValue<String>()
@@ -11161,6 +11165,114 @@ ir.cpp:
1116111165
# 1426| Value = [Literal] 5
1116211166
# 1426| ValueCategory = prvalue
1116311167
# 1427| 4: [ReturnStmt] return ...
11168+
# 1429| [CopyAssignmentOperator] POD_Base& POD_Base::operator=(POD_Base const&)
11169+
# 1429| params:
11170+
#-----| 0: [Parameter] (unnamed parameter 0)
11171+
#-----| Type = [LValueReferenceType] const POD_Base &
11172+
# 1429| [MoveAssignmentOperator] POD_Base& POD_Base::operator=(POD_Base&&)
11173+
# 1429| params:
11174+
#-----| 0: [Parameter] (unnamed parameter 0)
11175+
#-----| Type = [RValueReferenceType] POD_Base &&
11176+
# 1432| [ConstMemberFunction] float POD_Base::f() const
11177+
# 1432| params:
11178+
# 1435| [CopyAssignmentOperator] POD_Middle& POD_Middle::operator=(POD_Middle const&)
11179+
# 1435| params:
11180+
#-----| 0: [Parameter] (unnamed parameter 0)
11181+
#-----| Type = [LValueReferenceType] const POD_Middle &
11182+
# 1435| [MoveAssignmentOperator] POD_Middle& POD_Middle::operator=(POD_Middle&&)
11183+
# 1435| params:
11184+
#-----| 0: [Parameter] (unnamed parameter 0)
11185+
#-----| Type = [RValueReferenceType] POD_Middle &&
11186+
# 1435| [Constructor] void POD_Middle::POD_Middle()
11187+
# 1435| params:
11188+
# 1439| [CopyAssignmentOperator] POD_Derived& POD_Derived::operator=(POD_Derived const&)
11189+
# 1439| params:
11190+
#-----| 0: [Parameter] (unnamed parameter 0)
11191+
#-----| Type = [LValueReferenceType] const POD_Derived &
11192+
# 1439| [MoveAssignmentOperator] POD_Derived& POD_Derived::operator=(POD_Derived&&)
11193+
# 1439| params:
11194+
#-----| 0: [Parameter] (unnamed parameter 0)
11195+
#-----| Type = [RValueReferenceType] POD_Derived &&
11196+
# 1439| [Constructor] void POD_Derived::POD_Derived()
11197+
# 1439| params:
11198+
# 1443| [TopLevelFunction] void temporary_hierarchy()
11199+
# 1443| params:
11200+
# 1443| body: [BlockStmt] { ... }
11201+
# 1444| 0: [DeclStmt] declaration
11202+
# 1444| 0: [VariableDeclarationEntry] definition of b
11203+
# 1444| Type = [Struct] POD_Base
11204+
# 1444| init: [Initializer] initializer for b
11205+
# 1444| expr: [CStyleCast] (POD_Base)...
11206+
# 1444| Conversion = [BaseClassConversion] base class conversion
11207+
# 1444| Type = [Struct] POD_Base
11208+
# 1444| ValueCategory = prvalue
11209+
# 1444| expr: [FunctionCall] call to returnValue
11210+
# 1444| Type = [Struct] POD_Middle
11211+
# 1444| ValueCategory = prvalue
11212+
# 1445| 1: [ExprStmt] ExprStmt
11213+
# 1445| 0: [AssignExpr] ... = ...
11214+
# 1445| Type = [Struct] POD_Base
11215+
# 1445| ValueCategory = lvalue
11216+
# 1445| 0: [VariableAccess] b
11217+
# 1445| Type = [Struct] POD_Base
11218+
# 1445| ValueCategory = lvalue
11219+
# 1445| 1: [CStyleCast] (POD_Base)...
11220+
# 1445| Conversion = [BaseClassConversion] base class conversion
11221+
# 1445| Type = [Struct] POD_Base
11222+
# 1445| ValueCategory = prvalue(load)
11223+
# 1445| expr: [CStyleCast] (POD_Middle)...
11224+
# 1445| Conversion = [BaseClassConversion] base class conversion
11225+
# 1445| Type = [Struct] POD_Middle
11226+
# 1445| ValueCategory = lvalue
11227+
# 1445| expr: [TemporaryObjectExpr] temporary object
11228+
# 1445| Type = [Struct] POD_Derived
11229+
# 1445| ValueCategory = lvalue
11230+
# 1445| expr: [ParenthesisExpr] (...)
11231+
# 1445| Type = [Struct] POD_Derived
11232+
# 1445| ValueCategory = prvalue
11233+
# 1445| expr: [FunctionCall] call to returnValue
11234+
# 1445| Type = [Struct] POD_Derived
11235+
# 1445| ValueCategory = prvalue
11236+
# 1446| 2: [DeclStmt] declaration
11237+
# 1446| 0: [VariableDeclarationEntry] definition of x
11238+
# 1446| Type = [IntType] int
11239+
# 1446| init: [Initializer] initializer for x
11240+
# 1446| expr: [ValueFieldAccess] x
11241+
# 1446| Type = [IntType] int
11242+
# 1446| ValueCategory = prvalue
11243+
# 1446| -1: [CStyleCast] (POD_Base)...
11244+
# 1446| Conversion = [BaseClassConversion] base class conversion
11245+
# 1446| Type = [Struct] POD_Base
11246+
# 1446| ValueCategory = prvalue
11247+
# 1446| expr: [CStyleCast] (POD_Middle)...
11248+
# 1446| Conversion = [BaseClassConversion] base class conversion
11249+
# 1446| Type = [Struct] POD_Middle
11250+
# 1446| ValueCategory = prvalue
11251+
# 1446| expr: [FunctionCall] call to returnValue
11252+
# 1446| Type = [Struct] POD_Derived
11253+
# 1446| ValueCategory = prvalue
11254+
# 1447| 3: [DeclStmt] declaration
11255+
# 1447| 0: [VariableDeclarationEntry] definition of f
11256+
# 1447| Type = [FloatType] float
11257+
# 1447| init: [Initializer] initializer for f
11258+
# 1447| expr: [FunctionCall] call to f
11259+
# 1447| Type = [FloatType] float
11260+
# 1447| ValueCategory = prvalue
11261+
# 1447| -1: [CStyleCast] (const POD_Base)...
11262+
# 1447| Conversion = [BaseClassConversion] base class conversion
11263+
# 1447| Type = [SpecifiedType] const POD_Base
11264+
# 1447| ValueCategory = prvalue
11265+
# 1447| expr: [CStyleCast] (POD_Middle)...
11266+
# 1447| Conversion = [BaseClassConversion] base class conversion
11267+
# 1447| Type = [Struct] POD_Middle
11268+
# 1447| ValueCategory = prvalue
11269+
# 1447| expr: [ParenthesisExpr] (...)
11270+
# 1447| Type = [Struct] POD_Derived
11271+
# 1447| ValueCategory = prvalue
11272+
# 1447| expr: [FunctionCall] call to returnValue
11273+
# 1447| Type = [Struct] POD_Derived
11274+
# 1447| ValueCategory = prvalue
11275+
# 1448| 4: [ReturnStmt] return ...
1116411276
perf-regression.cpp:
1116511277
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
1116611278
# 4| params:

cpp/ql/test/library-tests/ir/ir/ir.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,4 +1426,25 @@ void temporary_unusual_fields() {
14261426
float f = returnValue<UnusualFields>().a[5];
14271427
}
14281428

1429+
struct POD_Base {
1430+
int x;
1431+
1432+
float f() const;
1433+
};
1434+
1435+
struct POD_Middle : POD_Base {
1436+
int y;
1437+
};
1438+
1439+
struct POD_Derived : POD_Middle {
1440+
int z;
1441+
};
1442+
1443+
void temporary_hierarchy() {
1444+
POD_Base b = returnValue<POD_Middle>();
1445+
b = (returnValue<POD_Derived>()); // Multiple conversions plus parens
1446+
int x = returnValue<POD_Derived>().x;
1447+
float f = (returnValue<POD_Derived>()).f();
1448+
}
1449+
14291450
// semmle-extractor-options: -std=c++17 --clang

0 commit comments

Comments
 (0)