C语言安全编码摘录

news/2024/7/20 15:43:37 标签: c/c++, 内存管理

C语言安全编码规范

1 来源

《The SEI CERT C Coding Standard, 2016 Edition》

2 预处理器PRE

2.2避免不安全宏的参数出现副作用

一个不安全的函数宏在展开时多次使用或根本不使用某个参数,所以不要调用包含赋值、增量、减量、输入、输出等具有副作用参数的不安全宏。

 

#define ABS(x) (((x) < 0)?-(x):(x))

int m = ABS(++n)  

 

2.2.6NDEBUG&assert()

assert()宏是编码中整合诊断与测试的一种方便机制。assert宏的行为依赖于类对象的宏定义NDEBUG,若宏NDEBUG没有定义,assert()宏就会计算参数表达式的值,若结果为0,则调用abort()函数。若宏NDEBUG已被定义,assert宏入参表达式在断言中不会被计算。

assert(i++ > 0)

 

3 声明和初始化DCL

3.4不要声明或定义保留的标识符

#ifndef _MY_HEADER_H_

#define _MY_HEADER_H

static const size_t _max_limit = 1024;  

#endif /*_MY_HEADER_H_*/

以上_MY_HEADER_H__max_limit 可能其它定义冲突,去掉开头的下划线。

 

3.5.2动态大小的结构体正确用法

struct flexArrayStruct{

   size_t num;

   int data[];

};

void func(size_t array_size){

   struct flexArrayStruct  *structP = (struct flexArrayStruct *)

       malloc(sizeof(struct flexArrayStruct)+sizeof(int)*array_size);

   if (NULL == structP){ /*Handle malloc failure*/

   }

   structP->num = array_size;

   for(size_t i = 0; i < array_size; ++i){

      structP->data[i] = 1;

   }

}

 

3.6 结构跨越信任边界时避免信息泄露

意思是说如下的结构体,因为对齐的因素,实际大小占12字节,拷贝的时候,会将填充的信息拷贝到其它地方。

struct test{

int a;

char b;

int c;

};

extern int copy_to_user(void *dst,void *src, size_t size);

void do_stuff(void *usr_buf){

struct test arg={.a =1, .b =2, .c=3};

copy_to_user(usr_buf, &arg, sizeof(arg));

}

解决办法之一,用结构体1字节对齐。

 

3.7.9 超长标识符

C99外部标识符最长为31的限制

 

3.8不要在switch语句第一个case前声明变量

变量i被实例化为块内部自动存储值,但初始化语句不会被执行。

switch(expr){

int i = 4;

f(i);

case 0:

i = 18;

default:

printf(“%d\n”,i);

}

 

4 表达式EXP

4.1.1不要依赖序列点之间的求值顺序

void func(int i, int *b){

int a = i + b[++i];

}

 

4.10.1不要比较填充数据

如下代码,如果结构体因为对齐有填充,不能用memcmp比较两个结构体的内容。

struct s{

char c;

int i;

char buffer[13];

};

void compare(const struct s*left, const struct s *right){

if (0 == memcmp(left, righe, sizeof(struct s))){

/**/

}

}

 

5 整数INT

5.1.1.1保证无符号整数操作不会出现回绕

加法保护

void func(unsigned int ui_a, unsigned int ui_b){

unsigned int usum;

if (UINT_MAX - ui_a < ui_b){

   /*Handle error*/

}else{

   usum = ui_a + ui_b;

}

}

 

5.1.2.1减法操作保护

void func(unsigned int ui_a, unsigned int ui_b){

unsigned int udiff;

if (ui_a < ui_b){

   /*Handle error*/

}else{

   udiff = ui_a - ui_b;

}

}

 

5.1.3.1 乘法操作保护

测试乘法的操作数,以保证不会出现回绕.

num_vertices = M;

if (num_vertices > SIZE_MAX / sizeof(vertex_t){

/*Handle error*/

}

vetices = malloc(num_vertices * sizeof(vertex_t));

 

5.2.9保证整型转换不会丢失或错误解释数据

time函数在表示当前日历时间无效时,会将-1转换为time_t类型后的值。如果time_t的精度小于signed int的无符号整数类型,那么下面转换有误。

void func(void){

time_t now = time(NULL);

if (now != -1){    /*应改为now != (time_t)-1*/

    /*....*/

}

}

 

5.3.3.1有符号整数加法保护

两端都要做溢出保护

void func(signed int si_a, signed int si_b){

signed int sum;

if(((si_b > 0)&& (si_a > (INT_MAX -si_b))) ||

  ((si_b < 0)&&(si_a < (INT_MIN-si_b)))){

/*Handle error*/

    }else{

      sum = si_a + si_b;

}

}

 

5.4.1有符号除法保护

void func(signed long s_a, signed long s_b){

   signed long result;

   if ((s_b ==0) || ((s_a == LONG_MIN)&&(s_b == -1))){

   }else{

       result = s_a / s_b;

   }

}

 

5.5.2不要将表达式移动负数位或者移动大于等于操作数中存在的位数

无符号类型左位移保护

#include <limits.h>

#include <stddef.h>

#include <inttypes.h>

extern size_t popcount(uintmax_t);

#define PRECISION(x) popcount(x)

 

void func(unsigned int ui_a, unsigned int ui_b){

   unsigned int uresult = 0;

   if (ui_b >= PRECISION(UINT_MAX)){

      /*Handle error*/

   }else{

      uresult = ui_a << ui_b;

   }

}

 

5.6.1使用正确的整数精度

C中整数类型包含大小和精度两部分,大小表示一个对象使用的字节数,可以通过sizeof得到。一个整数类型的精度是它用来表示值的位数,不包括任何符号位和填充位。

例如,在一个平台上用64位来存储无符号整数,但仅用48位表示值,左移56位将导致未定义的行为。

6 浮点数FLP

6.1.1不要用浮点数作为循环计数器

void func(void){

for(float x = 0.1f; x <= 1.0f; x+= 0.1f){

/*Loop may iterate 9 or 10times*/

}

}

 

6.1.3 浮点数精度导致死循环

for(float x = 100000001.0f; x <= 100000010.0f; x += 1.0f){

/*Loop may not terminate*/

}

 

6.3.2floatint要确保float值的范围适合int

extern size_t popcount(uintmax_t);

#define PRECISION(umax_value) popcount(umax_value)

void func(float f_a){

int i_a;

if (PRECISION(INT_MAX) < log2f(fabsf(f_a)) ||

   (f_a != 0.0F && fabs(f_a) < FLT_MIN)){

/*Handle error */

}else{

  i_a = f_a;

}

}

 

6.5.1不要使用对象表示来比较浮点值

-0.0 0.0是等价的,但是对象表示中使用的位模式是不相同的。

struct S{

  int i;

  float f;

};

bool are_equal(const struct S *s1, const struct S *s2){

  if (!s1 && !s2)return true;

  else if (!s1 || !s2) return false;

  return 0 == memcmp(s1, s2, sizeof(struct S));

}

 

7 数组ARR

7.1.8索引超出范围的访问

多维数组访问时,数组下标不要搞混了。

 

7.2.4确保变长数组的大小参数在有效范围内

enum {N1 = 4096};

void *func(size_t n2){

if (n2 > SIZE_MAX/(N1*sizeof(int))){

    return NULL;

}

typedef int A[n2][N1];

A *array = malloc(sizeof(A));

if (!array){

    return NULL;

}

/*init*/

return array;

}

 

8 字符和字符串STR

8.1.1不要试图修改字符串常量

char *p = “string literals”;

p[0] = ‘S’; //未定义行为

 

8.2保证字符串的存储具有足够的空间容纳字符数据和null结尾符

void copy(size_t n, char src[n], char dest[n]){

   size_t i;

   for (i = 0; src[i] && (i < n-1); ++i){  //合规解决方法

      dest[i] = src[i];

   }

   dest[i] = ‘\0’;

}

 

8.2.9 fscanf()合规方法,防止缓冲区溢出

void get_data(void){

char buf[1024];

if (1 != fscanf(stdin, “%1023s”, buf)){

   /*Handle error*/

}

}

 

8.3不要将非null结尾的字符序列当做字符串传递给库函数

void func(void){

char c_str[3] = “abc”;

printf(“%s\n”,c_str); /*没有null结尾的字符序列,传给了printf*/

}

 

9 内存管理MEM

9.3.5含有灵活数据成员的结构拷贝

struct flex_array_struct{

size_t num;

int data[];

};

void print_array(struct flex_array_struct struct_p){

for (size_t i = 0; i < struct_p.num; ++i){

    printf(“%d”, struct_p.data[i]);  /*error当以传值方式传递入参时,灵活数组成员的大小不会被考虑,因此,只有num被拷贝*/

}

}

 

9.4.3只释放动态分配的内存

下面的不合规代码中,realloc的参数,buf不是指向动态分配的内存。

char buf[1024];

char *p = (char *)realloc(buf, 2048);  /*error*/

 

9.5.4为对象分配足够的内存

struct tm *tmb;

tmb = (struct tm *)malloc(sizeof(tmb)); /*错误,只申请了指针所占的大小*/

 

9.6不要使用realloc()修改对齐的内存

转载于:https://www.cnblogs.com/sunnypoem/p/11368473.html


http://www.niftyadmin.cn/n/1719006.html

相关文章

dart语言学习(七)— dynamic动态类型

一般在使用泛型时使用 void main() {var a;a 10;a "Dart";dynamic b 20;b "huangxiaoguo";var list new List<dynamic>();list.add("1");list.add("value");list.add(true);print(list);}[1, value, true] Exited

11降低版本显示语法错误_iOS14.1正式版推送,全面修复iOS14版本13大问题

今日凌晨&#xff0c;苹果正式推出了iOS14.1正式版本以及iPadOS14.1正式版&#xff0c;这也是3天以后由iOS14.1GM版本过渡而来的正式版。iOS14.1正式版基本算得上是iOS14这个大版本以来第一个比较正式、修复比较完善的版本。并且在即将推出的iPhone12以及iPhone12 Pro上&#x…

dart语言学习(八)— 算术运算符

运算符 加减乘除&#xff1a;、-、*、/、~/、% 递增递减&#xff1a;var、var–、--var、var– 案例 void main(List<String> args) {int a 92;int b 3;print("a b${a b}");print("a - b${a - b}");print("a * b${a * b}");print(&…

VPP概述汇总

一、背景介绍 http://www.360doc.com/content/18/0428/20/53742993_749517107.shtml 《高性能网关设备及服务实践》这篇博文&#xff0c;介绍了传统内核网络协议栈技术的瓶颈及怎么突破&#xff0c;从而引入DPDK VPP这种处理方式。 二、高性能 性能提升方法。 架构&#xff1a…

dart语言学习(九)— 关系运算符

运算符 运算符&#xff1a;、!、>、<、>、< 判断内容是否相同使用 案例 void main(List<String> args) {int a 5;int b 9;print("ab ${a b}");print("a!b ${a ! b}");print("a>b ${a > b}");print("a<b …

遥感云大数据在灾害、水体与湿地领域典型案例及GPT模型

详情点击链接&#xff1a;遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型 第一&#xff1a;基础 一&#xff1a;平台及基础开发平台 GEE平台及典型应用案例&#xff1b; GEE开发环境及常用数据资源&#xff1b; ChatGPT、文心一言等GPT模型 JavaScript基础&am…

VPP tips

VPP tips 1.性能从何而来。 原文链接&#xff1a; http://www.360doc.com/content/18/0428/20/53742993_749517107.shtml https://steeven.iteye.com/blog/2347150 DPDK代码级别性能优化总结 https://www.jianshu.com/p/346bf99b2fb1 https://www.jianshu.com/p/ed914b24f6da h…

dart语言学习(十)— 逻辑运算符

运算符 运算符&#xff1a;!、&&、|| 针对布尔类型运算 案例 void main(List<String> args) {bool isTrue true;print("!isTrue ${!isTrue}");bool isFalse false;print("isTrue && isFalse ${isTrue && isFalse}");p…