CAN CSA ISO/IEC TR 24731-1-12 (2016) 技术报告:C语言边界检查接口详解

提升C语言应用程序内存安全的关键技术规范与实践指南

标准概况与适用范围

CAN CSA ISO/IEC TR 24731-1-12 (2016) 是加拿大标准委员会(SCC)采纳的国际标准化组织/国际电工委员会技术报告(ISO/IEC TR 24731-1)的加拿大国家版本。该技术报告正式名称为 Information technology — Programming languages, their environments and system software interfaces — Extensions to the C library — Part 1: Bounds-checking interfaces,旨在为C语言标准库提供一组具有运行时边界检查功能的函数接口,以显著降低缓冲区溢出、字符串截断等常见内存安全漏洞的发生概率。

作为一份技术报告(TR),它并非强制性的国际标准,但为后来的ISO/IEC 9899:2011(C11)的Annex K提供了主要技术基础。加拿大标准CSA版本于2016年重新确认发布,明确了在加拿大国内推荐采用此技术规范,特别适用于安全关键系统、嵌入式开发以及需要遵循严格编码标准(如MISRA C)的工业领域。

适用范围:

  • C语言标准库函数的增强版本,主要用于字符串操作、内存拷贝、格式化输入/输出等易发生越界的场景。
  • 要求编译器及标准库实现支持 __STDC_WANT_LIB_EXT1__ 宏以启用边界检查接口。
  • 适用于医疗设备、汽车电子、航空航天、工业控制等对软件安全要求极高的领域。
技术要点: 本TR定义的函数均以 _s 后缀结尾(如 strcpy_ssprintf_s),并在检测到约束违规时会调用统一的约束处理函数,默认行为是触发运行时异常或调用用户安装的处理器,从而避免未定义行为的发生。

主要技术内容与要求

核心机制:约束处理模型

TR 24731-1 引入了 运行时约束违规(runtime-constraint violation)的概念。当安全函数检测到如下条件时将触发约束违规:

  • 源或目标指针为空(NULL);
  • 缓冲区大小参数为0或超过 RSIZE_MAX
  • 拷贝或追加操作可能导致目标缓冲区溢出;
  • 字符串或内存区域未正确终止等。

每个安全函数在执行其正常功能之前,必须首先检查这些约束。若违规发生,函数将:

  1. 调用当前注册的约束处理函数(通过 set_constraint_handler_s 设置,默认由 abort_handler_signore_handler_s 提供);
  2. 将目标缓冲区设置为空字符串或零填充状态(如适用);
  3. 返回一个非零的 errno_t 错误码。

新增数据类型与宏

名称 类型/宏 说明
errno_t int 类型 用于表示错误状态,成功返回0,失败返回非0。
rsize_t size_t 等价类型 用于表示缓冲区大小的特殊类型,用于区分合法的size_t值。
RSIZE_MAX 宏定义的常量 由实现定义的该类型最大值,任何大于此值的大小参数均被视为溢出。
constraint_handler_t 函数指针类型 指向约束处理函数的指针,原型为 void (*)(const char *restrict msg, void *restrict ptr, errno_t error)

关键安全函数示例

传统函数 边界检查版本 附加参数 主要增强
strcpy strcpy_s 目标最大长度 (rsize_t) 确保不超过目标大小,失败时将目标置空并返回错误码
strcat strcat_s 目标最大长度 (rsize_t) 防止拼接后的结果超过目标缓冲区
sprintf sprintf_s 缓冲区长度 (rsize_t) 自动截断并确保结尾空字符,返回-1表示错误
gets gets_s 缓冲区长度 (rsize_t) 读取不超过指定长度的行,防止缓冲区溢出
memcpy memcpy_s 目标最大长度 (rsize_t) 拷贝时检查目标空间是否足够
重要提示: TR 24731-1 中的 _s 函数与 C11 Annex K 中的同名函数在行为上存在细微差别(例如约束违规时是否清空目标缓冲区)。开发者不可假设所有平台行为完全一致,尤其在跨平台移植时必须查阅实现文档。

实施/应用要点

如何在项目中启用边界检查接口

