☰
CodeQL C 提取器对函数指针的支持:`FunctionPointerType` 与 `FunctionPointerCall` 的实现与查询实战
2026/10/7 2:35:50 网站建设 项目流程
  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

导读

C# 9.0 引入的delegate*函数指针语法,让托管代码可以直接获得非托管函数地址,是高性能与互操作场景的重要能力。本文以 csharp/old-change-notes/2021-01-19-Function-pointer.md 为主线,剖析 CodeQL 仓库中 C# 提取器如何抽取函数指针类型(FunctionPointerType)与函数指针调用(FunctionPointerCall),并结合源码实体、TRAP 事实表、QL 库类与测试用例,给出可复制、可运行的查询实践,帮助你理解并复用这套语义模型。

变更背景与能力概览

在 C# 9 / .NET 5 中,delegate*<T>函数指针是全新的语言特性。它不经过委托(delegate)的分配开销,而是直接保存指向方法的地址,并支持managed、unmanaged及unmanaged[Cdecl|Stdcall|...]等多种调用约定(calling convention)。该变更记录仅两条要点,却是整个提取链路的起点:

  • 新增类型实体FunctionPointerType,对应符号接口IFunctionPointerTypeSymbol;
  • 新增调用表达式实体FunctionPointerCall,用于描述对函数指针的调用。

在仓库中,这条链路横跨四个层面:

层面位置作用
Roslyn 符号IFunctionPointerTypeSymbol提供签名、调用约定等信息源
提取器实体FunctionPointerType.cs将符号落成 TRAP 事实
QL 库类Type.qll、Call.qll向查询作者暴露类型化 API
测试用例FunctionPointer.ql 与 FunctionPointer.cs校验提取与查询结果

提取器实现:从符号到 TRAP 事实

类型实体FunctionPointerType

提取器在 Entities/Types/FunctionPointerType.cs 中定义该实体,其关键职责有三:

  1. ID 生成(WriteId):借助 Roslyn 的Symbol.BuildTypeId构造类型 ID,再追加;functionpointertype后缀,从而与其他类型实体区分。这意味着在数据库中用@function_pointer_type表关联的实体拥有可辨识的稳定标识。
  2. 调用约定抽取(Populate):写入function_pointer_calling_conventions(this, (int)Symbol.Signature.CallingConvention)记录主调用约定;随后遍历Symbol.Signature.UnmanagedCallingConventionTypes,通过has_unmanaged_calling_conventions(this, i, conv.TypeRef)逐个写入非托管调用约定及其下标,保留其顺序语义。
  3. 类型成员抽取:继续调用PopulateType(trapFile),将函数指针的返回类型、参数类型等以function_pointer_return_type等关系写入 TRAP(详见下文 Type.cs)。

该实体还采用CachedEntityFactory工厂模式(FunctionPointerTypeFactory),保证同一符号在数据库构建过程中只产生唯一实体。

返回类型与参数抽取

在 Entities/Types/Type.cs 的PopulateType中:

  • TypeKind.FunctionPointer被映射为Kinds.TypeKind.FUNCTION_POINTER(见同文件第 106 行),即 QL 中TypeKind.FunctionPointer()对应的枚举值;
  • 当符号是IFunctionPointerTypeSymbol时,调用ExtractParametersForDelegateLikeType(trapFile, functionPointer.Signature, t => trapFile.function_pointer_return_type(this, t)),把函数指针签名当作"类委托"处理,抽取其返回类型及形参列表。

从源码结构看,这一复用设计意味着函数指针与委托在提取器内部共享同一套参数抽取逻辑,QL 端也相应地都归属Parameterizable家族。

调用表达式实体FunctionPointerCall

对函数指针的调用形如f(ref i, out object? o),其目标是函数指针类型的符号,而非普通方法符号。提取器在 Entities/Expressions/Invocation.cs 中通过IsDelegateLikeCall统一判定:当调用目标候选符号满足"是函数指针(symbol.Kind == SymbolKind.FunctionPointerType)"或"是委托的 Invoke 方法(MethodKind.DelegateInvoke)"时,就将其归类为类委托调用,从而生成FunctionPointerCall实体而非普通方法调用。

该处理同时覆盖了OverloadResolutionFailure(候选符号全部为函数指针)与LateBound动态调用等边缘场景,避免把合法的函数指针调用误报为编译错误或模型错误(ModelError)。

QL 库:查询函数指针的公开 API

类型侧 API

在 csharp/ql/lib/semmle/code/csharp/Type.qll 中:

class FunctionPointerType extends Type, Parameterizable, @function_pointer_type { ... }

它同时继承Type与Parameterizable,因此查询作者可以直接使用以下常用成员:

  • getAnnotatedReturnType():获取带注解(如可空性)的返回类型;
  • getParameter(int i)/getAnnotatedType():访问第 i 个参数及其注解类型;
  • getCallingConvention():获取主调用约定(managed / unmanaged / unmanaged 变体);
  • getUnmanagedCallingConvention(int i):逐个获取非托管调用约定类型(对应delegate* unmanaged[Cdecl, Stdcall, ...]列表)。

