首页>参考读物>计算机科学与技术>软件与程序设计

C安全编码标准:开发安全、可靠、稳固系统的98条规则(原书第2版)
作者 : [美]罗伯特 C.西科德(Robert C. Seacord)著
译者 : 姚军 等译
丛书名 : C/C++技术丛书
出版日期 : 2015-08-20
ISBN : 978-7-111-50982-0
定价 : 89.00元
扩展资源下载
扩展信息
语种 : 简体中文
页数 : 422
开本 : 16
原书名 : The CERT C Coding Standard: 98 Rules for Developing Safe, Reliable, and Secure Systems,Second Edition
原出版社: Pearson Education Asia
属性分类: 店面
包含CD :
绝版 : 未绝版
图书简介

C语言安全程序之难,即便是经验丰富的程序员也会为此头疼。为了破解这一难题,本书全面解读了C语言安全编码CERT标准第二版。本书第二版所列规则符合全新的C11标准,兼顾C99,条理清晰,提供程序漏洞评级和正反两方面的示例,有助于程序员编写安全、可靠、稳固的系统。

图书特色

本书是业界最广泛采纳的编程指导原则汇编,它紧扣各个版本的C语言标准,分门别类地介绍了各种可能引发可利用安全漏洞的未定义行为、未指定行为,提出了安全编码的规则和建议,在每条规则和建议上都用现实的相容及不相容代码示例加以说明,在第2版中,加入了对最新的C11标准的支持,对于所有有志于C语言软件开发的技术人员来说,都是不可或缺的参考书。
全书共14章,包括98条编码规则,每条规则都由一个标题、一段说明和不相容/相容的代码示例组成。第1章讲述与预处理器相关的规则;第2章介绍的规则与声明和初始化相关;第3章介绍的是与表达式相关的规则;第4~7章讲述的规则分别与整数、浮点数、数组及字符和字符串相关;第8章介绍与内存管理相关的规则;第9章讲解的规则与输入/输出相关;第10章介绍的规则与环境相关;第11~13章分别讲解与信号、错误处理和并发性有关的规则;第14章讲述几条杂项规则。最后提供的三个附录,分别包括本书使用的词汇表、未定义行为和未指定行为。

在Cisco,我们已经采用CERT C编码标准作为所有C语言开发人员的内部安全编码标准。它是我们安全开发生命期的核心组成部分。本书描述的编码标准将复杂的软件安全主题分解为容易遵循的规则以及优秀的现实示例。这是任何希望编写安全、有弹性软件的C/C++开发人员必不可少的参考书。 —— Edward D. Paradise Cisco Systems工程、威胁响应、智能和开发副总裁

C语言安全编程的难度可能超乎许多有经验的程序员的想象。为了帮助编程人员编写更安全的代码,本书提供了CERT C安全编码标准第二个正式发行版本的完整文档。这个新版本中的规则有助于确保程序员的代码完全遵循新的C11标准;它也处理早期版本(包括C99)的问题。
新标准列举了当前C语言软件漏洞的根源,按照严重性、利用的可能性和补救成本排定优先级。书中的98个指导方针都包含了不安全代码和对应的C11相容安全实现。如果统一应用,这些指导方针将消除导致缓冲区溢出、格式字符串漏洞、整数溢出和其他常见漏洞的严重编码错误。
本书内容涵盖:
预处理器
声明与初始化
表达式
整数
浮点数
数组
字符和字符串
内存管理
输入/输出
环境
信号
错误处理
并发性
杂项问题

作者简介
Robert C. Seacord 是卡内基-梅隆大学软件工程研究所(SEI)CERT计划的安全编码技术经理。CERT项目是运营相关网络安全研究和对美国网络安全挑战创新/及时响应的可信提供商。安全编码倡议和软件开发人员及软件开发组织合作,在部署之前消除由于编码错误造成的安全漏洞。Robert还是卡内-基梅隆大学计算机科学学院和信息网络学院的副教授。他曾经撰写过8本书籍,包括《Secure Coding in C and C++》第2版和《Java Coding Guidelines: 75 Recommendations for Reliable and Secure Programs》等。他还发表过40篇软件安全性、基于组件的软件工程、基于Web系统设计、遗留系统现代化、组件储存库和搜索引擎以及用户界面设计和开发方面的论文。

图书前言

本书为C语言编码提供了规则。这些规则的目标是开发安全、可靠和稳固的系统,例如,消除可能导致程序意外行为和可利用漏洞的未定义行为。遵循本标准定义的编码规则是确保C语言开发的软件系统安全、可靠、稳固的必要条件(但不是充分条件)。安全和稳固的设计也是必要的,安全性关键系统通常会提出比编码标准更严格的要求,例如,要求所有内存都是静态分配的。然而,应用本编码标准将产生高质量的系统,这些系统可靠、健壮并且能够抵御攻击。
每条规则都由一个标题、一段说明和不相容/相容的代码示例组成。标题是规则的简洁描述,但是有时候不够精确。说明提出了规则的规范要求。不相容代码示例是违反规则的代码示例。搭配的相容解决方案展示了等价的代码,这些代码不违反该规则或者该编码标准中的任何其他规则。
具有良好文档、可以实施的编码标准是C语言编码必不可少的要素。编码标准鼓励程序员遵循由项目需求和组织确定的一组统一规则,而不是简单地采用程序员熟悉的方法。一旦确定,这些标准可以作为评估源代码(使用人工或者自动化过程)的指标。
CERT编码规则为业界广泛采纳。Cisco系统公司在2011年10月的Cisco年度SecCon会议上宣布在其产品开发中采用CERT C安全编码标准作为基准编程标准。最近,Oracle将所有CERT安全编码标准整合到现有的安全编码标准中。注意,这是长期协作中的最新步骤:CERT和Oracle以前合作编写了《The CERT Oracle Secure Coding Standard for Java》(Addison-Wesley,2011)。
范围
本书是为如下标准中定义的C语言版本开发的:
ISO/IEC 9899: 2011,Programming Languages-C, Third Edition [ISO/IEC 9899: 2011]
ISO/IEC 9899: 2011/Cor.1: 2012,Technical Corrigendum 1
本书在第1版的基础进行更新或替换(Addison-Wesley,2008)。本书第1版的范围是C99(C语言标准第2版)[ISO/IEC 9899: 1999]。虽然本书中的规则是为C11开发的,但是它们也适用于C编程语言的较早版本(包括C99)。在C语言标准各版本之间的差异影响这些规则的正常应用时,本书将在合适的地方进行标注。
大部分规则都有一个不相容代码示例程序与C11兼容,以确保规则所识别的问题在标准范围内。但是,编码问题的最佳解决方案往往与平台相关。在许多情况下,本标准提供兼容POSIX和Windows操作系统的相应解决方案。以ISO/IEC技术报告或者技术规范形式发布的语言和程序库扩展常常优先使用,例如ISO/IEC TR 24731-2《Extensions to the C Library-Part II: Dynamic Allocation Functions》[ISO/IEC TR 24731-2: 2010]描述的扩展。在许多情况下,还有为Linux或者OpenBSD等平台提供的兼容解决方案。我们偶尔还会描述有趣或者直观的特定实现行为。
原理
C语言编码标准专注于C语言标准(C11)和C11之后的相关技术报告,可以在最长的时期内创造最高的价值。
C语言标准尽可能记录现有的实践方法。也就是说,大部分功能必须先在某个实现中测试,才能包含在标准中。本书有不同目的:确定一组最佳实践,有时候需要介绍尚未广为人知或者在现有方法无能为力时使用的新方法。换一种说法,本书试图推动变化,而不只是记录它们。
例如,C11中引入了可选而规范的附件K“Bounds-Checking Interfaces”(边界检查接口),对它的支持正在日益增加,但是目前只有少数供应商实现了这一接口。该接口引入了memcpy_s()等函数,目的是为API添加目标缓冲区大小,提供安全性服务。这一文档具有前瞻性,没有道理仅因为这些函数没有广泛实现而忽略它们。基本C语言标准的实现比附件K更广泛,尽管附件K还没有广泛实现,但它是行业发展的方向。新型C语言代码的开发人员尤其需要这样一本指南,指导他们使用正在开发的编译器和工具,从而最大限度地利用其能力。
有些供应商对C进行了扩展,有些则只实现了C语言标准的一部分便停止开发。因此,不可能倒退回去仅讨论C99、C95或者C90。供应商的支持方程非常复杂,无法确定一条基线,声称某个编译器具体支持某个标准。不管选择哪个分隔点,不同的编译器对于该语言的不同部分都可能出现相反的结果。要支持所有可能性,就必须对每个产品测试语言的每个特性。因此,我们选择了最近的一个分隔点,使标准定义的规则适用时间尽可能长。由于支持的种类不同,当程序员只使用C99规定的功能时,源代码的可移植性得到加强。这是C语言编程固有的安全性/可移植性之间的权衡。
前瞻性信息的价值随着时间的推移而提高,之后开始下降。而回溯性信息的价值从一开始就立刻下降。
由于以上这些原因,本书首先支持使用尚未加入C语言标准的C11及C11之后的技术报告进行的新代码开发。其次支持对C99旧代码及技术报告的纠正。
在效果显著且不会影响其他优先事项的时候,本书将为支持旧编译器做出贡献。这样做的目的不是捕捉所有偏离C语言标准的情况,而是捕捉少数几种重要的情况。
没有解决的问题
对于一些问题,本书没有提供解决方案。
编码风格:编码风格是一个主观的问题,实践证明,无法开发公认的正确风格指南。因此,本标准对于具体采用哪一种风格不作要求,只建议开发组织定义或者采用风格指南,并一致地应用这些方针。一致地应用某种编码风格的最简方法是使用代码格式化工具。许多交互式开发环境(Interactive Development Environment,IDE)都提供这种功能。
有争议的规则:一般来说,CERT编码标准试图避免包含缺乏广泛共识的争议规则。
本书的读者
本书主要是为C语言程序开发人员而编写的,但是也可以供软件采购者使用,以定义定制软件的需求。本书特别有利于对构造可靠、健壮、可以抵御攻击的高质量系统感兴趣的程序员。
本书虽然不是专门针对C++程序员的,但是对他们仍然有一定的价值,因为C语言程序的大部分问题在C++程序中也存在,不过在许多情况下,两者的解决方案不同。
历史
CERT安全编码标准的思路起源于C语言标准委员会(更正式的叫法是ISO/IEC JTC1/SC22/WG14)在德国柏林召开的2006春季会议[Seacord 2013a]。C语言标准是权威文档,但其受众主要是编译器实现者,而且,许多人注意到,它的语言模糊,往往很费解。安全编码标准主要针对C语言程序员,为使用该语言安全编码提供实用指南。
CERT C安全编码标准在CERT安全编码wiki(http://www.securecoding.cert.org)上,按照基于社区的开发过程开发。来自该社区的专家(包括WG14 C语言标准委员会成员)应邀投稿,并且得到wiki的编辑特权。社区成员可以在wiki上注册一个免费账号,对编码标准和单独的规则做出评论。提供高质量评论的审核者常常得到编辑特权,可以直接为编码标准的开发和发展做出贡献。目前,CERT安全编码wiki有1576名注册的贡献者。
基于社区的开发过程有许多好处。最重要的是,它广泛吸引专家,并让他们对规则的内容形成一致的意见。在wiki上开发安全编码标准的主要缺点是内容不断演化。如果你需要最新信息,愿意接受尚未全面核查的变化,那么这种不稳定性是可以接受的。然而,许多软件开发组织想要一组静态的规则和建议,以便作为软件开发过程的需求。为此,在社区开发两年半之后,我们出版了本书第1版。随着2008年6月该书手稿的制作,书籍的1.0版本和安全编码标准的wiki版本开始分道扬镳。
在2007年4月的伦敦会议上首次提交CERT C安全编码指南给WG14审核,2007年8月在夏威夷可纳的会议上再次审核。
根据报道,2008年4月15日召开的J11/U.S. TAG会议讨论了INCITS PL22.11是否应该将CERT C安全编码标准提交给WG14,作为2类或者3类技术报告候选的问题。J11现在成为PL22.11 C语言编程任务组,这个技术委员会是ISO/IEC JTC1 SC22/WG14的美国技术顾问小组。我们就“谁有时间承担这一项目”这个问题进行了一次民意测验,赞成者(有时间)有4人,反对者(没有时间)有12人。之后,我们接收到的反馈是,虽然CERT C安全编码标准是一组强大的指导方针,开发时得到了WG14的许多技术专家的意见,并且已经由WG14审核多次,但是WG14通常不负责将指南“赐予”程序员,而负责定义编译器等工具的规范需求。
了解了这一点之后,我们提议WG14建立一个研究小组,考虑为C语言制作可分析安全编码指南的问题。这个研究小组于2009年10月27日第一次召开会议,CERT向ISO/IEC贡献了C安全编码规则的一个可自动实施子集,用于标准化进程。
研究小组的参与者包括分析程序供应商(如Coverity、Fortify、GammaTech、Gimpel、Klocwork和LDRA)、安全性专家、语言专家和消费者。2012年3月,WG14批准开发和发表ISO/IEC TS 17961—C语言编码规则的新工作项目,研究小组工作结束。意大利国家分委员会在WG14的代表Roberto Bagnara后来加入了WG14编辑委员会。2013年11月,ISO/IEC TS 17961: 2013(E)《信息技术—编程语言、环境和系统软件接口—C安全编码规则》[ISO /IEC TS 17961:2013]正式发表,可以在ISO商店(http://www.iso.org/iso/catalogue_detail.htm csnumber=61134)订购。
ISO/IEC TS 17961 C语言安全编码规则
ISO/IEC TS 17961的目的是建立一组分析程序(包括静态分析工具和C语言编译器)的需求基准,供希望诊断超出语言标准需求的不安全代码的供应商应用。所有规则都可以通过静态分析实施。选择这些规则的标准是,实施这些规则的分析程序必须能够有效地发现安全编码错误,不会产生过量的假阳性。
目前,不同供应商已经以特殊的方式将静态分析应用到安全保障上,造成重要安全保障问题的覆盖面不统一。ISO/IEC TS 17961列举了安全编码规则,需要分析引擎诊断这些规则的违背情况,作为规范相容性的指标。这些规则能够以实现相关的方式进行扩展,为任何相容静态分析实现的客户提供最小覆盖保证。
ISO/IEC TS 17961指定了C语言中安全编码的规则,包含每条规则的代码示例。不相容的代码示例阐述了具有潜在可利用安全性弱点的语言结构;这些例子预计会引起相容分析程序对受影响的语言结构的诊断。相容的例子预计不会引起诊断。ISO/IEC TS 17961不指定实施这些规则的机制和任何特殊的编码风格。
表P-1展示了ISO/IEC TS 17961与其他标准和方针的关联性。在已有的出版物中,ISO/IEC TS 17961是唯一以分析程序而非开发人员为直接受众的。
表P-1 ISO/IEC TS 17961与其他标准的比较
编码标准 C语言标准 安全保障标准 安全性标准 国际化标准 完整语言
CWE 无/全部 是 否 否 —
MISRA C2 C89 否 是 否 否
MISRA C3 C99 否 是 否 否
CERT C99 C99 是 否 否 是
CERT C11 C11 是 是 否 是
ISO/IEC TS 17961 C11 是 否 是 是

在隔离检测出违反规则的情况时,相容的分析程序必须能够为技术规范中每条规则进行一次诊断。如果程序文本同时违反多条规则,相容的分析程序可以进行聚合诊断,但是必须至少进行一次诊断。诊断消息可以采用如下形式:
函数abc访问释放的内存,文件xyz.c,行号nnn。
ISO/IEC TS 17961不要求分析程序为任何违反语法规则或者C语言标准规定约束的内容生成诊断消息。相容性的定义仅针对分析程序可见的源代码。仅使用二进制码的程序库以及对它们的调用不在这些规则的范围内。
这个技术规范的一个有趣特征是可移植性假设,在小组中这被称作“旧金山规则”,因为这一假设是Coverity在其总部举行的一次会议中发展而来的。旧金山规则规定,相容的分析程序必须能够诊断至少一个C语言实现中对指导方针的违反,但是如果违反规则的结果已经记录在目标实现的文档中,且不会导致安全性缺陷,就没有必要加以诊断。实现质量的差异使得分析程序可以生成和可移植性问题有关的诊断。例如,下列程序片段可以进行一次诊断,如%d和long int之间的失配:
long i; printf("i=%d",i);
这种失配问题可能不是所有目标实现都存在的问题,但它是一个可移植性问题,因为不是所有实现对int和long都有相同的表示。
除了已经描述的其他目标之外,为了同ISO/IEC TS 17961一致,本书已经更新。尽管文档服务的受众不同,但是文档之间的一致性可以加强程序员的能力,让程序员更好地利用ISO/IEC TS 17961相容的分析程序找出违反编码标准规则的情况。
安全编码验证(Secure Coding Validation)套件(https://github.com/SEI-CERT/scvs)是CERT开发的一组测试工具,用于验证ISO/IEC TS 17961中定义的规则。这些测试工具基于本技术规范中的示例,在BSD风格的许可证下分发。
工具选择和验证
尽管规则检查可以人工进行,但是随着程序规模和复杂度的增加,人工检查很快就不再可行。因此,建议使用静态分析工具。
当选择一个编译器(应该理解为包含链接程序)时,尽可能采用与C兼容的编译器。如果预处理翻译单元或者翻译单元中包含了对任何语法规则或者约束的违反,即使这种行为显式地定义为“未定义”或者“实现定义”,也应该产生至少一条诊断消息。你使用的任何分析程序也可能假定编译器与C兼容。
当选择源代码分析工具时,很明显这个工具最好尽可能多地遵循wiki上的建议。并非所有建议都可以实现,有些建议完全是说明性的。
虽然CERT建议使用ISO/IEC TS 17961相容的分析程序,但是作为联邦政府出资的研究和开发中心(Federally Funded Research and Development Center,FFRDC),软件工程学会不支持任何特定的供应商或者工具。我们鼓励供应商开发相容的分析程序,本编码标准的用户可以自由评估,选择最适合其目的的分析程序。
完整性和稳定性
应该承认,一般来说,编码规则的相容性无法通过计算方法确定。静态分析的精确度有实际的局限性。例如,计算机科学的“停机定理”规定,程序从哪个控制流中退出在统计学上无法确定。因此,任何依赖控制流的属性—例如停机—对某些程序来说都有可能不确定。不可判定性的结果之一是,任何工具都可能无法从统计学上确定在特定情况下是否满足某条规则。这类代码的广泛存在可能导致分析工具出现不可预知的结果。
进行检查时,分析可能产生:
假阴性:无法报告代码中的真实缺陷,这通常被视为最严重的分析误差,因为这会使用户产生虚假的安全感。大部分工具倾向于过分谨慎,因而产生假阳性。然而,在一些情况下,报告一些高风险的缺陷,忽略其他缺陷可能更好,这样用户不会因为大量的假阳性而不知所措。
假阳性:工具报告不存在的缺陷。假阳性的出现是因为代码过于复杂,以至于工具无法进行全面检查。函数指针和库等特性的使用更可能造成假阳性。
为了达到最大程度的可行性,对于可实施的规则,分析程序应该完整、可靠。如果分析程序不会产生假阴性结果,意味着它能找出整个程序中违反规则的情况,这时可以认为它对特定规则来说是可靠的。如果分析程序不会产生假阳性(错误的警报)结果,那么可以认为它是完整的。对于给定的规则,分析程序结果的可能性如图P-1所示。

图P-1 假阴性和假阳性的可能性
编译器和源代码分析工具是可信过程,也就是说工具的输出有一定的可靠性。相应地,开发人员必须确保不会错误地信任这些工具。理想情况下,工具供应商必须运行合适的验证测试(如使用安全编码验证套件)才能得到信任。
假阳性
虽然许多规则列出了常见的例外情况,但是为每条指导方针开发完整的例外情况列表几乎不可能。因此,源代码符合每条规则的意图、工具最大限度地减少假阳性(没有违反规则意图的情况)是很重要的。工具减少假阳性诊断的程度属于实现质量的问题。
污染分析
污染和污染源
某些运算和函数的定义域是其操作数或者参数类型定义域的子集。当实际值在定义域之外时,结果可能为未定义或者至少是不可预期的。如果操作数或者参数的值可能在操作数或者消费该值的函数定义域之外,且该值来源于程序的任何外部输入(如命令行参数、从系统调用返回的数据或者共享内存中的数据),该值就被污染了,它的来源被称作污染源。被污染的值不一定已知在定义域之外,而是不知其在定义域之内。只有值(而不是操作数或者参数)可能被污染;在某些情况下,在不同路径上,同样的操作数或者参数可能保存污染或者未污染的值。从这方面看,污染是赋予任何来自污染源数值的一个属性,它被赋予来自污染源的任何值。
受限槽
定义域是类型描述定义域子集的操作数和参数被称作受限槽。任何用于指针算术运算的整型操作数都是该操作数的受限槽。某些库函数的特定参数是受限槽,因为这些函数用这些参数进行地址算术运算,或者控制资源的分配、将这些参数传递给另一个受限槽。库函数的所有字符串输入参数也都是受限槽,因为这些参数可能用不以null作为结束符的字符序列形式传递。strncpy()和strncpy_s()的输入参数例外,它们明确允许来源字符序列不以null作为结束符。
传播
污染通过运算从操作数向结果传播,除非运算本身按照受限槽的约束限制其结果值。除了传播同类污染的运算之外,有些运算将操作数的某类污染传播给结果时,会产生不同类的污染,最有名的例子是strlen(),它的参数和与字符串长度有关的污染传播给返回值时,会产生范围相关的污染。
虽然循环的退出条件通常不被视为受限槽,但是如果循环根据受污染值确定退出条件,就会将污染传播给任何按照循环迭代数量比例增加或者减小的数值型或者指针型变量。
净化
为了从数值中消除污染,该值必须进行净化,确保它在所进入的任何受限槽的定义域中。净化通过替换或者终止进行。在替换中,定义域之外的值由定义域内的值代替,处理用定义域内的值代替原始值继续进行。在终止中,程序逻辑在检测到定义域之外的值时终止执行路径,往往简单地绕过使用该值的任何代码。
一般来说,无法使用静态分析识别净化。执行污染分析的分析程序通常提供某种语言学之外的机制识别净化函数,这些函数净化实参(通过地址传递),返回实参的净化版本或者返回表示实参是否处于要求的定义域的状态码。因为这种语言学之外的机制超出了本编码标准的范围,我们使用一组基本的净化定义,这些定义可能可以识别真正的净化,但是也可能造成没有净化或者净化效率低下的代码被错误地理解为净化。下列净化定义假设分析以某种形式维护模拟执行期间遇到的每个值上的一组约束:如果给定代码路径将某个值的范围限制在给定受限槽类型定义域的一个子集内,那么它就根据这个受限槽对该值进行了净化。例如,有符号整数对数组索引运算的净化,必须将该整数值的范围限制在0~(数组大小–1)。
上述描述适合于数字值,字符串内容的净化更难以用通用的方法识别。
规则与建议
本书包含98条编码规则。在写作本书时,CERT编码标准wiki还包含178条建议。规则提供代码的规范化要求,而建议提供的是一种指导,遵循建议能够改善软件系统的安全性、可靠性和稳固性,但是违反建议不一定代表代码中存在缺陷。
规则和建议统称“指导方针”。规则必须符合如下条件:
1)违反指导方针可能造成对系统安全性、可靠性、保障性不利的缺陷,例如,引入可能造成可利用漏洞的安全缺陷。
2)指导方针不依赖于源代码注释或者程序员意图的假设。
3)指导方针的相容性可以通过自动化分析(静态或者动态)、正式方法或者人工检查技术确定。
建议是可以改善代码质量的提议。符合如下所有条件时,指导方针被定义为建议:
1)指导方针的应用可能改善软件系统的安全性、可靠性和保障性。
2)指导方针被视为规则的必要条件中有一条或者多条不符合。
图P-2展示了98条规则和178条建议的组织方式。
在写作本书时,wiki上还包含两个特定平台的附件,一个用于POSIX,另一个用于Windows,因为它们不是核心标准的一部分,所以本书略去不谈。
特定开发工作中采用的建议集根据最终软件产品的需求而定。需求较为严格的项目可能决定将更多的资源用于确保系统安全性、可靠性和保障性,最后可能采用更广泛的建议集。



