From e0f62b722b6d895e41579e62ad883f1e9c8c5c79 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 11 Apr 2025 19:33:35 +0100 Subject: [PATCH 1/5] C++: Instantiate model generation library. --- .../modelgenerator/internal/CaptureModels.qll | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll diff --git a/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll new file mode 100644 index 000000000000..6439cd467492 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -0,0 +1,388 @@ +/** + * Provides predicates related to capturing summary models of the Standard or a 3rd party library. + */ + +private import cpp +private import semmle.code.cpp.dataflow.new.DataFlow +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon +private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific +private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate +private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl +private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific +private import semmle.code.cpp.dataflow.new.TaintTracking +private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl + +module ModelGeneratorInput implements ModelGeneratorInputSig { + class Type = DataFlowPrivate::DataFlowType; + + // Note: This also includes `this` + class Parameter = DataFlow::ParameterNode; + + class Callable = Declaration; + + class NodeExtended extends DataFlow::Node { + Callable getAsExprEnclosingCallable() { result = this.asExpr().getEnclosingDeclaration() } + } + + Parameter asParameter(NodeExtended n) { result = n } + + Callable getEnclosingCallable(NodeExtended n) { + result = n.getEnclosingCallable().asSourceCallable() + } + + Callable getAsExprEnclosingCallable(NodeExtended n) { + result = n.asExpr().getEnclosingDeclaration() + } + + private predicate hasManualSummaryModel(Callable api) { + api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()) or + api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()) + } + + private predicate hasManualSourceModel(Callable api) { + api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()) + } + + private predicate hasManualSinkModel(Callable api) { + api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()) + } + + private predicate relevant(Callable api) { api.fromSource() } + + class SummaryTargetApi extends Callable { + private Callable lift; + + SummaryTargetApi() { + lift = this and + not hasManualSummaryModel(lift) + } + + Callable lift() { result = lift } + + predicate isRelevant() { + relevant(this) and + not hasManualSummaryModel(this) + } + } + + class SourceOrSinkTargetApi extends Callable { + SourceOrSinkTargetApi() { relevant(this) } + } + + class SinkTargetApi extends SourceOrSinkTargetApi { + SinkTargetApi() { not hasManualSinkModel(this) } + } + + class SourceTargetApi extends SourceOrSinkTargetApi { + SourceTargetApi() { not hasManualSourceModel(this) } + } + + class InstanceParameterNode extends DataFlow::ParameterNode { + InstanceParameterNode() { + DataFlowPrivate::nodeHasInstruction(this, + any(InitializeParameterInstruction i | i.hasIndex(-1)), 1) + } + } + + /** + * Holds if the summary generated for `c` should also apply to overrides + * of `c`. + */ + private string isExtensible(Callable c) { + if c instanceof MemberFunction then result = "true" else result = "false" + } + + /** + * Gets the string representing the list of template parameters declared + * by `template`. + * + * `template` must either be: + * - An uninstantiated template, or + * - A declaration that is not from a template instantiation. + */ + private string templateParams(Declaration template) { + exists(string params | + params = + concat(int i | + | + template.getTemplateArgument(i).(TypeTemplateParameter).getName(), "," order by i + ) + | + if params = "" then result = "" else result = "<" + params + ">" + ) + } + + /** + * Gets the string representing the list of parameters declared + * by `functionTemplate`. + * + * `functionTemplate` must either be: + * - An uninstantiated template, or + * - A declaration that is not from a template instantiation. + */ + private string params(Function functionTemplate) { + exists(string params | + params = + concat(int i | + | + ExternalFlow::getParameterTypeWithoutTemplateArguments(functionTemplate, i, true), "," + order by + i + ) + | + if params = "" then result = "()" else result = "(" + params + ")" + ) + } + + /** + * Holds if the callable `c` is: + * - In the namespace represented by `namespace`, and + * - Has a declaring type represented by `type`, and + * - Has the name `name`, and + * - Has a list of parameters represented by `params` + * + * This is the predicate that computes the columns that it put into the MaD + * row for `callable`. + */ + private predicate qualifiedName( + Callable callable, string namespace, string type, string name, string params + ) { + exists( + Function functionTemplate, string typeWithoutTemplateArgs, string nameWithoutTemplateArgs + | + functionTemplate = ExternalFlow::getFullyTemplatedFunction(callable) and + functionTemplate.hasQualifiedName(namespace, typeWithoutTemplateArgs, nameWithoutTemplateArgs) and + nameWithoutTemplateArgs = functionTemplate.getName() and + name = nameWithoutTemplateArgs + templateParams(functionTemplate) and + params = params(functionTemplate) + | + exists(Class classTemplate | + classTemplate = functionTemplate.getDeclaringType() and + type = typeWithoutTemplateArgs + templateParams(classTemplate) + ) + or + not exists(functionTemplate.getDeclaringType()) and + type = "" + ) + } + + predicate isRelevantType(Type t) { any() } + + Type getUnderlyingContentType(DataFlow::ContentSet c) { + result = c.(DataFlow::FieldContent).getField().getUnspecifiedType() or + result = c.(DataFlow::UnionContent).getUnion().getUnspecifiedType() + } + + string qualifierString() { result = "Argument[-1]" } + + private predicate parameterContentAccessImpl(Parameter p, string argument) { + exists(int indirectionIndex, int argumentIndex, DataFlowPrivate::Position pos | + p.isSourceParameterOf(_, pos) and + pos.getArgumentIndex() = argumentIndex and + argumentIndex != -1 and // handled elsewhere + pos.getIndirectionIndex() = indirectionIndex + | + indirectionIndex = 0 and + argument = "Argument[" + argumentIndex + "]" + or + indirectionIndex > 0 and + argument = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]" + ) + } + + string parameterAccess(Parameter p) { parameterContentAccessImpl(p, result) } + + string parameterContentAccess(Parameter p) { parameterContentAccessImpl(p, result) } + + bindingset[c] + string paramReturnNodeAsOutput(Callable c, DataFlowPrivate::Position pos) { + exists(Parameter p | + p.isSourceParameterOf(c, pos) and + result = parameterAccess(p) + ) + or + pos.getArgumentIndex() = -1 and + result = qualifierString() and + pos.getIndirectionIndex() = 1 + } + + bindingset[c] + string paramReturnNodeAsContentOutput(Callable c, DataFlowPrivate::ParameterPosition pos) { + result = paramReturnNodeAsOutput(c, pos) + } + + pragma[nomagic] + Callable returnNodeEnclosingCallable(DataFlow::Node ret) { + result = DataFlowImplCommon::getNodeEnclosingCallable(ret).asSourceCallable() + } + + /** Holds if this instance access is to an enclosing instance of type `t`. */ + pragma[nomagic] + private predicate isEnclosingInstanceAccess(DataFlowPrivate::ReturnNode n, Class t) { + n.getKind().isIndirectReturn(-1) and + t = n.getType().stripType() and + t != n.getEnclosingCallable().asSourceCallable().(Function).getDeclaringType() + } + + pragma[nomagic] + predicate isOwnInstanceAccessNode(DataFlowPrivate::ReturnNode node) { + node.getKind().isIndirectReturn(-1) and + not isEnclosingInstanceAccess(node, _) + } + + predicate sinkModelSanitizer(DataFlow::Node node) { none() } + + predicate apiSource(DataFlow::Node source) { + DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) + or + source instanceof DataFlow::ParameterNode + } + + string getInputArgument(DataFlow::Node source) { + exists(DataFlowPrivate::Position pos, int argumentIndex, int indirectionIndex | + source.(DataFlow::ParameterNode).isParameterOf(_, pos) and + argumentIndex = pos.getArgumentIndex() and + indirectionIndex = pos.getIndirectionIndex() and + result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]" + ) + or + DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and + result = qualifierString() + } + + string getReturnValueString(DataFlowPrivate::ReturnKind k) { + k.isNormalReturn() and + exists(int indirectionIndex | indirectionIndex = k.getIndirectionIndex() | + indirectionIndex = 0 and + result = "ReturnValue" + or + indirectionIndex > 0 and + result = "ReturnValue[" + DataFlow::repeatStars(indirectionIndex) + "]" + ) + } + + predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() } + + bindingset[kind] + predicate isRelevantSourceKind(string kind) { any() } + + bindingset[kind] + predicate isRelevantSinkKind(string kind) { any() } + + predicate containerContent(DataFlow::ContentSet cs) { cs instanceof DataFlow::ElementContent } + + predicate isAdditionalContentFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + TaintTracking::defaultAdditionalTaintStep(node1, node2, _) and + not exists(DataFlow::Content f | + DataFlowPrivate::readStep(node1, f, node2) and containerContent(f) + ) + } + + predicate isField(DataFlow::ContentSet cs) { + exists(DataFlow::Content c | cs.isSingleton(c) | + c instanceof DataFlow::FieldContent or + c instanceof DataFlow::UnionContent + ) + } + + predicate isCallback(DataFlow::ContentSet c) { none() } + + string getSyntheticName(DataFlow::ContentSet c) { + exists(Field f | + not f.isPublic() and + f = c.(DataFlow::FieldContent).getField() and + result = f.getName() + ) + } + + string printContent(DataFlow::ContentSet c) { + exists(int indirectionIndex, string name, string kind | + exists(DataFlow::UnionContent uc | + c.isSingleton(uc) and + name = uc.getUnion().getName() and + indirectionIndex = uc.getIndirectionIndex() and + // Note: We don't actually support the union string in MaD, but we should do that eventually + kind = "Union[" + ) + or + exists(DataFlow::FieldContent fc | + c.isSingleton(fc) and + name = fc.getField().getName() and + indirectionIndex = fc.getIndirectionIndex() and + kind = "Field[" + ) + | + result = kind + DataFlow::repeatStars(indirectionIndex) + name + "]" + ) + or + exists(DataFlow::ElementContent ec | + c.isSingleton(ec) and + result = "Element[" + ec.getIndirectionIndex() + "]" + ) + } + + /** + * Holds if `f` is a "private" function. + * + * A "private" function does not contribute any models as it is assumed + * to be an implementation detail of some other "public" function for which + * we will generate a summary. + */ + private predicate isPrivate(Function f) { + f.getNamespace().getParentNamespace*().isAnonymous() + or + f.(MemberFunction).isPrivate() + or + f.isStatic() + } + + predicate isUninterestingForDataFlowModels(Callable api) { + // Note: This also makes all global/static-local variables + // uninteresting (which is good!) + not api.(Function).hasDefinition() + or + isPrivate(api) + or + api instanceof Destructor + or + api = any(LambdaExpression lambda).getLambdaFunction() + or + api.isFromUninstantiatedTemplate(_) + } + + predicate isUninterestingForHeuristicDataFlowModels(Callable api) { + isUninterestingForDataFlowModels(api) + } + + string partialModelRow(Callable api, int i) { + i = 0 and qualifiedName(api, result, _, _, _) // namespace + or + i = 1 and qualifiedName(api, _, result, _, _) // type + or + i = 2 and result = isExtensible(api) // extensible + or + i = 3 and qualifiedName(api, _, _, result, _) // name + or + i = 4 and qualifiedName(api, _, _, _, result) // parameters + or + i = 5 and result = "" and exists(api) // ext + } + + string partialNeutralModelRow(Callable api, int i) { + i = 0 and qualifiedName(api, result, _, _, _) // namespace + or + i = 1 and qualifiedName(api, _, result, _, _) // type + or + i = 2 and qualifiedName(api, _, _, result, _) // name + or + i = 3 and qualifiedName(api, _, _, _, result) // parameters + } + + predicate sourceNode = ExternalFlow::sourceNode/2; + + predicate sinkNode = ExternalFlow::sinkNode/2; +} + +import MakeModelGenerator From e7010921a336a74634ff5c10c36f5f629ba903de Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 11 Apr 2025 19:34:09 +0100 Subject: [PATCH 2/5] C++: Add tests that will soon succeed. --- .../modelgenerator/dataflow/summaries.cpp | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp new file mode 100644 index 000000000000..3f4018128590 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp @@ -0,0 +1,130 @@ +using size_t = decltype(sizeof(int)); + +size_t strlen(const char* str); +char* strcpy(char* dest, const char* src); + +namespace Models { + struct BasicFlow { + int* tainted; + + //No model as destructors are excluded from model generation. + ~BasicFlow() = default; + + //summary=Models;BasicFlow;true;returnThis;(int *);;Argument[-1];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnThis;(int *);;Argument[-1];ReturnValue[*];value;dfc-generated + BasicFlow* returnThis(int* input) { + return this; + } + + //summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[0];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[*0];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[0];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[*0];ReturnValue[*];value;dfc-generated + int* returnParam0(int* input0, int* input1) { + return input0; + } + + //summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[1];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[*1];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[1];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[*1];ReturnValue[*];value;dfc-generated + int* returnParam1(int* input0, int* input1) { + return input1; + } + + //summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[1];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*1];ReturnValue[*];taint;df-generated + //summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[2];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*2];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[1];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*1];ReturnValue[*];value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[2];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*2];ReturnValue[*];value;dfc-generated + int* returnParamMultiple(bool b, int* input0, int* input1) { + return b ? input0 : input1; + } + + //summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];Argument[*1];taint;df-generated + //summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];ReturnValue[*];taint;df-generated + //summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];ReturnValue[*];taint;df-generated + //summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[1];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];Argument[*1];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];Argument[*1];taint;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];ReturnValue[*];taint;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];ReturnValue[*];value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[1];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];Argument[*1];value;dfc-generated + char* returnSubstring(const char* source, char* dest) { + return strcpy(dest, source + 1); + } + + //summary=Models;BasicFlow;true;setField;(int *);;Argument[0];Argument[-1];taint;df-generated + //summary=Models;BasicFlow;true;setField;(int *);;Argument[*0];Argument[-1];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;setField;(int *);;Argument[0];Argument[-1].Field[*tainted];value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;setField;(int *);;Argument[*0];Argument[-1].Field[**tainted];value;dfc-generated + void setField(int* s) { + tainted = s; + } + + //summary=Models;BasicFlow;true;returnField;();;Argument[-1];ReturnValue;taint;df-generated + //summary=Models;BasicFlow;true;returnField;();;Argument[-1];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;BasicFlow;true;returnField;();;Argument[-1].Field[*tainted];ReturnValue;value;dfc-generated + //contentbased-summary=Models;BasicFlow;true;returnField;();;Argument[-1].Field[**tainted];ReturnValue[*];value;dfc-generated + int* returnField() { + return tainted; + } + }; + + template + struct TemplatedFlow { + T tainted; + + //summary=Models;TemplatedFlow;true;template_returnThis;(T);;Argument[-1];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;TemplatedFlow;true;template_returnThis;(T);;Argument[-1];ReturnValue[*];value;dfc-generated + TemplatedFlow* template_returnThis(T input) { + return this; + } + + //summary=Models;TemplatedFlow;true;template_returnParam0;(T *,T *);;Argument[0];ReturnValue;taint;df-generated + //summary=Models;TemplatedFlow;true;template_returnParam0;(T *,T *);;Argument[*0];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;TemplatedFlow;true;template_returnParam0;(T *,T *);;Argument[0];ReturnValue;value;dfc-generated + //contentbased-summary=Models;TemplatedFlow;true;template_returnParam0;(T *,T *);;Argument[*0];ReturnValue[*];value;dfc-generated + T* template_returnParam0(T* input0, T* input1) { + return input0; + } + + //summary=Models;TemplatedFlow;true;template_setField;(T);;Argument[0];Argument[-1];taint;df-generated + //contentbased-summary=Models;TemplatedFlow;true;template_setField;(T);;Argument[0];Argument[-1].Field[*tainted];value;dfc-generated + void template_setField(T s) { + tainted = s; + } + + //summary=Models;TemplatedFlow;true;template_returnField;();;Argument[-1];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;TemplatedFlow;true;template_returnField;();;Argument[-1].Field[*tainted];ReturnValue[*];value;dfc-generated + T& template_returnField() { + return tainted; + } + + //summary=Models;TemplatedFlow;true;templated_function;(U *,T *);;Argument[0];ReturnValue;taint;df-generated + //summary=Models;TemplatedFlow;true;templated_function;(U *,T *);;Argument[*0];ReturnValue[*];taint;df-generated + //contentbased-summary=Models;TemplatedFlow;true;templated_function;(U *,T *);;Argument[0];ReturnValue;value;dfc-generated + //contentbased-summary=Models;TemplatedFlow;true;templated_function;(U *,T *);;Argument[*0];ReturnValue[*];value;dfc-generated + template + U* templated_function(U* u, T* t) { + return u; + } + }; + + void test_templated_flow() { + // Ensure that we have an instantiation of the templated class + TemplatedFlow intFlow; + intFlow.template_returnThis(0); + + intFlow.template_returnParam0(nullptr, nullptr); + + intFlow.template_setField(0); + intFlow.template_returnField(); + + intFlow.templated_function(nullptr, nullptr); + } +} \ No newline at end of file From 37e91fd7bb9acb6e82f5f89f344614575420c1ae Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 11 Apr 2025 19:34:57 +0100 Subject: [PATCH 3/5] C++: Instantiate inline expectation test framework to test model generation. --- cpp/ql/lib/utils/test/InlineMadTest.qll | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 cpp/ql/lib/utils/test/InlineMadTest.qll diff --git a/cpp/ql/lib/utils/test/InlineMadTest.qll b/cpp/ql/lib/utils/test/InlineMadTest.qll new file mode 100644 index 000000000000..599dfec7d86a --- /dev/null +++ b/cpp/ql/lib/utils/test/InlineMadTest.qll @@ -0,0 +1,34 @@ +private import cpp +private import codeql.mad.test.InlineMadTest + +class MadRelevantFunction extends Function { + MadRelevantFunction() { not this.isFromUninstantiatedTemplate(_) } +} + +private module InlineMadTestLang implements InlineMadTestLangSig { + class Callable = MadRelevantFunction; + + /** + * Holds if `c` is the closest `Callable` that suceeds `comment` in the file. + */ + private predicate hasClosestCallable(CppStyleComment comment, Callable c) { + c = + min(Callable cand, int dist | + // This has no good join order, but should hopefully be good enough for tests. + cand.getFile() = comment.getFile() and + dist = cand.getLocation().getStartLine() - comment.getLocation().getStartLine() and + dist > 0 + | + cand order by dist + ) + } + + string getComment(Callable c) { + exists(CppStyleComment comment | + hasClosestCallable(comment, c) and + result = comment.getContents().suffix(2) + ) + } +} + +import InlineMadTestImpl From 8283594b0f315f0337db82903214100594a50001 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 11 Apr 2025 19:36:36 +0100 Subject: [PATCH 4/5] C++: Add copy-pasted files from C#. --- .../modelgenerator/CaptureContentSummaryModels.ql | 13 +++++++++++++ .../modelgenerator/CaptureMixedNeutralModels.ql | 13 +++++++++++++ .../modelgenerator/CaptureMixedSummaryModels.ql | 13 +++++++++++++ .../utils/modelgenerator/CaptureNeutralModels.ql | 13 +++++++++++++ .../src/utils/modelgenerator/CaptureSinkModels.ql | 13 +++++++++++++ .../utils/modelgenerator/CaptureSourceModels.ql | 13 +++++++++++++ .../utils/modelgenerator/CaptureSummaryModels.ql | 13 +++++++++++++ .../src/utils/modelgenerator/GenerateFlowModel.py | 15 +++++++++++++++ .../internal/CaptureModelsPrinting.qll | 13 +++++++++++++ .../dataflow/CaptureContentSummaryModels.expected | 2 ++ .../dataflow/CaptureContentSummaryModels.ext.yml | 6 ++++++ .../dataflow/CaptureContentSummaryModels.ql | 11 +++++++++++ .../dataflow/CaptureSummaryModels.expected | 2 ++ .../dataflow/CaptureSummaryModels.ext.yml | 6 ++++++ .../dataflow/CaptureSummaryModels.ql | 11 +++++++++++ 15 files changed, 157 insertions(+) create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureMixedNeutralModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureMixedSummaryModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureNeutralModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureSinkModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureSourceModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/CaptureSummaryModels.ql create mode 100644 cpp/ql/src/utils/modelgenerator/GenerateFlowModel.py create mode 100644 cpp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.expected create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ext.yml create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.expected create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ext.yml create mode 100644 cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ql diff --git a/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql new file mode 100644 index 000000000000..8dc0c3d7f6b1 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture content based summary models. + * @description Finds applicable content based summary models to be used by other queries. + * @kind diagnostic + * @id cpp/utils/modelgenerator/contentbased-summary-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSummaryTargetApi api, string flow +where flow = ContentSensitive::captureFlow(api, _) +select flow order by flow diff --git a/cpp/ql/src/utils/modelgenerator/CaptureMixedNeutralModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureMixedNeutralModels.ql new file mode 100644 index 000000000000..d03fa1b6030d --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureMixedNeutralModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture mixed neutral models. + * @description Finds neutral models to be used by other queries. + * @kind diagnostic + * @id cpp/utils/modelgenerator/mixed-neutral-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSummaryTargetApi api, string noflow +where noflow = captureMixedNeutral(api) +select noflow order by noflow diff --git a/cpp/ql/src/utils/modelgenerator/CaptureMixedSummaryModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureMixedSummaryModels.ql new file mode 100644 index 000000000000..dc289a07ab12 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureMixedSummaryModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture mixed summary models. + * @description Finds applicable summary models to be used by other queries. + * @kind diagnostic + * @id cpp/utils/modelgenerator/mixed-summary-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSummaryTargetApi api, string flow +where flow = captureMixedFlow(api, _) +select flow order by flow diff --git a/cpp/ql/src/utils/modelgenerator/CaptureNeutralModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureNeutralModels.ql new file mode 100644 index 000000000000..8a244cabe869 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureNeutralModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture neutral models. + * @description Finds neutral models to be used by other queries. + * @kind diagnostic + * @id cpp/utils/modelgenerator/neutral-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSummaryTargetApi api, string noflow +where noflow = captureNoFlow(api) +select noflow order by noflow diff --git a/cpp/ql/src/utils/modelgenerator/CaptureSinkModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureSinkModels.ql new file mode 100644 index 000000000000..8cbcac192a27 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureSinkModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture sink models. + * @description Finds public methods that act as sinks as they flow into a known sink. + * @kind diagnostic + * @id cpp/utils/modelgenerator/sink-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSinkTargetApi api, string sink +where sink = captureSink(api) +select sink order by sink diff --git a/cpp/ql/src/utils/modelgenerator/CaptureSourceModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureSourceModels.ql new file mode 100644 index 000000000000..b4614634ce6a --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureSourceModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture source models. + * @description Finds APIs that act as sources as they expose already known sources. + * @kind diagnostic + * @id cpp/utils/modelgenerator/source-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSourceTargetApi api, string source +where source = captureSource(api) +select source order by source diff --git a/cpp/ql/src/utils/modelgenerator/CaptureSummaryModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureSummaryModels.ql new file mode 100644 index 000000000000..ba6921351725 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/CaptureSummaryModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture summary models. + * @description Finds applicable summary models to be used by other queries. + * @kind diagnostic + * @id cpp/utils/modelgenerator/summary-models + * @tags modelgenerator + */ + +import internal.CaptureModels + +from DataFlowSummaryTargetApi api, string flow +where flow = captureFlow(api) +select flow order by flow diff --git a/cpp/ql/src/utils/modelgenerator/GenerateFlowModel.py b/cpp/ql/src/utils/modelgenerator/GenerateFlowModel.py new file mode 100644 index 000000000000..38bbaad118bd --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/GenerateFlowModel.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 + +import sys +import os.path +import subprocess + +# Add Model as Data script directory to sys.path. +gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip() +madpath = os.path.join(gitroot, "misc/scripts/models-as-data/") +sys.path.append(madpath) + +import generate_flow_model as model + +language = "cpp" +model.Generator.make(language).run() diff --git a/cpp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll b/cpp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll new file mode 100644 index 000000000000..7841f8ed1a44 --- /dev/null +++ b/cpp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll @@ -0,0 +1,13 @@ +private import cpp as Cpp +private import codeql.mad.modelgenerator.internal.ModelPrinting +private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput + +private module ModelPrintingLang implements ModelPrintingLangSig { + class Callable = Cpp::Declaration; + + predicate partialModelRow = ModelGeneratorInput::partialModelRow/2; + + predicate partialNeutralModelRow = ModelGeneratorInput::partialNeutralModelRow/2; +} + +import ModelPrintingImpl diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.expected b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.expected new file mode 100644 index 000000000000..cb6fc390349c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.expected @@ -0,0 +1,2 @@ +unexpectedModel +expectedModel diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ext.yml b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ext.yml new file mode 100644 index 000000000000..10a8a069808c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ext.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/cpp-all + extensible: summaryModel + data: + - [ "models", "ManuallyModelled", False, "hasSummary", "(void *)", "", "Argument[0]", "ReturnValue", "value", "manual"] diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql new file mode 100644 index 000000000000..22f1ba66b529 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql @@ -0,0 +1,11 @@ +import cpp +import utils.modelgenerator.internal.CaptureModels +import utils.test.InlineMadTest + +module InlineMadTestConfig implements InlineMadTestConfigSig { + string getCapturedModel(MadRelevantFunction c) { result = ContentSensitive::captureFlow(c, _) } + + string getKind() { result = "contentbased-summary" } +} + +import InlineMadTest diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.expected b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.expected new file mode 100644 index 000000000000..cb6fc390349c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.expected @@ -0,0 +1,2 @@ +unexpectedModel +expectedModel diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ext.yml b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ext.yml new file mode 100644 index 000000000000..44d895bc7e01 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ext.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/cpp-all + extensible: summaryModel + data: + - [ "Models", "ManuallyModelled", False, "hasSummary", "(void *)", "", "Argument[0]", "ReturnValue", "value", "manual"] diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ql b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ql new file mode 100644 index 000000000000..630acc295b3d --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureSummaryModels.ql @@ -0,0 +1,11 @@ +import cpp +import utils.modelgenerator.internal.CaptureModels +import utils.test.InlineMadTest + +module InlineMadTestConfig implements InlineMadTestConfigSig { + string getCapturedModel(MadRelevantFunction c) { result = captureFlow(c) } + + string getKind() { result = "summary" } +} + +import InlineMadTest From 9b5643d676a9fe4535b14c1d1b73ebb997bc2461 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 11 Apr 2025 20:01:31 +0100 Subject: [PATCH 5/5] C++: Fix ql-for-ql findings. --- cpp/ql/lib/utils/test/InlineMadTest.qll | 2 +- cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/lib/utils/test/InlineMadTest.qll b/cpp/ql/lib/utils/test/InlineMadTest.qll index 599dfec7d86a..4fbfa793fff0 100644 --- a/cpp/ql/lib/utils/test/InlineMadTest.qll +++ b/cpp/ql/lib/utils/test/InlineMadTest.qll @@ -9,7 +9,7 @@ private module InlineMadTestLang implements InlineMadTestLangSig { class Callable = MadRelevantFunction; /** - * Holds if `c` is the closest `Callable` that suceeds `comment` in the file. + * Holds if `c` is the closest `Callable` that succeeds `comment` in the file. */ private predicate hasClosestCallable(CppStyleComment comment, Callable c) { c = diff --git a/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll index 6439cd467492..d33786b2b30c 100644 --- a/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -3,7 +3,6 @@ */ private import cpp -private import semmle.code.cpp.dataflow.new.DataFlow private import semmle.code.cpp.ir.IR private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon