diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index ee894f95648f..008a7c51c8e7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -1220,6 +1220,7 @@ module ConstructorForwarding { (t1.isVolatile() implies t2.isVolatile()) } + bindingset[arg] private predicate baseTypeCompatible(Type arg, Type param) { param.stripTopLevelSpecifiers() = arg.stripTopLevelSpecifiers().(Class).getABaseClass+() and preservesQualifiers(arg, param) @@ -1229,6 +1230,7 @@ module ConstructorForwarding { private predicate routineType(Type t) { t.stripTopLevelSpecifiers() instanceof RoutineType } + bindingset[arg] private predicate pointerConversion(Cpp::PointerType arg, Cpp::PointerType param) { exists(Type argBase, Type paramBase | argBase = arg.getBaseType() and @@ -1256,6 +1258,7 @@ module ConstructorForwarding { t instanceof RoutineType } + bindingset[t, category] private predicate referenceAcceptsCategory(Cpp::ReferenceType t, ValueCategory category) { if t instanceof Cpp::LValueReferenceType then @@ -1285,19 +1288,13 @@ module ConstructorForwarding { private predicate isUnderlyingType(Type type) { type = type.getUnderlyingType() } - private newtype TTypeState = - MkTypeState(Type type, ValueCategory category, Boolean conversionUsed, Phase phase) { - not type instanceof Cpp::ReferenceType and - not type instanceof FunctionReferenceType and - isUnderlyingType(type) - } - private ValueCategory getCategoryForRef(Cpp::ReferenceType reference) { reference instanceof Cpp::LValueReferenceType and result = LValue() or reference instanceof Cpp::RValueReferenceType and result = XValue() } + bindingset[t] private Type getValueType(Type t, ValueCategory category) { result = t.(FunctionReferenceType).getBaseType().getUnderlyingType() and category = LValue() @@ -1311,71 +1308,33 @@ module ConstructorForwarding { category = PRValue() } - bindingset[arg, param] - pragma[inline_late] + bindingset[arg] private predicate qualificationCompatible(Type arg, Type param) { arg.stripTopLevelSpecifiers() = param.stripTopLevelSpecifiers() and preservesQualifiers(arg, param) } - private class TypeState extends TTypeState { - Type getType() { this = MkTypeState(result, _, _, _) } - - ValueCategory getCategory() { this = MkTypeState(_, result, _, _) } - - Phase getPhase() { this = MkTypeState(_, _, _, result) } - - /** Holds if this state's phase permits a value or base-class conversion. */ - predicate canConvertValue() { - this.getPhase() = Initial() or this.getPhase() = AfterTransformation() - } - - predicate hasNotUsedConversion() { this = MkTypeState(_, _, false, _) } - - string toString() { result = this.getType().toString() } - - predicate isSource(Type argType) { - argType = getForwardedArgumentType(_, _, _) and - exists(ValueCategory category | - this = MkTypeState(getValueType(argType, category), category, false, Initial()) - ) - } - - predicate matchesParameter(Type paramType) { - exists(Type base | base = paramType.(Cpp::ReferenceType).getBaseType() | - qualificationCompatible(this.getType(), base) and - referenceAcceptsCategory(paramType, this.getCategory()) - ) - or - not paramType instanceof Cpp::ReferenceType and - this.getType().stripTopLevelSpecifiers() = paramType.stripTopLevelSpecifiers() - } - - predicate isSink(Type paramType, Cpp::Constructor constructor) { - paramType = getConstructorParameterType(constructor, _) and - this.matchesParameter(paramType) - } + bindingset[argType, category] + private predicate matchesParameter(Type argType, ValueCategory category, Type paramType) { + exists(Type base | base = paramType.(Cpp::ReferenceType).getBaseType() | + qualificationCompatible(argType, base) and + referenceAcceptsCategory(paramType, category) + ) + or + not paramType instanceof Cpp::ReferenceType and + argType.stripTopLevelSpecifiers() = paramType.stripTopLevelSpecifiers() } private Cpp::PointerType pointerType(Type base) { result.getBaseType() = base.getUnderlyingType() } - private predicate arrayToPointerStep(TypeState argState, TypeState paramState) { - exists(Cpp::ArrayType array, boolean conversionUsed | - argState = MkTypeState(array, _, conversionUsed, Initial()) and - paramState = - MkTypeState(pointerType(array.getBaseType()), PRValue(), conversionUsed, - AfterTransformation()) - ) + private predicate arrayToPointerStep(Cpp::ArrayType array, Cpp::PointerType pointer) { + pointer = pointerType(array.getBaseType()) } - private predicate functionToPointerStep(TypeState argState, TypeState paramState) { - exists(RoutineType routine, FunctionPointerType pointer, boolean conversionUsed | - argState = MkTypeState(routine, _, conversionUsed, Initial()) and - routine = pointer.getBaseType().getUnderlyingType() and - paramState = MkTypeState(pointer, PRValue(), conversionUsed, AfterTransformation()) - ) + private predicate functionToPointerStep(RoutineType routine, FunctionPointerType pointer) { + routine = pointer.getBaseType().getUnderlyingType() } private predicate arithmeticConversion(Type t) { @@ -1386,14 +1345,12 @@ module ConstructorForwarding { private predicate hasNoTopLevelSpecifiers(Type type) { type = type.stripTopLevelSpecifiers() } - private predicate valueConversionStep(TypeState argState, TypeState paramState) { - exists(Type argType, Type paramType, boolean conversionUsed | - argState = MkTypeState(_, _, conversionUsed, _) and - argState.canConvertValue() and - argType = argState.getType().stripTopLevelSpecifiers() and + bindingset[type] + private predicate valueConversionStep(Type type, Type paramType) { + exists(Type argType | + argType = type.stripTopLevelSpecifiers() and hasNoTopLevelSpecifiers(paramType) and - argType != paramType and - paramState = MkTypeState(paramType, PRValue(), conversionUsed, AfterValueConversion()) + argType != paramType | paramType instanceof ArithmeticType and arithmeticConversion(argType) @@ -1420,86 +1377,151 @@ module ConstructorForwarding { k = FunPtrKind() } - private predicate pointerQualificationStep(TypeState argState, TypeState paramState) { - exists(Type argType, Type paramType, boolean conversionUsed, PtrKind k | - argState = MkTypeState(_, _, conversionUsed, _) and - argState.getPhase() != AfterQualification() and - argType = argState.getType().stripTopLevelSpecifiers() and + bindingset[type] + private predicate pointerQualificationStep(Type type, Type paramType) { + exists(Type argType, PtrKind k | + argType = type.stripTopLevelSpecifiers() and hasNoTopLevelSpecifiers(paramType) and argType != paramType and - qualificationCompatible(pointerBase(argType, k), pointerBase(paramType, k)) and - paramState = MkTypeState(paramType, PRValue(), conversionUsed, AfterQualification()) + qualificationCompatible(pointerBase(argType, pragma[only_bind_into](k)), + pointerBase(paramType, pragma[only_bind_into](k))) ) } - private predicate baseClassStep(TypeState argState, TypeState paramState) { - exists(Type arg, Type param, ValueCategory category, boolean conversionUsed | - argState = MkTypeState(arg, category, conversionUsed, _) and - argState.canConvertValue() and - baseTypeCompatible(arg, param) and - paramState = MkTypeState(param, category, conversionUsed, AfterValueConversion()) - ) + bindingset[argType] + private predicate baseClassStep(Type argType, Type paramType) { + baseTypeCompatible(argType, paramType) } - private predicate convertingConstructorStep(TypeState argState, TypeState paramState) { + bindingset[argType, argCategory] + private predicate convertingConstructorStep( + Type argType, ValueCategory argCategory, Class paramType + ) { exists(ConvertingConstructor constructor | - argState.hasNotUsedConversion() and - argState.matchesParameter(constructor.getUnderlyingFromType()) and - paramState = MkTypeState(constructor.getDeclaringType(), PRValue(), true, Initial()) + matchesParameter(argType, argCategory, constructor.getUnderlyingFromType()) and + paramType = constructor.getDeclaringType() ) } - private predicate conversionOperatorStep(TypeState argState, TypeState paramState) { - exists(ConversionOperator conversion, ValueCategory category | + bindingset[argType] + private predicate conversionOperatorStep(Type argType, Type paramType, ValueCategory paramCategory) { + exists(ConversionOperator conversion | not conversion.isFromUninstantiatedTemplate(_) and not conversion.isExplicit() and not conversion.isDeleted() and - argState.hasNotUsedConversion() and - conversion.getSourceType() = - argState.getType().stripTopLevelSpecifiers().(Class).getABaseClass*() and - paramState = - MkTypeState(getValueType(conversion.getDestType().getUnderlyingType(), category), category, - true, Initial()) + conversion.getSourceType() = argType.stripTopLevelSpecifiers().(Class).getABaseClass*() and + paramType = getValueType(conversion.getDestType().getUnderlyingType(), paramCategory) ) } - private predicate step(TypeState argState, TypeState paramState) { - arrayToPointerStep(argState, paramState) - or - functionToPointerStep(argState, paramState) - or - valueConversionStep(argState, paramState) - or - pointerQualificationStep(argState, paramState) - or - baseClassStep(argState, paramState) - or - convertingConstructorStep(argState, paramState) - or - conversionOperatorStep(argState, paramState) - } - - private predicate typeFwd(TypeState state) { - state.isSource(_) + bindingset[argType, argCategory, argConversionUsed, argPhase] + pragma[inline_late] + private predicate typeStep( + Type argType, ValueCategory argCategory, boolean argConversionUsed, Phase argPhase, + Type paramType, ValueCategory paramCategory, boolean paramConversionUsed, Phase paramPhase + ) { + paramConversionUsed = argConversionUsed and + ( + argPhase = Initial() and + paramCategory = PRValue() and + paramPhase = AfterTransformation() and + ( + arrayToPointerStep(argType, paramType) + or + functionToPointerStep(argType, paramType) + ) + or + (argPhase = Initial() or argPhase = AfterTransformation()) and + paramPhase = AfterValueConversion() and + ( + valueConversionStep(argType, paramType) and paramCategory = PRValue() + or + baseClassStep(argType, paramType) and paramCategory = argCategory + ) + or + argPhase != AfterQualification() and + pointerQualificationStep(argType, paramType) and + paramCategory = PRValue() and + paramPhase = AfterQualification() + ) or - exists(TypeState previous | - typeFwd(previous) and - step(previous, state) + argConversionUsed = false and + paramConversionUsed = true and + paramPhase = Initial() and + ( + convertingConstructorStep(argType, argCategory, paramType) and paramCategory = PRValue() + or + conversionOperatorStep(argType, paramType, paramCategory) ) } - private predicate typeRev(TypeState state, Cpp::Constructor constructor) { - typeFwd(state) and + private predicate typeFwd(Type type, ValueCategory category, boolean conversionUsed, Phase phase) { + not type instanceof Cpp::ReferenceType and + not type instanceof FunctionReferenceType and + isUnderlyingType(type) and ( - state.isSink(_, constructor) + type = getValueType(getForwardedArgumentType(_, _, _), category) and + conversionUsed = false and + phase = Initial() or - exists(TypeState next | - typeRev(next, constructor) and - step(state, next) + exists( + Type previousType, ValueCategory previousCategory, boolean previousConversionUsed, + Phase previousPhase + | + typeFwd(previousType, previousCategory, previousConversionUsed, previousPhase) and + typeStep(previousType, previousCategory, previousConversionUsed, previousPhase, type, + category, conversionUsed, phase) ) ) } + private newtype TTypeState = + MkTypeState(Type type, ValueCategory category, Boolean conversionUsed, Phase phase) { + typeFwd(type, category, conversionUsed, phase) + } + + private class TypeState extends TTypeState { + Type getType() { this = MkTypeState(result, _, _, _) } + + ValueCategory getCategory() { this = MkTypeState(_, result, _, _) } + + Phase getPhase() { this = MkTypeState(_, _, _, result) } + + string toString() { result = this.getType().toString() } + + predicate isSource(Type argType) { + argType = getForwardedArgumentType(_, _, _) and + exists(ValueCategory category | + this = MkTypeState(getValueType(argType, category), category, false, Initial()) + ) + } + + predicate isSink(Type paramType, Cpp::Constructor constructor) { + paramType = getConstructorParameterType(constructor, _) and + matchesParameter(this.getType(), this.getCategory(), paramType) + } + } + + private predicate step(TypeState argState, TypeState paramState) { + exists( + Type t1, Type t2, ValueCategory vc1, ValueCategory vc2, boolean b1, boolean b2, Phase p1, + Phase p2 + | + argState = MkTypeState(t1, vc1, b1, p1) and + paramState = MkTypeState(t2, vc2, b2, p2) and + typeStep(t1, vc1, b1, p1, t2, vc2, b2, p2) + ) + } + + private predicate typeRev(TypeState state, Cpp::Constructor constructor) { + state.isSink(_, constructor) + or + exists(TypeState next | + typeRev(next, constructor) and + step(state, next) + ) + } + private predicate prunedStep(TypeState argState, TypeState paramState) { exists(Cpp::Constructor constructor | typeRev(argState, constructor) and