图P-2 CERT C编码指导方针
使用方法
本标准中的规则可能用组织的特定规则进行扩展。但是,必须遵守标准中的规则才能声称与标准相容。
可以开发培训教程,用来培训软件开发专业人员正确应用编码标准。在经过测验后,培训过的程序员可能认证为编码专家。例如,卡内基·梅隆大学开发了一个认证项目——软件开发人员认证(Software Developer Certification,SDC)。SDC利用认证考试达到如下目的:
1)认定求职者具备特定的编程技能
2)说明人员训练有素,具备软件工作能力
3)为教学和培训机构提供指导
一旦确立了编码标准,可以开发或者修改工具及过程,确定标准的相容性。
相容性测试
为了确保源代码遵守编码标准,有必要采取措施检查规则违反情况。实现这一目标最有效的手段是使用一种或者多种ISO/IEC TS 17961相容的分析程序。在某条规则无法用工具检查的情况下,需要人工审核。
源代码分析实验室(Source Code Analysis Laboratory,SCALe)提供了一种根据本标准和其他标准验证软件系统相容性的手段。CERT编码标准提供了一组可以评估软件系统的规范化规则。相容的软件系统应该表现出优于不相容系统的安全性、可靠性和保障性。
卡内基·梅隆大学软件工程学院CERT部门的SCALe团队分析开发人员的源代码,提供指导代码修复的详细报告。在开发人员解决了发现的问题,SCALe团队确定该产品版本与标准相容之后,CERT项目会向开发人员颁发认证证书,将该系统列入相容系统注册表中。这一报告详细介绍了SCALe过程,并提供对选中的软件系统的分析。
相容性
CERT C编码标准的相容性要求代码不违反本书指定的任何规则。如果要求某种例外情况,这种例外必须与预先定义的例外情况对应,例外的应用必须在源代码中记录。
wiki上的建议相容不一定代表与CERT C编码标准相容。遵守建议在许多情况下可以使遵守规则变得更容易,可以消除许多潜在的代码缺陷。
偏离规程
严格遵守所有规则是不可能的,因此违反特定规则的偏离情况是必要的。偏离用于这种情况:发现的真阳性毫无争议,但是代码却可以确认是正确的。无可争议的真阳性可能是软件设计或者架构特性的结果,也可能有编码标准没有预见到的有效理由。在这种情况下,考虑到编码规则过于严格的可能性,偏离规则是可能的[Seacord 2012]。
不能以性能或者易用性为理由而偏离。成功通过相容性测试的软件系统必须不包含任何缺陷或者可利用漏洞。偏离请求由主评估员评估,如果开发人员能提供足够证据,证明偏离不会造成漏洞,偏离请求就会被接受。偏离不常使用,因为修复编码错误几乎总是比提供编码错误不会造成漏洞的证据更容易。
系统质量
本编码标准的目标是制作安全、可靠、有保障的系统。安全关键系统可能存在额外的要求,例如不使用动态内存分配。其他感兴趣的软件质量属性包括可移植性、易用性、可用性、可维护性、可读性和性能。
这些属性中,许多都以有趣的方式相互关联。例如,可读性是可维护性的一个属性;二者对于限制维护期间引入的缺陷都很重要,这些缺陷可能造成安全漏洞或者可靠性问题。此外,可理解性有助于安全工作人员进行代码检查。可靠性和可用性要求正确的资源管理方法,这对系统的保障性和安全性也有贡献。性能和安全性等系统属性往往产生冲突,需要考虑权衡。
本书的组织方式
本书组织为14个包含特定主题领域规则的章节、3个附录、参考文献。附录A包含本书使用的词汇表。附录B列出C语言标准附件J,J.2节[ISO/IEC 9899: 2011]中的未定义行为,这些未定义行为在规则中经常引用。附录C包含来自C语言标准附件J,J.1节[ISO/IEC 9899: 2011]中的未指定行为,这些未指定行为在规则中也经常引用。参考文献是每条规则的参考文献部分和本书其他引用的纲要。
大部分规则采用统一的结构。本标准中的每条规则都在标题中包含唯一的标识符。标题和介绍性段落定义规则,然后是一对或者多对不相容代码示例和相容解决方案。每条规则还包含风险评估、相关指导方针和参考文献。规则还可能包含相关漏洞表格。CERT编码标准wiki上的建议组织方式也与此处类似。
标识符
每条规则和建议都有唯一的标识符,由3部分组成:
3个字母的助记符,代表在标准中所属的部分
2位数字,范围为00~99
大写字母C,表示这是一条C语言指导方针
3个字母组成的助记符用于对类似编码惯例进行分组,表示编码惯例所属的类别。
数字用于为编码惯例提供唯一的标识符。数字00~29为建议保留,数字30~99为规则保留。在本书中,规则和建议常常用它们的标识符和标题引用。规则可以在本书的目录中找到,而建议只能在wiki上找到。
不相容代码示例和相容解决方案
不相容代码示例展示了违反所讨论指导方针的代码。要注意,这些代码仅仅是示例,消除代码中出现的这些示例,并不一定意味着所分析的代码已经遵守指导方针。
不相容代码示例后面通常有相容解决方案,说明不相容代码示例如何用安全、相容的方式重新编码。除非特别说明,不相容的示例应该只包含对所讨论规则的违反。相容解决方案应该遵循所有安全编码规则,但是有时候可能不遵循某条建议。
例外
任何规则或者建议都可能有一组例外,在某些极为具体的情况下,指导方针不一定能够确保软件的安全性、可靠性和保障性。例外仅供参考,不一定需要遵循。
风险评估
本书中的每条指导方针都包含一个风险评估部分,试图为软件开发人员提供不在代码中处理特定规则违反情况的一个后果说明,并为预期补救成本做出提示。开发团队可以利用这一信息排定修复规则违反情况的优先级。其中的指标主要设计用于补救项目。一般假设开发的新代码将遵循整个编码标准和适用的建议。
每条规则和建议都指定了一个优先级。优先级根据失效模式、影响和危害性分析(Failure Mode,Effect,Criticality Analysis,FMECA)[IEC 60812]度量。每条规则用3个值表示严重性、可能性和补救代价,取值范围为1~3。
严重性—忽略规则的后果有多严重?
取值 含义 漏洞示例
1 低 拒绝服务攻击,异常终止
2 中 破坏数据完整性,意外信息泄露
3 高 运行任意代码
可能性—忽略规则引起的缺陷导致可利用漏洞的可能性有多大?
取值 含义
1 不太可能
2 有可能
3 很可能
补救代价—使代码遵循规则的代价有多高?
取值 含义 检测 纠正
1 高 人工 人工
2 中 自动 人工
3 低 自动 自动
每条规则的3个值相乘形成一个指标,用于确定应用规则的优先级。乘积的范围为1~27,但是只有10种不同的可能值:1、2、3、4、6、8、9、12、18和27。优先级范围1~4的规则和建议是3级规则,6~9的是2级规则,12~27的是1级规则。下面是优先级和规则级别的可能含义:
级别 优先级 可能的含义
L1 12、18、27 极为严重,很可能发生,补救代价不高
L2 6、8、9 中等严重,可能发生,补救代价中等
L3 1、2、3、4 不甚严重,不太可能发生,补救代价高
特定项目的补救工作可能从实施某个特别级别的所有规则开始,然后进入较低优先级的规则,如图P-3所示。