要使用这些安全函数,必须在任何包含标准头文件之前定义宏 __STDC_WANT_LIB_EXT1__1。例如:

#define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> #include <stdio.h>

并非所有编译器都完全支持此TR。GCC/Clang 在严格模式(例如 -std=c11)下需要配合特定的 C 运行库(如 glibc 或 musl)的可选实现,而 MSVC 在 /std:c11/std:c17 模式下已提供部分 Annex K 函数。建议先确认目标平台的支持程度。

约束处理函数的选择

默认约束处理函数(abort_handler_s)在违规时会导致程序终止,适合安全关键场景;对于非关键场景,可以安装 ignore_handler_s 或自定义处理函数(如记录日志后继续执行)。使用 set_constraint_handler_s 设置全局处理器:

set_constraint_handler_s(your_handler);
标准实施益处: 采用 TR 24731-1 边界检查函数后,缓冲区溢出漏洞几乎可被彻底阻断。结合自动化代码审查工具,能帮助团队在编译阶段捕获 90% 以上的内存安全问题,显著降低产品的安全缺陷密度。
安全关键要求: 根据 CSA 采纳版的精神,在安全关键系统中使用 _s 函数时,绝对禁止 将约束处理函数设置为 ignore_handler_s 而不进行任何替代性错误处理。忽略约束违规将直接破坏函数的安全保证,等于放弃了机制本身。

常见移植陷阱

  • 参数顺序差异: 部分实现遵循 Annex K 的顺序(目标大小在目标指针之后),而有的早期实现可能使用不同的原型,务必核对头文件声明。
  • RSIZE_MAX 的可变性: 不同实现定义的 RSIZE_MAX 可能不同,不可硬编码。
  • 缓冲区清零行为: 约束违规时有些实现会清零缓冲区,有些仅写入空字符,依赖此行为可能导致逻辑隐患。

与其他标准的关系

CAN CSA ISO/IEC TR 24731-1-12 (2016) 与多项国际标准和技术文献密切相关:

  • ISO/IEC 9899:2011 (C11) Annex K: C11 以本TR为蓝本,正式将边界检查接口纳入可选附件,但修改了部分细节(如约束处理函数的默认行为)。因此,本TR可视为 Annex K 的“母标准”。
  • ISO/IEC TR 24772 (Guidance to avoiding vulnerabilities in programming languages): 该指南将 TR 24731-1 列为 C 语言安全编码的重要参考。
  • MISRA C:2012 / CERT-C: 这些行业编码标准强烈推荐使用 _s 函数替代传统不安全函数,并结合静态分析工具实施。
实用提示: 当目标平台不支持 TR 24731-1 时,可考虑使用替代安全库如 Safe C Library(遵循该TR的参考实现)或 Bionic libc 的部分实现,以保证代码可移植性。

常见问题 (FAQ)

问: CAN CSA ISO/IEC TR 24731-1-12 (2016) 与 C11 Annex K 完全相同吗?
答: 不完全相同。本TR是 Annex K 的技术前身,两者核心一致,但在细节上有若干差异,例如约束违规时是否必须清空目标缓冲区,以及 gets_s 的具体行为。编写跨平台兼容代码时,应同时考虑两者规定的子集,并充分测试。
问: 使用这些安全函数后,是否就可以完全避免缓冲区溢出?
答: 大大降低,但不能完全避免。如果程序员故意使用错误大小的参数,或错误地使用约束处理函数(如忽略错误返回码),仍可能出现安全问题。安全函数是防御深度的重要一层,需配合代码审查、静态分析和运行时监控。
问: 在 GNU/Linux 系统上使用 GCC 原生支持这些函数吗?
答: GCC 本身支持语法,但 glibc 默认不包含 Annex K 函数的完整实现,除非启用特定的扩展。一些发行版提供了选项或替代库。建议使用前检查 __STDC_LIB_EXT1__ 宏,若未定义则准备回退方案。

本文撰写于2026年,内容基于CAN CSA ISO/IEC TR 24731-1-12 (2016) 官方文本及公开资料。实际实施时请以最新版本的合规文档为准。

📥 标准文件下载

🔒
请等待 10 秒,广告加载完成后将自动显示下载链接

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注