zoukankan      html  css  js  c++  java
  • 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 ”,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] = ‘’;

    }

    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 ”,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()修改对齐的内存

  • 相关阅读:
    html5实现GIF图效果
    响应式网页设计简单入门(强烈推薦!!!!)
    form表单提交和ajax表单提交,关于移动端如何通过软键盘上的【搜索】和【前进】进行提交操作
    【JavaScript】Write和Writeln的区别
    HTML表格中各元素标签的位置对style属性有效性的影响
    HTML textarea 莫名其妙出现几个空格的原因
    PHP 正则匹配手机号
    极简主义法编写JavaScript类
    jQuery on绑定事件
    jQuery ajax() 参数,回调函数,数据类型,发送数据到服务器,高级选项
  • 原文地址:https://www.cnblogs.com/sunnypoem/p/11368473.html
Copyright © 2011-2022 走看看