图P-3 相容性级别
自动化检测
在wiki上,规则和建议通常都有一个描述自动化检测的小节。这些小节提供了有能力自动化诊断编码指导方针违反情况的分析程序的附加信息。对C编程语言的大部分自动化分析都不可靠、不全面,所以在这一小节中包含某种工具,通常表示该工具可以诊断某些违反特定规则的情况。虽然安全编码验证套件可以用于测试分析程序诊断ISO/IEC TS 17961规则违反情况的能力,但是目前没有能够评估分析程序诊断本书中规则违反情况的相容性测试套件。因此,wiki上自动化检测小节中的信息可能:
由供应商提供
由CERT对分析程序进行的非正式评估确定
由CERT对供应商文档进行的审阅确定
在可能的情况下,我们试图引用获得该结果的准确工具版本。因为这些工具在不断发展,这一信息可能很快过时。因此,本书忽略这一信息,仅在wiki上维护。
相关漏洞
wiki上的相关漏洞小节包含了一个在CERT网站上搜索相关安全漏洞的链接。只要可能,CERT漏洞说明都有一个与编码指导方针唯一ID对应的关键词。这一搜索为你提供现实世界中已经确定至少部分源自违反特定指导方针的安全漏洞的最新列表。这些漏洞只在CERT/CC的漏洞分析团队评估源代码、确定漏洞根源之后才做标记。因为许多安全漏洞说明所指的是闭源软件系统中的漏洞,不总是能够提供这种附加分析。因此,相关漏洞部分的内容常常缺失。
最新相关漏洞列表的网址是:
https://www.kb.cert.org/vulnotes/bymetric searchview&query=FIELD+KEYWORDS+contains+XXXNN-X
其中的XXXNN-X是搜索的规则或者建议ID。
本书中引用特定的安全漏洞(Vulnerability,VU)标识符和通用漏洞披露(Common Vulnerability and Exposure,CVE)标识符,你可以创建唯一的URL,在固定字符串最后附加相关ID,以获得特定漏洞的更多信息。例如:
要寻找VU#551436“Mozilla Firefox SVG查看器整数值溢出漏洞”的相关信息,你可以在https://www.kb.cert.org/vulnotes/id/后附加551436,在浏览器中输入:https://www.kb.cert.org/vulnotes/id/551436。
要寻找CVE-2006-1174的相关信息,可以在http://cve.mitre.org/cgi-bin/cvename.cgi name=后面附加CVE-2006-1174,在浏览器中输入结果URL:http://cve.mitre.org/cgi-bin/cvename.cgi name= CVE-2006-1174
本书中只在上述信息实用且有趣时,才在特定规则中包含相关漏洞小节。
相关指导方针
本小节包含指向相关标准、技术规范和指导方针汇编的链接,如《Information Techno-logy—Programming Languages,Their Environments and System Software Interfaces—C Secure Coding Rules》(信息技术—编程语言、环境及系统软件接口—C安全编码规则)[ISO/IEC TS 17961: 2013];《Information Technology—Programming Languages—Guidance to Avoiding Vulnerabilities in Programming Languages Through Language Selection and Use》(信息技术—编程语言—通过语言选用避免编程语言漏洞指南)[ISO/IEC TR 24772: 2013];《MISRA C 2012:Guidelines for the Use of the C Language in Critical Systems》(MISRA C 2012:关键系统中的C语言使用指南)[MISRA C: 2012]以及MITRE常见弱点列举(Common Weakness Enumeration)中的CWE ID[MITRE 2013]。
你可以创建唯一的URL,在固定字符串最后附加相关ID,以获得CWE的更多相关信息。例如,要找到CWE-192“整数强制错误”的更多相关信息,可以在http://cwe.mitre.org/data/definitions/后附加192.html,在浏览器中输入结果URL:http://cwe.mitre.org/data/definitions/ 192.html。
书中还引用了其他商业化技术规范、技术报告和指导方针。
参考文献
大部分规则都有一个参考文献小节,其中列出了提供与该规则相关的信息的文档和文档中的小节。
自动生成的代码
如果要使用代码生成工具,就必须选择合适的工具并进行验证。遵循本文档的要求,能够为评估工具提供一个标准。
编码指导方针因代码的生成和维护方式而有所不同。代码的类别包括:
工具生成、工具维护的代码由一种更高级的格式规定和维护,特定语言的源代码以这种格式生成。源代码由这种更高级别的描述生成,然后作为语言编译器的输入。程序员从未查看或者修改生成的源代码。
工具生成、手工维护的代码由一种更高级的格式规定和维护,特定语言的源代码以这种格式生成。然而,按照预期或者预测,在开发周期的某一时刻,该工具将停止使用,生成的源代码将由程序员检查、人工修改和维护。
手工编写的代码,由程序员用文本编辑器或者交互式开发环境人工编写;程序员以提供给编译器的源代码格式直接维护源代码。
手工编写和维护的源代码必须有如下属性:
可读性
程序内涵
这些要求不适用于不由程序员直接处理的源代码,但是正确行为的要求仍然适用。可读性和内涵的要求适用于工具生成和人工维护的代码,但是不适用于工具生成和工具维护的代码。工具生成、工具维护的代码可能产生一致性约束,手工生成代码中的某些风险构造在这里可以确保安全性。
政府监管
按照安全编码规则开发软件是一个好的想法,而且越来越成为一种要求。2013财政年度的美国《国防授权法案》第933小节“国防部采购计算机软件可靠性改善措施”中要求,政府软件开发及维护组织和承包商必须提供软件开发、升级和维护活动期间遵守国防部(Department of Defense,DoD)批准编码标准的证据,包括检查和评估的方法。
DoD采购计划以征求建议书(RFP)的形式提出了《The Application Security and Development Security Technical Implementation Guide(STIG)(程序安全性和开发安全性技术实施指南)》第2版,第1次发行[DISA 2008]。2.1.5小节“Coding Standards(编码标准)”中要求“项目经理将确保开发团队遵循一组编码标准。”
这个标准的正确应用将使系统遵循《应用程序安全性和开发安全性技术实施指南》[DISA 2008]中的如下要求:
(APP2060.1: CAT II):项目经理将确保开发团队遵循一组编码标准。
(APP2060.2: CAT II):项目经理将确保开发团队创建一个需要避免的不安全函数列表,并在编码标准中存档这个列表。
(APP3550: CAT I):设计人员将确保应用程序不会因为整数运算问题而出现漏洞。
(APP3560: CAT I):设计人员将确保应用程序不包含格式字符串漏洞。
(APP3570: CAT I):设计人员将确保应用程序不允许命令注入。
(APP3590.1: CAT I):设计人员将确保应用程序没有缓冲区溢出。
(APP3590.2: CAT I):设计人员将确保应用程序不使用已知容易遭到缓冲区溢出攻击的函数。
(APP3590.3: CAT II):设计人员将确保应用程序不在程序语言允许的地方对内存分配使用带符号值。
(APP3600: CAT II):设计人员将确保应用程序没有规范表示漏洞。
(APP3630.1: CAT II):设计人员将确保应用程序不会遭到竞争条件的影响。
(APP3630.2: CAT III):设计人员将确保应用程序不在可以使用局部变量时使用全局变量。
对程序员和软件测试人员的培训将满足如下要求:
(APP2120.3: CAT II):项目经理将确保开发人员每年至少得到一次有关安全设计和编码方法的培训。
(APP2120.4: CAT II):项目经理将确保为测试人员提供年度培训。
(APP2060.3: CAT II):设计人员将遵循为项目确定的编码标准。
(APP2060.4: CAT II):设计人员将不使用项目编码标准文档中记录的不安全函数。
(APP5010: CAT III):测试经理将确保除了功能测试以外,还分配至少一名测试人员进行安全缺陷测试。
致谢
本书是社区的广泛努力所成就的。首先,我要感谢为本书贡献指导方针的人们,包括Arbob Ahmad、Juan Alvarado、Dave Aronson、Abhishek Arya、Berin Babcock-McConnell、Roberto Bagnara、Bruce Bayha、Joe Black、Jodi Blake、Jill Britton、Levi Broderick、Hal Burch、J. L. Charton、Steve Christey、Ciera Christopher、Geoff Clare、Frank Costello、Joe Damato、Stephen C. Dewhurst、Susan Ditmore、Chad Dougherty、Mark Dowd、Apoorv Dutta、Emily Evans、Xiaoyi Fei、William Fithen、Hallvard Furuseth、Jeff Gennari、Andrew Gidwani、Ankur Goyal、Douglas A. Gwyn、Shaun Hedrick、Michael Howard、Sujay Jain、Christina Johns、Pranjal Jumde、Andrew Keeton、David Kohlbrenner、Takuya Kondo、Masaki Kubo、Pranav Kukreja、Richard Lane、Stephanie Wan-Ruey Lee、Jonathan Leffler、Pengfei Li、Fred Long、Justin Loo、Gregory K.Look、Larry Maccherone、Aditya Mahendrakar、Lee Mancuso、John McDonald、James McNellis、Randy Meyers、Dhruv Mohindra、Nat Lyle、Bhaswanth Nalabothula、Todd Nowacki、Adrian Trejo Nu馿z、Bhadrinath Pani、Vishal Patel、David M. Pickett、Justin Pincar、Thomas Plum、Abhijit Rao、Raunak Rungta、Dan Saks、Alexandre Santos、Brendan Saulsbury、Jason Michael Sharp、Astha Singhal、Will Snavely、Nick Stoughton、Alexander E. Strommen、Glenn Stroz、Dean Sutherland、Kazunori Takeuchi、Chris Tapp、Chris Taschner、Mira Sri Divya Thambireddy、Melanie Thompson、Elpiniki Tsakalaki、Ben Tucker、Fred J. Tydeman、Abhishek Veldurthy、Wietse Venema、Alex Volkovitsky、Michael Shaye-Wen Wang、Grant Watters、Tim Wilson、Eric Wong、Lutz Wrage、Shishir Kumar Yadav、Gary Yuan、Ricky Zhou和Alen Zukich 。
我还要感谢下列评审:Stefan Achatz、Arbob Ahmad、Laurent Alebarde、Kevin Bagust、Greg Beeley、Arjun Bijanki、John Bode、Konrad Borowski、Stewart Brodie、Jordan Brown、Andrew Browne、G. Bulmer、Kyle Comer、Sean Connelly、Ale Contenti、Tom Danielsen、Eric Decker、Mark Dowd、T.Edwin、Brian Ewins、Justin Ferguson、William L. Fithen、Stephen Freidl、Hallvard Furuseth、Shay Green、Samium Gromoff、Kowsik Guruswamy、Jens Gustedt、Peter Gutmann、Douglas A. Gwyn、Richard Hearthfield、Darryl Hill、Paul Hsieh、Ivan Jager、Steven G. Johnson、Anders Kaseorg、Matt Kfaai、Piotr Krukowiecki、Jerry Leichter、Nicholas Marriott、Frank Martinez、Scott Meyers、Eric Miller、Charles-Francois Natali、Ron Natalie、Adam O払rien、Heikki Orsila、Balog Pal、Jonathan Paulson、P. J. Plauger、Leslie Satenstein、Kirk Sayre、Neil Schellenberger、Michel Schinz、Eric Sosman、Chris Tapp、Andrey Tarasevich、Josh Triplett、Yozo Toda、Pavel Vasilyev、Ivan Vecerina、Zeljko Vrba、David Wagner、Henry S. Warren、Colin Watson、Zhenyu Wu、Drew Yao、Christopher Yeleighton和Robin Zhu。
感谢Addison-Wesley的团队,包括助理编辑Kim Boedigheimer、制作编辑Caroline Senay、全方位服务生产经理Julie B. Nahil、文稿编辑Deborah Thompson、执行编辑Christopher Guzikowski、总编辑John Fuller,以及产品市场经理Stephane Nakib。
我还要感谢Peter Godon,他批准了这个项目,但是在项目完成之前退休了,大概是为了避免再和我谈生意吧。我第一次见到Peter Godon是在1999年,当时我们在洛杉矶希尔顿酒店(靠近机场的那一家)的餐厅里共进早餐,商讨我们的首次合作—《Building Systems from Commercial Components》,该书的合著者Kurt Wallnau也在场。我不记得讨论的细节,但是对Peter的本领却印象深刻,比对早餐的印象深多了,我们在同一次旅程中还在Santa Monica的海鲜餐厅中共进了晚餐。
从那时起,Peter和我在14年的时间里合作了8本书。从波士顿Back Bay的晚餐到东京的午餐,Peter给我带来了深远的影响,在我的职业生涯中给予了无人能及的帮助。虽然我对Peter的回忆大部分都和食物有关,但是从他在技术书籍营销方面的卓越思想和知识中获益良多,例如 “将封面做成红色,标题中加入某些数字”的建议。更重要的是,尽管Peter有机会和Donald Knuth这样的著名作家合作,但是他本人却一直很谦逊。Peter,我希望你能够享受退休生活,我很快就会有机会再和你一起聚餐了。
我还要感谢CERT团队中的其他人,包括Tamara Butler、Pamela Curtis、Gina DeCola、Ed Desautels、Shannon Haas、Paul Ruggiero、Osona Steave、Tracey Tamules和Pennie Walters,感谢他们的支持和帮助,没有他们,本书就不可能完成。最后(但是并非不重要),我们要感谢内部编辑Carol J. Lallier,感谢他帮助我完成本书。
特别感谢Archie Andrews、Mark Sherman、Bill Wilson和Rich Pethia。