在 Member.qll 与 AnnotatedType.qll 中亦有对函数指针类型的相应引用,使其完整融入类型系统。

调用侧 API

函数指针调用在 csharp/ql/lib/semmle/code/csharp/exprs/Call.qll 中被建模为FunctionPointerCall(Call的子类)。这使得既有的调用图分析、数据流分析(如csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql)可以直接覆盖函数指针调用,而不需要查询作者为这一新语法单独编写调用边。

查询实践:直接可运行的 QL 示例

以下查询直接取自仓库测试 FunctionPointer.ql,并补充了便于观察的order by与输出列,可在csharp/ql目录下对测试数据库运行:

1. 枚举函数指针类型、返回类型与调用约定

import csharp from FunctionPointerType fpt, string returnType, string callingConvention where fpt.getAnnotatedReturnType().toString() = returnType and fpt.getCallingConvention().toString() = callingConvention select fpt, returnType, callingConvention order by fpt.toString()

对应测试输入 FunctionPointer.cs 中的delegate*<int>(返回int,managed约定)等声明,可以验证提取结果。

2. 枚举非托管调用约定

import csharp from FunctionPointerType fpt, int i, string callingConvention where fpt.getUnmanagedCallingConvention(i).toString() = callingConvention select fpt, i, callingConvention

对应测试用例中的delegate* unmanaged[Stdcall]<ref int, out object?, T, void>:下标 0 处应能查询到Stdcall调用约定(源码第 20 行还注释展示了StdcallSuppressGCTransition等候选约定)。

3. 枚举函数指针的参数类型

import csharp from FunctionPointerType fpt, int i, Parameter p, string t where fpt.getParameter(i) = p and p.getAnnotatedType().toString() = t select fpt, i, p, t order by fpt.toString(), i

可覆盖ref int、out object?、in int、ref readonly int等参数修饰符组合(见测试文件 M1/M3)。

4. 找出所有函数指针调用点

import csharp from FunctionPointerCall fpc select fpc, fpc.getTarget()

对应测试文件中f(ref i, out object? o)、f(ref i, out object? o, new T())等调用表达式。

5. 追踪函数指针的隐式转换

import csharp from ImplicitCast cast, FunctionPointerType fromType, FunctionPointerType toType where cast.getSourceType() = fromType and cast.getTargetType() = toType select cast, fromType, toType

测试文件 M5/M6 验证了函数指针间的两种隐式转换:由隐式引用转换(delegate*<B,A>到delegate*<A,B>)与隐式指针转换(delegate*<int*,void*>到delegate*<void*,int*>)触发的转换节点。

测试验证与关联变更

仓库中与该能力配套的测试包括:

  • 库测试:csharp9/FunctionPointer.ql 与 csharp9/FunctionPointer.cs 覆盖类型、调用约定、参数、调用点与隐式转换五类谓词;期望输出记录在 PrintAst.expected 中;
  • 数据流测试:csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql 验证函数指针调用可以作为数据流经过的调用边;
  • 委托测试:delegates/PrintAst.expected 中同样出现FunctionPointerCall,印证"函数指针与委托共享类委托调用建模"的设计。

另一条关联变更 2021-01-25-Function-pointer-cil.md 则记录了 IL(CIL)侧对FunctionPointerType的抽取,并修复了pinned、by-reference类型注解(cil_type_annotation)的抽取问题,说明同一能力在源码级与 IL 级两条路径上均已落地。

使用前提与限制

  • 语言版本:函数指针是 C# 9.0(.NET 5)特性,需要编译器与运行库支持;查询函数指针相关库 API 时,建议在包含 C# 9 语法测试的数据库(如csharp/ql/test/library-tests/csharp9)上运行;
  • unsafe上下文:delegate*声明与调用通常需要unsafe上下文(测试类即声明为unsafe static class Program),提取与查询并不依赖运行时是否真正启用非托管代码,但代码语义受此约束;
  • 非托管调用约定:getUnmanagedCallingConvention(i)只对unmanaged[...]形式有意义,managed与无括号unmanaged形式没有该项,查询时需注意下标越界为空的结果;
  • 本仓库只读:以上查询与测试均为查看、运行与验证用途,不涉及对仓库的修改。

借助本文的实体映射、QL API 与可直接运行的查询片段,你可以基于 CodeQL C# 数据库进一步编写针对函数指针的调用图、类型安全或数据流分析,例如检测不安全的调用约定混用、追踪函数指针指向的敏感方法等。

  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

相关推荐

上一篇:macOS 免费歌词同步神器 LyricsX 完整使用指南
下一篇:产线停机两小时换来的教训:光伏缺陷检测AI质检系统的完整搭建实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询