| description | Guidance for developing CodeQL queries targeting C++ code |
|---|
This prompt provides guidance for developing CodeQL queries targeting C++ code. For common query development patterns and best practices, see query_development.prompt.md.
- Import
cppfor C++ AST nodes and predicates - Common imports:
Stmt,Expr,Function,Class,Type,Variable - Use
DataFlowandTaintTrackingfor data flow analysis - Import
semmle.code.cpp.securityfor security-related predicates
- Functions:
TopLevelFunction,MemberFunction,Constructor,Destructor - Operators:
CopyAssignmentOperator,MoveAssignmentOperator - Statements:
BlockStmt,DeclStmt,ExprStmt,ReturnStmt - Expressions:
FunctionCall,VariableAccess,AssignExpr,Literal - Declarations:
VariableDeclarationEntry,Parameter - Types:
IntType,VoidType,PointerType,LValueReferenceType,RValueReferenceType - Control Flow: Function entry points via
getEntryPoint() - Value Categories:
prvalue,lvaluefor expression value categories
- Track
new/deletepairs for memory leaks - Check
malloc/freeusage patterns - Analyze smart pointer usage:
unique_ptr,shared_ptr,weak_ptr - RAII patterns and resource management
- Stack vs heap allocation patterns
- Function calls:
call.getTarget().hasName("functionName") - Method calls:
call.getTarget().(MemberFunction).hasName("method") - Class inheritance:
derived.getABaseClass() = base - Template instantiations: Use
TemplateInstantiationand related predicates - Operator overloading: Check for overloaded operators
- Virtual function calls: Track polymorphic dispatch
- Exception handling:
ThrowStmt,TryStmt,CatchBlock
- Use
DataFlow::Nodefor nodes in the data flow graph TaintTracking::Configurationfor taint analysis- Handle pointer aliasing and reference semantics
- Track through function parameters and return values
- Consider const-correctness in flow analysis
Local vs Global Data Flow:
// Local data flow within function scope
DataFlow::localFlow(source, sink)
DataFlow::localExprFlow(expr1, expr2)
// Global data flow across function boundaries
module GlobalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { ... }
predicate isSink(DataFlow::Node sink) { ... }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { ... }
}
module GlobalFlow = DataFlow::Make<GlobalConfig>;Flow-State for Tracking Validation Context:
// Define flow states using algebraic data types
newtype TValidationState =
TUnvalidated() or
TValidated(TypeValidationCall call)
// Stateful data flow configuration
module StatefulConfig implements DataFlow::StateConfigSig {
class FlowState = TValidationState;
predicate isSource(DataFlow::Node source, FlowState state) { ... }
predicate isSink(DataFlow::Node sink, FlowState state) { ... }
predicate isAdditionalFlowStep(DataFlow::Node node1, FlowState state1,
DataFlow::Node node2, FlowState state2) { ... }
predicate isBarrier(DataFlow::Node node, FlowState state) { ... }
}
module StatefulFlow = DataFlow::MakeWithState<StatefulConfig>;Multiple Data Flow Configurations:
// Separate configs for different aspects
module InputFlow = DataFlow::Make<InputFlowConfig>;
module TypeValidationFlow = DataFlow::Make<TypeValidationConfig>;
// Avoid non-monotonic recursion by careful sink definition
predicate typeValidationGuard(GuardCondition guard, Expr other, BasicBlock block) {
// Use TypeValidationFlow::hasFlowToExpr() to validate flow
TypeValidationFlow::hasFlowToExpr(other) and
guard.ensuresEq(_, other, 0, block, true)
}- Buffer overflows: Array bounds checking, string operations
- Use after free: Pointer usage after deallocation
- Double free: Multiple deallocations of same memory
- Null pointer dereference: Unguarded pointer usage
- Integer overflows: Arithmetic operations on integers
- Format string vulnerabilities:
printffamily functions - Race conditions: Multi-threading without proper synchronization
- Injection attacks: Command injection, SQL injection through C++ APIs
Guard Condition Analysis for Type Validation:
// Model type validation checks with macro calls
class TypeValidationCall extends FunctionCall {
TypeValidationCall() { this.getTarget().hasName("VALIDATE_TYPE") }
int getExpectedType(int index) {
result = this.getArgument(index).getValue().toInt()
}
}
// Guard conditions ensuring type safety
predicate typeValidationGuard(GuardCondition guard, TypeValidationCall call,
Expr other, BasicBlock block) {
exists(Expr dest |
DataFlow::localExprFlow(call, dest) and
guard.ensuresEq(dest, other, 0, block, true)
)
}Union Type Safety Analysis:
// Detect unsafe union field access
class UnionAccess extends FieldAccess {
UnionAccess() {
this.getQualifier().getType().stripType() instanceof Union
}
predicate isUnsafeAccess() {
// Check if proper type validation exists
not exists(TypeValidationCall call, GuardCondition guard |
typeValidationGuard(guard, call, _, this.getBasicBlock()) and
// Match expected type with actual field access
matchesExpectedType(call, this)
)
}
}Inter-procedural Validation Analysis:
// Track validation across function boundaries
predicate typeValidationGuardOrIndirect(GuardCondition guard, TypeValidationCall call,
Expr other, BasicBlock block) {
typeValidationGuard(guard, call, other, block)
or
exists(FunctionCall fc |
fc.getBasicBlock() = block and
typeValidationGuardOrIndirect(guard, call, other,
fc.getTarget().getACallToThisFunction().getBasicBlock())
)
}Missing Flow Step Detection:
// Use partial flow for debugging missing edges
import DataFlow::Impl::FlowExploration as FlowExploration
import FlowExploration::PartialPathGraph
// Additional flow steps for custom patterns
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// Model return value flow for wrapper functions
exists(FunctionCall call |
call.getTarget().hasName("lock_input") and
call.getArgument(0) = node1.asExpr() and
call = node2.asExpr()
)
}- STL containers:
vector,string,map,setoperations - Iterators: Iterator usage and invalidation
- Algorithms:
std::find,std::sort, lambda expressions - Smart pointers: Modern C++ memory management
- Threading:
std::thread,std::mutex,std::atomic - I/O streams:
iostream, file operations
- Templates: Template metaprogramming, SFINAE
- Lambda expressions: Capture lists, return type deduction
- Move semantics:
std::move, rvalue references - Constexpr: Compile-time evaluation
- Auto type deduction: Modern C++ type inference
Using Partial Flow for Missing Edges:
// Import partial flow exploration
import DataFlow::Impl::FlowExploration as FlowExploration
import FlowExploration::PartialPathGraph
// Use in query to debug missing flow paths
from FlowExploration::PartialPathNode source, FlowExploration::PartialPathNode sink
where FlowExploration::hasPartialFlow(source, sink, _)
select source, sink, "Partial flow from source to sink"Path-Sensitive vs Path-Insensitive Analysis:
// Path-sensitive: Use flow-state to track validation context
// Path-insensitive: Simple guard condition checks may miss cases
// Path-sensitive with barriers
predicate isBarrier(DataFlow::Node node, FlowState state) {
exists(TypeValidationCall call |
typeValidationGuard(_, call, _, node.asExpr().getBasicBlock()) and
(
state instanceof UnvalidatedState or
state.(ValidatedState).getCall() != call
)
)
}Entry Point Modeling:
// Model functions that aren't called internally
class EntryPointFunction extends Function {
EntryPointFunction() {
this.hasName(["EP_example", "EP_main"]) or
// Heuristic: functions with specific signatures not called internally
(
this.getNumberOfParameters() = 3 and
this.getParameter(0).getType().toString().matches("%input%") and
not exists(FunctionCall call | call.getTarget() = this)
)
}
Parameter getInputParameter() { result = this.getParameter(0) }
Parameter getInputTypesParameter() { result = this.getParameter(1) }
}Complex Guard Conditions:
// Handle macro-based type validation
class MacroTypeValidation extends Expr {
MacroTypeValidation() {
// Match DYN_INPUT_TYPE(type1, type2) patterns
this.toString().regexpMatch("DYN_INPUT_TYPE\\s*\\(.*\\)")
}
int getTypeForIndex(int index) {
// Extract type constants from macro arguments
exists(Expr arg |
arg = this.getChild(index) and
result = arg.getValue().toInt()
)
}
}For detailed C++ analysis patterns and implementation guides:
- C++ AST Reference - Comprehensive C++ AST node patterns and navigation
- C++ Security Query Guide - Advanced security analysis patterns including flow-state, union type safety, and debugging techniques
- qlt query generate new-query - Generate scaffolding for a new CodeQL query with packs and tests
- codeql query format
- codeql query compile
- codeql query run
- codeql test run