上架指导

计算机\程序设计

封底文字

封底文字
“在Cisco,我们已经采用CERT C编码标准作为所有C语言开发人员的内部安全编码标准。它是我们的安全开发生命期的核心组成部分。本书描述的编码标准将复杂的软件安全主题分解为容易遵循的规则以及优秀的现实示例。这是任何希望编写安全、有弹性软件的C/C++开发人员必不可少的参考书。”
——Edward D. Paradise,Cisco Systems工程、威胁响应、智能和开发副总裁

C语言安全编程的难度可能超乎许多有经验的程序员的想象之外。为了帮助编程人员编写更安全的代码,本书提供了CERT C安全编码标准第二个正式发行版本的完整文档。这个新版本中的规则有助于确保程序员的代码完全遵循新的C11标准;它也处理早期版本(包括C99)的问题。
新标准列举了当前C语言软件漏洞的根源,按照严重性、利用的可能性和补救成本排定优先级。书中的98个指导方针都包含了不安全代码和对应的C11相容安全实现。如果统一应用,这些指导方针将消除导致缓冲区溢出、格式字符串漏洞、整数溢出和其他常见漏洞的严重编码错误。
本书内容涵盖:
 预处理器
 声明与初始化
 表达式
 整数
 浮点数
 数组
 字符和字符串
 内存管理
 输入/输出
 环境
 信号
 错误处理
 并发性
 杂项问题

