Skip to content

Conversation

MathiasVP
Copy link
Contributor

This PR switches C/C++ away from our own guards library and over to the shared guards library which was introduced in #19573.

The main motivation for this is two-fold: It reduces the amount of C/C++ specific QL code that needs to be maintained 1. It also enables C/C++ to instantiate the newly shared "nullness" library which was introduced in #20367. This should hopefully open the doors to better null dereference / use-after-free / double free queries.

On top of that, the improvements to the internal guards logic also means much better query results on existing queries. In particular, the cpp/missing-check-scanf query has lost a tons of FPs 🎉.

Commit-by-commit review extremely recommended. It's a large PR, but I actually think I managed to make it fairly reviewable 🤞.

This PR does bring a syntactic breaking changes which was just decided a couple of weeks ago would be okay for CodeQL going forward. The breaking change is that the AST wrapper around the IR-based guards library now extends Element instead of Expr. This is because the shared library also infers that Parameters (which are not Exprs in the AST) can be guards if the parameter determines a condition.

In fact, whereas the old library only made the condition (and certain subexpressions inside the condition) were Guards, the new library makes every expression a Guard, but only some of those Guards actually controls conditions. This is a fundamental difference that's always been between the C/C++ guards library and the Java/C# guards library, and we're finally getting rid of that difference which brings some more language consistency 🎉.

Footnotes

  1. Although, there is still lots of maintenance required because C/C++ still relies on our own implication of ensuresEq and ensuresLt. Getting rid of those requires switching C/C++ fully over to the shared range analysis library.

@MathiasVP MathiasVP requested a review from a team as a code owner September 18, 2025 09:23
@MathiasVP MathiasVP requested review from Copilot and removed request for a team September 18, 2025 09:23
@github-actions github-actions bot added the C++ label Sep 18, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR switches the C++ CodeQL library from its own guards library to the shared guards library that was introduced previously. The main purpose is to reduce C++-specific QL code maintenance and enable C++ to use the shared "nullness" library. This change also improves query results on existing queries, particularly reducing false positives in the cpp/missing-check-scanf query.

Key changes include:

  • Replacing the old C++-specific guards implementation with the shared guards library
  • Updating guard value types from AbstractValue to GuardValue
  • Making guards extend Element instead of Expr to support parameters as guards
  • Improving the guards logic to provide better query results

Reviewed Changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
GuardsEnsure.expected Updated test expectations showing improved guard inference with more comprehensive results
GuardsControl.ql Changed parameter type from AbstractValue to GuardValue
GuardsControl.expected Expanded test results with additional guard control relationships
GuardsCompare.ql Updated parameter type from AbstractValue to GuardValue
GuardsCompare.expected Extended comparison results with more comprehensive guard comparisons
Guards.ql Removed simple guard selection query
Guards.expected Removed corresponding test expectations
tests.ql Updated parameter types to use GuardValue
TOCTOUFilesystemRace.ql Fixed guard child access by casting to Expr
SSLResultConflation.ql Fixed guard child access by casting to Expr
SemanticExprSpecific.qll Updated method call from controlsEdge to controlsBranchEdge
Instruction.qll Added getAnInput() method to BinaryInstruction
EdgeKind.qll Enhanced switch edge handling with better value range support
SsaImpl.qll Major updates to SSA implementation with improved guard integration
IRGuards.qll Complete rewrite using shared guards library with expanded functionality
RangeAnalysis.qll Updated method call to use controlsBranchEdge

}

private import semmle.code.cpp.dataflow.new.DataFlow::DataFlow as DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private

Check warning

Code scanning / CodeQL

Names only differing by case Warning

Private is only different by casing from private that is used elsewhere for modules.
@MathiasVP MathiasVP marked this pull request as draft September 18, 2025 09:52
@MathiasVP MathiasVP force-pushed the use-shared-guards-library branch from c99ab52 to 6fe3e83 Compare September 18, 2025 10:21
@aschackmull
Copy link
Contributor

This is because the shared library also infers that Parameters (which are not Exprs in the AST) can be guards if the parameter determines a condition

No. Parameters can't be Guards. But case statements can - and that's what brings Guards beyond mere Exprs.

@MathiasVP
Copy link
Contributor Author

No. Parameters can't be Guards. But case statements can - and that's what brings Guards beyond mere Exprs.

Oh. I guess there's a subtle thing for C/C++ here then: In the IR world a parameter is an Instruction (specifically, it's an InitializeParameter instruction). And since Instructions are Exprs in the new guards library this means the new guards library can infer that InitializeParameter can be a guard ... and the InitializeParameter maps back to the AST-world as a Parameter.

Is that ... bad?

@MathiasVP MathiasVP force-pushed the use-shared-guards-library branch from 6fe3e83 to c481be8 Compare September 18, 2025 11:23
MathiasVP added a commit to MathiasVP/codeql-coding-standards that referenced this pull request Sep 18, 2025
@aschackmull
Copy link
Contributor

No. Parameters can't be Guards. But case statements can - and that's what brings Guards beyond mere Exprs.

Oh. I guess there's a subtle thing for C/C++ here then: In the IR world a parameter is an Instruction (specifically, it's an InitializeParameter instruction). And since Instructions are Exprs in the new guards library this means the new guards library can infer that InitializeParameter can be a guard ... and the InitializeParameter maps back to the AST-world as a Parameter.

Is that ... bad?

No, I guess that's actually completely fine - I just didn't expect it. That just represents the initial value of the parameter, and pretty much anything with a value can be a guard. I just generally expected the ast nodes that could have a value (and could be assigned to a variable) to be expressions.

@MathiasVP
Copy link
Contributor Author

Phew! Thanks for sanity checking that! I'll pull this PR out of draft in a second once CI has finished the last check. I chatted with @jketema earlier and he's hoping that you can chime in on the reviewing on this PR. I guess primarily 840097f. Would you mind doing that at some point? 🙏

@aschackmull
Copy link
Contributor

Would you mind doing that at some point? 🙏

Sure, I'll take a look (not today, though).

@MathiasVP MathiasVP marked this pull request as ready for review September 18, 2025 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants