规则/建议条目全称
|
例外
|
笔记 /备注/点评
|
FIO01-C. 小心使用以文件名作为标识符的函数
|
|
具体来说指的是C99标准中的这几个函数:</p>
remove()
rename()
fopen()
freopen()
尽可能的使用文件描述符或者FILE指针操作文件,以避免访问非预期文件。 </td> </tr>
|
FIO02-C. 归一化来自不可信输入源的路径名
|
|
避免类似“使用相对路径访问任意文件”的漏洞
|
FIO03-C. 使用fopen和创建文件时不要随意假设
|
|
fopen函数用来打开一个已有文件或创建一个新文件,但fopen()并不能区别一个已有文件被打开待写还是创建了一个新文件。因此,可能导致文件错误覆盖或非法访问文件。推荐的方法是使用正确的fopen参数或者使用操作系统提供的更精细化的文件操作API,明确指定是新创建一个文件还是打开一个已有文件。
|
FIO04-C. 检测并处理输入输出错误
|
|
|
FIO05-C. 使用多重文件属性识别一个文件
|
|
例如文件属主,创建时间,文件创建和关闭相关信息等可以联合用于标识一个文件是否是“上次”操作的“那个”文件。
|
FIO06-C. 创建文件时正确设置文件访问权限
|
|
不要仅仅依赖操作系统的umask机制
|
FIO07-C. 优先使用fseek()于rewind()
|
|
使用rewind()后会重置流的错误指示符,而rewind()可以等效的使用:</p>
(void)fseek(stream, 0L, SEEK_SET)
来代替。 </td> </tr>
|
FIO08-C. 对已打开的文件使用remove()函数要小心
|
|
其行为是”具体实现相关”的。如果确实要删除已打开的文件,尽可能使用相应平台上支持该功能的函数。否则,避免删除已打开的文件。
|
FIO09-C. 跨系统传输二进制数据时要小心
|
|
不同系统上的结构体对齐方式、浮点数模型、每字节的bit位数、endian类型(little endian or big endian)等可能会有所差别。
|
FIO10-C. 小心使用rename()函数
|
|
rename函数的原型如下:</p>
int rename(const char *src_file, const char *dest_file);
如果dest_file所指向的文件抢先一步调用了rename()函数,则后调用rename()函数的行为结果是“未定义”的。在POSIX系统上,目标文件会被删除。在Windows系统上,后一次rename()调用会失败。 </td> </tr>
|
FIO11-C. 小心指定fopen()的文件操作模式参数
|
|
重复使用或同时使用多种模式字符串其行为是“具体实现相关”的。
|
FIO12-C. 优先使用setvbuf()于setbuf()
|
|
|
FIO13-C. 一次只“退回”一个字符
|
|
该条建议是针对ungetc()和ungetwc()说的,根据C99标准,这两个函数无论重复调用多少次只“确保”退回一个字符。所以,对同一个输入输出流多次调用ungetc()或ungetwc()时必须确保用一个read函数或者文件流定位函数分隔多次调用。
|
FIO14-C. 理解文件流的文本模式和二进制模式
|
|
注意fputs()、fseek()、ungetc()等函数在不同文件流操作模式下的调用结果区别
|
FIO15-C. 确保文件操作是在一个安全的目录中执行
|
|
安全目录的概念指的是除了当前进程的属主用户之外,其他用户无权限修改创建、重命名、删除或改变“安全目录”中的数据。安全配置是系统管理员的职责和任务,但对于程序员来说,需要在程序运行过程中检查目录相关权限、文件、属性等的完整性,避免“误操作”落入攻击者设定的“陷阱”。
|
FIO16-C. 创建一个受控环境限制文件访问
|
|
在*nix系统上,可以通过chroot、setuid/setgid、chidir等API调用,完成进程的文件访问限制在特定目录、特定资源、特定权限范围之内,避免进程被非法控制后“提权”、“越界”访问。
|
FIO17-C. 在使用fread()函数时不要依赖于null结尾字符
|
|
fread的函数原型如下:</p>
size_t fread(void * restrict ptr,
size_t size, size_t nmemb,
FILE * restrict stream)
fread读取数据的大小是受nmemb和size参数控制,且不会自动添加null字符到ptr。 </td> </tr>
|
FIO18-C. 绝不要认为fwrite()会在遇到null字符时停止写操作
|
|
fwrite的函数原型如下:</p>
size_t fwrite(const void * restrict ptr,
size_t size, size_t nitems,
FILE * restrict stream)
C99标准中没有要求fwrite()在遇到null字符时停止向文件写入数据,因此在向文件写入NTBS(Null Terminated Byte String)时记得nitems参数值应该是待写入NTBS长度+1(null字符)。</td> </tr>
|
FIO19-C. 禁止使用fseek()和ftell()来计算文件大小
|
|
fseek()在二进制流模式方式访问文件时,SEEK_END宏无法正确标识文件结尾。ftell()在文本模式方式访问文件时,无法返回文件指针当前的偏移量值。
|
FIO00-C. 小心创建格式化字符串
|
|
格式化字符串通常特指以%开头的转换指示符,包括必须的转换格式类型标志和可选的最小宽度、精度和长度修饰符。官方文档中所给出C99兼容格式化字符串搭配表格值得一看。
|
FIO30-C. 排除用户输入中的格式化字符串
|
|
|
FIO31-C. 禁止打开一个已经处于打开状态的文件
|
|
否则会产生一个“实现相关的”行为。
|
FIO32-C. 禁止对设备(文件)执行只适用于(普通)文件的API
|
|
假设一个浏览器程序违反了本条规则,则以下代码就有可能使该浏览器程序在浏览到包含如下代码的网页时导致鼠标死锁。</p>
在执行文件操作API前,先检查所打开文件的类型,避免对设备文件的误操作。 </td> </tr>
|
FIO33-C. 检查并处理输入输出错误避免未定义的行为
|
|
I/O函数执行成功与否一般可以通过检查函数的返回值来实现,但不要认为只要不是“错误”状态返回值就一定是执行成功了。部分函数执行成功时的返回值条件相比较出错返回值会更精确、更苛刻。例如snprintf()函数在执行失败时返回负数或>=缓冲区长度的值,在执行成功时返回成功操作的字符个数(非负数并且小于缓冲区长度)。
|
FIO34-C. 使用int来接受字符IO函数的返回值
|
FIO34-EX1: 如果一个字符输入/输出函数的返回值不会被和EOF整数常量表达式值进行比较,就没有必要遵循本规则。
|
|
FIO35-C. 当sizeof(int) == sizeof(char)时使用feof()和ferror()来检查文件结尾和文件错误
|
FIO35-EX1: C99中有一些函数不会返回字符但会返回EOF作为状态码。这些函数包括:fclose(), fflush(), fputs(), fscanf(), puts(), scanf(), sscanf(), vfscanf(), vscanf()。直接将这些函数的返回值与EOF进行比较是没有问题的。 FIO35-EX2: 如果能够保证sizeof(char) != sizeof(int),则不需要遵守本规则。
|
sizeof(char) == sizeof(int)的平台其实并不多见。
|
FIO36-C. 使用fgets()时不要假设会读取换行符
|
|
fgets()有可能读取时出现截断的问题,因此可能读取的内容不包含换行符。fgetws()也是类似。
|
FIO37-C. 不要假设fgets()在执行成功时一定会返回一个非空字符串
|
|
最典型的情况就是以“二进制模式”访问文件时,完全有可能读取到一行的内容的第一个字符就是一个null字符。
|
FIO38-C. 禁止使用一个FILE对象的拷贝来输入和输出
|
|
违反本规则有可能导致编译失败,也有可能引起DoS攻击。
|
FIO39-C. 禁止对一个流交替输入和输出而不间隔使用flush或者流指针定位函数调用
|
|
否则会导致“未定义”行为。
|
FIO40-C. fgets()调用失败时执行字符串重置操作
|
FIO40-EX1:如果在fgets()或者fgetws()执行后,相关字符串立刻失去作用域范围或者失去引用,则无需字符串重置。
|
fgets()在调用失败时会向缓冲区中写入不确定的内容。
|
FIO41-C. 禁止向getc()或者putc()函数传递有副作用的流参数
|
|
getc()和putc()有可能是以宏的形式来实现,因此在向这两个函数传递参数不当(例如传递一个表达式)时可能会造成参数被执行多次的情况。
|
FIO42-C. 确保文件在不再使用时被正确关闭
|
|
否则可能会导致文件描述符耗尽引起的DoS攻击。
|
FIO43-C. 禁止在共享目录中创建临时文件
|
FIO43-EX1: 如果所有目标实现代码都是在一个安全目录中执行则TR24731-1 tmpfile_s()可以不受本条规则限制使用。
|
|
FIO44-C. 仅使用fgetpos()的返回值作为fsetpos()的参数值
|
|
否则会产生一个“未定义”行为。
|
</tbody> </table>