作者简介

[美]罗伯特 C.西科德(Robert C. Seacord)著:暂无简介

译者简介

姚军 等译:暂无简介

译者序

历数各种编程语言,可能很难有一种能够达到C语言的高度。凭借良好的移植性和跨平台支持,以及高效率的低级处理能力,C语言成为现代最流行操作系统平台的基石,也成为教育、研究和软件开发中最受人欢迎的语言之一。
C语言灵活的类型转换和贴近底层机器实现、目标代码效率高的特性,一直是系统软件开发人员最为喜爱的,但这也是一把双刃剑。随着软件系统的复杂度不断提高,编码中的一些小瑕疵越来越容易暴露出来,从而引发严重的安全问题,加之UNIX、Windows等主流操作系统的各种组件大多以C语言编写,黑客们乐此不疲地寻找着这些方面的漏洞,这给全球的计算机系统安全带来了严重的威胁。
在这种形势下,制定一种严格的安全编码标准,避免出现可利用的安全漏洞,就成为IT业界的当务之急。许多大型开发团体都制定了自己的安全编码标准,国际标准化组织也对C语言标准进行了修订,对C语言实现提出更严格的要求,从而为安全编码提供了基础。
本编码标准是业界采纳最广泛的编程指导原则汇编 ,它紧扣各个版本的C语言标准,分门别类地介绍了各种可能引发可利用安全漏洞的未定义行为、未指定行为,提出了安全编码的规则和建议,在每条规则和建议上都用现实的相容及不相容代码示例加以说明。本书是该标准文档的第2版,加入了对最新的C11标准的支持,对所有有志于C语言软件开发的技术人员来说,本书都是不可或缺的参考书。
本书内容极其丰富,正如作者所言,即便是C语言国际标准中,也存在着许多模糊之处,书中的例子和说明文字,就是要揭开层层迷雾,帮助读者认识很多并不那么明显但是可能造成严重问题的不当编程方法。在翻译过程中我们也深有体会,书中引用的标准条文中,确实有许多似是而非的东西,也许只有在细细咀嚼书中代码片段之后,才能逐步厘清。我们力求再现原书所要阐述的基本原理,希望能够真正地帮助到读者,但因水平所限,错误在所难免,期待广大读者批评指正。
本书的翻译工作主要由姚军完成,徐锋、陈志勇、谢志雄、方翊、白龙、林耀成、陈霞、宁懿、吴玥等也对翻译工作做出了贡献,在此衷心感谢机械工业出版社华章分社的关敏编辑和其他编审人员做出的辛勤努力。

