diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 850033a76b7..0765ff92cf9 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -87,6 +87,9 @@ private cached newtype HCBase = HC_NonmemberFunctionCall(Function fcn, HC_Args args) { mk_NonmemberFunctionCall(fcn, args, _) } + or HC_ExprCall(HashCons hc, HC_Args args) { + mk_ExprCall(hc, args, _) + } or HC_MemberFunctionCall(Function trg, HashCons qual, HC_Args args) { mk_MemberFunctionCall(trg, qual, args, _) @@ -107,6 +110,12 @@ private cached newtype HCBase = or HC_AlignofExpr(HashCons child) {mk_AlignofExpr(child, _)} or + HC_UuidofOperator(Type t) {mk_UuidofOperator(t, _)} + or + HC_TypeidType(Type t) {mk_TypeidType(t, _)} + or + HC_TypeidExpr(HashCons child) {mk_TypeidExpr(child, _)} + or HC_ClassAggregateLiteral(Class c, HC_Fields hcf) { mk_ClassAggregateLiteral(c, hcf, _) } @@ -123,6 +132,14 @@ private cached newtype HCBase = or HC_ReThrowExpr() or + HC_ConditionalExpr(HashCons cond, HashCons trueHC, HashCons falseHC) { + mk_ConditionalExpr(cond, trueHC, falseHC, _) + } + or + HC_NoExceptExpr(HashCons child) { + mk_NoExceptExpr(child, _) + } + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } @@ -153,11 +170,11 @@ private newtype HC_Align = /** Used to implement hash-consing of argument lists */ private newtype HC_Args = - HC_EmptyArgs(Function fcn) { + HC_EmptyArgs() { any() } - or HC_ArgCons(Function fcn, HashCons hc, int i, HC_Args list) { - mk_ArgCons(fcn, hc, i, list, _) + or HC_ArgCons(HashCons hc, int i, HC_Args list) { + mk_ArgCons(hc, i, list, _) } /** @@ -228,12 +245,18 @@ class HashCons extends HCBase { if this instanceof HC_SizeofExpr then result = "SizeofExprOperator" else if this instanceof HC_AlignofType then result = "AlignofTypeOperator" else if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else + if this instanceof HC_UuidofOperator then result = "UuidofOperator" else + if this instanceof HC_TypeidType then result = "TypeidType" else + if this instanceof HC_TypeidExpr then result = "TypeidExpr" else if this instanceof HC_ArrayAggregateLiteral then result = "ArrayAggregateLiteral" else if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else if this instanceof HC_DeleteExpr then result = "DeleteExpr" else if this instanceof HC_DeleteArrayExpr then result = "DeleteArrayExpr" else if this instanceof HC_ThrowExpr then result = "ThrowExpr" else if this instanceof HC_ReThrowExpr then result = "ReThrowExpr" else + if this instanceof HC_ExprCall then result = "ExprCall" else + if this instanceof HC_ConditionalExpr then result = "ConditionalExpr" else + if this instanceof HC_NoExceptExpr then result = "NoExceptExpr" else result = "error" } @@ -473,19 +496,40 @@ private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionC analyzableNonmemberFunctionCall(fc) and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) ) or fc.getNumberOfArguments() = 0 and - args = HC_EmptyArgs(fcn) + args = HC_EmptyArgs() + ) +} + +private predicate analyzableExprCall(ExprCall ec) { + forall(int i | + exists(ec.getArgument(i)) | + strictcount(ec.getArgument(i).getFullyConverted()) = 1 + ) and + strictcount(ec.getExpr().getFullyConverted()) = 1 +} + +private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) { + hc.getAnExpr() = ec.getExpr() and + ( + exists(HashCons head, HC_Args tail | + args = HC_ArgCons(head, ec.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, ec.getNumberOfArguments() - 1, tail, ec) + ) + or + ec.getNumberOfArguments() = 0 and + args = HC_EmptyArgs() ) } private predicate analyzableMemberFunctionCall( FunctionCall fc) { forall(int i | - exists(fc.getArgument(i)) | + exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1 ) and strictcount(fc.getTarget()) = 1 and @@ -503,40 +547,39 @@ private predicate mk_MemberFunctionCall( hashCons(fc.getQualifier().getFullyConverted()) = qual and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) ) or fc.getNumberOfArguments() = 0 and - args = HC_EmptyArgs(fcn) + args = HC_EmptyArgs() ) } -private predicate analyzableFunctionCall( - FunctionCall fc -) { - analyzableNonmemberFunctionCall(fc) +private predicate analyzableCall(Call c) { + analyzableNonmemberFunctionCall(c) or - analyzableMemberFunctionCall(fc) + analyzableMemberFunctionCall(c) + or + analyzableExprCall(c) } /** * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, FunctionCall fc) { - analyzableFunctionCall(fc) and - fc.getTarget() = fcn and - hc = hashCons(fc.getArgument(i).getFullyConverted()) and +private predicate mk_ArgCons(HashCons hc, int i, HC_Args list,Call c) { + analyzableCall(c) and + hc = hashCons(c.getArgument(i).getFullyConverted()) and ( exists(HashCons head, HC_Args tail | - list = HC_ArgCons(fcn, head, i - 1, tail) and - mk_ArgCons(fcn, head, i - 1, tail, fc) and + list = HC_ArgCons(head, i - 1, tail) and + mk_ArgCons(head, i - 1, tail, c) and i > 0 ) or i = 0 and - list = HC_EmptyArgs(fcn) + list = HC_EmptyArgs() ) } @@ -545,14 +588,15 @@ private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, Fun * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, FunctionCall fc) { - analyzableFunctionCall(fc) and - fc.getTarget() = fcn and - hc = hashCons(fc.getArgument(i).getFullyConverted()) and +private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, + Call c) { + analyzableCall(c) and + c.getTarget() = fcn and + hc = hashCons(c.getArgument(i).getFullyConverted()) and ( exists(HashCons head, HC_Alloc tail | list = HC_AllocArgCons(fcn, head, i - 1, tail, aligned) and - mk_AllocArgCons(fcn, head, i - 1, tail, aligned, fc) and + mk_AllocArgCons(fcn, head, i - 1, tail, aligned, c) and ( aligned = true and i > 2 @@ -735,6 +779,34 @@ private predicate mk_SizeofExpr(HashCons child, SizeofExprOperator e) { child = hashCons(e.getAChild()) } +private predicate analyzableUuidofOperator(UuidofOperator e) { + strictcount(e.getTypeOperand()) = 1 +} + +private predicate mk_UuidofOperator(Type t, UuidofOperator e) { + analyzableUuidofOperator(e) and + t = e.getTypeOperand() +} + +private predicate analyzableTypeidType(TypeidOperator e) { + strictcount(e.getAChild()) = 0 +} + +private predicate mk_TypeidType(Type t, TypeidOperator e) { + analyzableTypeidType(e) and + t = e.getResultType() +} + +private predicate analyzableTypeidExpr(Expr e) { + e instanceof TypeidOperator and + strictcount(e.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_TypeidExpr(HashCons child, TypeidOperator e) { + analyzableTypeidExpr(e) and + child = hashCons(e.getAChild()) +} + private predicate analyzableAlignofType(AlignofTypeOperator e) { strictcount(e.getType().getUnspecifiedType()) = 1 and strictcount(e.getTypeOperand()) = 1 @@ -851,6 +923,30 @@ private predicate mk_ReThrowExpr(ReThrowExpr te) { any() } +private predicate analyzableConditionalExpr(ConditionalExpr ce) { + strictcount(ce.getCondition().getFullyConverted()) = 1 and + strictcount(ce.getThen().getFullyConverted()) = 1 and + strictcount(ce.getElse().getFullyConverted()) = 1 +} + +private predicate mk_ConditionalExpr(HashCons cond, HashCons trueHc, HashCons falseHc, + ConditionalExpr ce) { + analyzableConditionalExpr(ce) and + cond.getAnExpr() = ce.getCondition() and + trueHc.getAnExpr() = ce.getThen() and + falseHc.getAnExpr() = ce.getElse() +} + +private predicate analyzableNoExceptExpr(NoExceptExpr nee) { + strictcount(nee.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_NoExceptExpr(HashCons child, NoExceptExpr nee) { + analyzableNoExceptExpr(nee) and + nee.getExpr() = child.getAnExpr().getFullyConverted() +} + + /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -914,6 +1010,11 @@ cached HashCons hashCons(Expr e) { result = HC_NonmemberFunctionCall(fcn, args) ) or + exists(HashCons hc, HC_Args args + | mk_ExprCall(hc, args, e) and + result = HC_ExprCall(hc, args) + ) + or exists(Function fcn, HashCons qual, HC_Args args | mk_MemberFunctionCall(fcn, qual, args, e) and result = HC_MemberFunctionCall(fcn, qual, args) @@ -940,6 +1041,16 @@ cached HashCons hashCons(Expr e) { ) or exists(Type t + | mk_TypeidType(t, e) and + result = HC_TypeidType(t) + ) + or + exists(HashCons child + | mk_TypeidExpr(child, e) and + result = HC_TypeidExpr(child) + ) + or + exists(Type t | mk_AlignofType(t, e) and result = HC_AlignofType(t) ) @@ -979,6 +1090,11 @@ cached HashCons hashCons(Expr e) { result = HC_ReThrowExpr() ) or + exists(HashCons cond, HashCons thenHC, HashCons elseHC + | mk_ConditionalExpr(cond, thenHC, elseHC, e) and + result = HC_ConditionalExpr(cond, thenHC, elseHC) + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -1011,16 +1127,22 @@ predicate analyzableExpr(Expr e, string kind) { (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or (analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or (analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") or + (analyzableExprCall(e) and kind = "ExprCall") or (analyzableNewExpr(e) and kind = "NewExpr") or (analyzableNewArrayExpr(e) and kind = "NewArrayExpr") or (analyzableSizeofType(e) and kind = "SizeofTypeOperator") or (analyzableSizeofExpr(e) and kind = "SizeofExprOperator") or (analyzableAlignofType(e) and kind = "AlignofTypeOperator") or (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") or + (analyzableUuidofOperator(e) and kind = "UuidofOperator") or + (analyzableTypeidType(e) and kind = "TypeidType") or + (analyzableTypeidExpr(e) and kind = "TypeidExpr") or (analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral") or (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") or (analyzableDeleteExpr(e) and kind = "DeleteExpr") or (analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr") or (analyzableThrowExpr(e) and kind = "ThrowExpr") or - (analyzableReThrowExpr(e) and kind = "ReThrowExpr") + (analyzableReThrowExpr(e) and kind = "ReThrowExpr") or + (analyzableConditionalExpr(e) and kind = "ConditionalExpr") or + (analyzableNoExceptExpr(e) and kind = "NoExceptExpr") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 0680e7d8f2c..b985d73d559 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -111,3 +111,13 @@ | test.cpp:311:5:311:5 | 0 | 311:c5-c5 312:c5-c5 315:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 | | test.cpp:313:3:313:6 | access to array | 313:c3-c6 314:c3-c6 | | test.cpp:315:3:315:3 | y | 315:c3-c3 316:c3-c3 | +| test.cpp:323:3:323:11 | test_18_p | 323:c3-c11 324:c3-c11 | +| test.cpp:323:3:323:13 | call to expression | 323:c3-c13 324:c3-c13 | +| test.cpp:327:3:327:11 | test_19_p | 327:c3-c11 328:c3-c11 329:c3-c11 | +| test.cpp:327:3:327:17 | call to expression | 327:c3-c17 328:c3-c17 | +| test.cpp:327:13:327:13 | x | 327:c13-c13 328:c13-c13 329:c16-c16 | +| test.cpp:327:16:327:16 | y | 327:c16-c16 328:c16-c16 329:c13-c13 | +| test.cpp:333:3:333:8 | ... == ... | 333:c3-c8 334:c3-c8 336:c3-c8 | +| test.cpp:333:3:333:16 | ... ? ... : ... | 333:c3-c16 334:c3-c16 | +| test.cpp:333:12:333:12 | x | 333:c12-c12 333:c3-c3 334:c12-c12 334:c3-c3 335:c12-c12 335:c8-c8 336:c16-c16 336:c3-c3 | +| test.cpp:333:16:333:16 | y | 333:c16-c16 333:c8-c8 334:c16-c16 334:c8-c8 335:c16-c16 335:c3-c3 336:c12-c12 336:c8-c8 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 5dcbf848921..40635ed268c 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -315,3 +315,23 @@ void test19(int *x, int *y) { y[0]; y[1]; } + +void test20(int *x, int *y) { + void (*test_18_p)() = &test18; + void (*test_17_p)() = &test17; + void (*test_19_p)(int *, int *) = &test19; + test_18_p(); + test_18_p(); + test_17_p(); + + test_19_p(x, y); + test_19_p(x, y); + test_19_p(y, x); +} + +void test21(int x, int y) { + x == y ? x : y; + x == y ? x : y; + y == x ? x : y; + x == y ? y : x; +}