图书目录

译者序
前言
贡献者简介
第1章 预处理器(PRE) 1
1.1 PRE30-C. 不要通过连接创建通用字符名称 1
1.2 PRE31-C. 避免不安全宏参数的副作用 3
1.3 PRE32-C. 不要在类函数的宏调用中使用预处理器指令 7
第2章 声明和初始化(DCL) 9
2.1 DCL30-C. 声明具有正确存储持续期的对象 10
2.2 DCL31-C. 在使用前声明标识符 13
2.3 DCL36-C. 不要声明具有冲突链接类别的标识符 16
2.4 DCL37-C. 不要声明或者定义保留标识符 18
2.5 DCL38-C. 使用正确语法声明灵活数组成员 23
2.6 DCL39-C. 避免在结构填充中泄露信息 26
2.7 DCL40-C. 不要创建相同函数或者对象的不兼容声明 30
2.8 DCL41-C. 不要在switch语句第一个条件标签之前声明变量 35
第3章 表达式(EXP) 37
3.1 EXP30-C. 不要依赖求值顺序以避免副作用 38
3.2 EXP32-C. 不要通过非易失性引用访问易失性对象 42
3.3 EXP33-C. 不要读取未初始化的内存 44
3.4 EXP34-C. 不要对null指针进行解引用 52
3.5 EXP35-C. 不要修改具有临时生命期的对象 56
3.6 EXP36-C. 不要将指针转换为更严格对齐的指针类型 59
3.7 EXP37-C. 用正确数量和类型的参数调用函数 62
3.8 EXP39-C. 不要通过不兼容类型的指针访问变量 67
3.9 EXP40-C. 不要修改常量对象 72
3.10 EXP42-C. 不要比较填充数据 73
3.11 EXP43-C. 使用restrict限定的指针时避免未定义行为 75
3.12 EXP44-C. 不要向sizeof、_Alignof或者_Generic传递有副作用的操作数 82
3.13 EXP45-C. 不要在选择语句中执行赋值 85
第4章 整数(INT) 89
4.1 INT30-C. 确保无符号整数运算不产生回绕 90
4.2 INT31-C. 确保整数转换不会造成数据丢失或者错误解释 95
4.3 INT32-C. 确保有符号整数的运算不造成溢出 101
4.4 INT33-C. 确保除法和余数运算不会造成0除数错误 109
4.5 INT34-C. 不要用负数或者不小于操作数位数的位数对表达式进行移位 111
4.6 INT35-C. 使用正确的整数精度 115
4.7 INT36-C. 将指针转换为整数或者将整数转换为指针 117
第5章 浮点数(FLP) 122
5.1 FLP30-C. 不要使用浮点变量作为循环计数器 122
5.2 FLP32-C. 避免或者检测数学函数中的定义域和值域错误 124
5.3 FLP34-C. 确保浮点数转换在新类型的范围内 132
5.4 FLP36-C. 将整数值转换为浮点指针类型时保持精度 135
第6章 数组(ARR) 137
6.1 ARR30-C. 不要形成或者使用超限的指针或者数组下标 137
6.2 ARR32-C. 确保变长数组的大小参数在有效范围内 146
6.3 ARR36-C. 不要进行两个不引用相同数组的指针之间的减法或者比较 148
6.4 ARR37-C. 不要在指向非数组对象的指针上加或者减一个整数 149
6.5 ARR38-C. 保证库函数不形成无效指针 152
6.6 ARR39-C. 不要在指针上加或者减一个按比例调整的整数 159
第7章 字符和字符串(STR) 163
7.1 STR30-C. 不要企图修改字符串字面量 163
7.2 STR31-C. 保证字符串存储有足够的空间容纳字符数据和null结束符 166
7.3 STR32-C. 不要向要求字符串参数的库函数传递非null结束字符序列 177
7.4 STR34-C. 在转换为更大的整数尺寸之前将字符转换为unsigned char类型 181
7.5 STR37-C. 字符串处理函数的实参必须可以表示为unsigned char 184
7.6 STR38-C. 不要混淆窄和宽字符串及函数 186
第8章 内存管理(MEM) 189
8.1 MEM30-C. 不要访问已释放内存 189
8.2 MEM31-C. 在不再需要时释放动态分配的内存 194
8.3 MEM33-C. 动态分配和复制包含灵活数组成员的结构 196
8.4 MEM34-C. 只释放动态分配的内存 200
8.5 MEM35-C. 为对象分配足够的内存 203
8.6 MEM36-C. 不要通过调用realloc()修改对象的对齐方式 206
第9章 输入/输出(FIO) 209
9.1 FIO30-C. 从格式字符串中排除用户输入 210
9.2 FIO31-C. 不要打开已经打开的文件 213
9.3 FIO32-C. 不要在只适合文件的设备上执行操作 216
9.4 FIO34-C. 区分从文件读入的字符和EOF/WEOF 221
9.5 FIO37-C. 不要假定fgets()或者fgetws()在成功时返回非空字符串 225
9.6 FIO38-C. 不要复制FILE对象 227
9.7 FIO39-C. 不要在没有中间刷新或者定位调用的情况下在一个流中交替输入和输出 228
9.8 FIO40-C. 在fgets()或者fgetws()失败时重置字符串 230
9.9 FIO41-C. 不要用有副作用的流作为实参调用getc()、putc()、getwc()或者putwc() 231
9.10 FIO42-C. 在不再需要时关闭文件 234
9.11 FIO44-C. 对fsetpos()只使用fgetpos()返回的值 237
9.12 FIO45-C. 避免访问文件时出现TOCTOU竞争条件 239
9.13 FIO46-C. 不要访问已关闭文件 242
9.14 FIO47-C. 使用有效格式字符串 243
第10章 环境(ENV) 247
10.1 ENV30-C. 不要修改某些函数返回值引用的对象 247
10.2 ENV31-C. 在可能使某个环境指针无效的操作之后不要依赖该指针 252
10.3 ENV32-C. 所有退出处理程序必须正常返回 255
10.4 ENV33-C. 不要调用system() 258
10.5 ENV34-C. 不要保存某些函数返回的指针 264
第11章 信号(SIG) 270
11.1 SIG30-C. 在信号处理程序中只调用异步安全函数 270
11.2 SIG31-C. 不在信号处理程序中访问共享对象 277
11.3 SIG34-C. 不在可中断的信号处理程序中调用signal() 280
11.4 SIG35-C. 不从计算性异常信号处理程序中返回 283
第12章 错误处理(ERR) 286
12.1 ERR30-C. 在调用已知设置errno的库函数之前,将errno设置为0,只在函数返回表示故障的值之后才检查errno 286
12.2 ERR32-C. 不要依赖不确定的errno值 292
12.3 ERR33-C. 检测并处理标准库错误 295
第13章 并发性(CON) 308
13.1 CON30-C. 清理线程特定存储 309
13.2 CON31-C. 不要在互斥体被锁定时删除它们 312
13.3 CON32-C. 从多个线程访问位域时避免数据竞争 315
13.4 CON33-C. 使用库函数时避免竞争条件 318
13.5 CON34-C. 用正确的存储持续期声明线程间共享的对象 320
13.6 CON35-C. 以预定义顺序加锁,避免死锁 327
13.7 CON36-C. 将可能不合逻辑地唤醒的函数包装在一个循环中 331
13.8 CON37-C. 不要在多线程程序中调用signal() 334
13.9 CON38-C. 使用条件变量时保持线程安全性和活性 336
13.10 CON39-C. 不要加入或者断开之前已经加入或者断开的线程 343
13.11 CON40-C. 不要在一个表达式中两次引用同一个原子变量 344
13.12 CON41-C. 将可能不合逻辑地失败的函数包装在一个循环中 348
第14章 杂项(MSC) 351
14.1 MSC30-C. 不要使用rand()函数生成伪随机数 352
14.2 MSC32-C. 正确地设置伪随机数生成器的种子 354
14.3 MSC33-C. 不要向asctime()函数传递无效的数据 357
14.4 MSC37-C. 确保控制永远不会到达非void函数的结束位置 359
14.5 MSC38-C. 如果预定义标识符只能以宏的形式实现,不要将其当作对象处理 362
14.6 MSC39-C. 不要在va_list的值不确定时调用va_arg() 363
14.7 MSC40-C. 不要违反约束 365
附录A 词汇表 370
附录B 未定义行为 375
附录C 未指定行为 384
参考文献 387

教学资源推荐
作者: [美] 凯·霍斯特曼(Cay Horstmann) 著
作者: 裘宗燕,李安邦 编著
作者: (美)Steven S.Muchnick
作者: (英)Lawrence C. Paulson
参考读物推荐
作者: (德)Marko Boger
作者: (美)Charles Petzold
作者: 胡劲寒 编著