一起不太引起注意的越界操作

本文探讨了C++中越界操作可能导致的未知后果,如数据破坏和潜在的安全风险。通过实例演示,作者揭示了强制类型转换和不同数据类型之间的交互如何触发问题,并提供了预防措施,如类型一致性检查和适当的数据类型选择。
摘要由CSDN通过智能技术生成

谁都知道越界会带来很多问题。但究竟什么问题?会有什么后果,估计很少人能够说得清楚吧

C++缺少一些保护机制,因此,越界了之后,事实上是无法预估的,有很大可能会“没事”,有时候会莫名其妙错误

一般来说,对于一个成熟的C++开发者,或多或少会碰到越界的问题

 

最近在处理一个程序,经过跟踪发现一处隐藏的bug,一开始百思不得其解,后来算是搞清楚了。

为了保密的原因,隐去项目的信息,以下的代码均是测试代码,并非真实项目中的代码,但表达原理是一致的。

(so,测试代码就是测试代码,请不要直接copy使用,也不要拿代码规范去要求)

先来看看,代码:

//参数定义:注意123的顺序
static USHORT g_us1 = 1201;
static USHORT g_us2= 2202;
static USHORT g_us3 = 3203;

//实际调用代码:
void TestInput(int *nGet)
{
    *nGet = 0;
}

printf("ori: %d %d %d \r\n",g_us1,g_us2,g_us3);
TestInput((int *)&g_us2);  //这里我用了强制转换,是因为我当前的IDE是编译有错误,实际很多IDE是不用强制转换,也不会报错的。
printf("TestInput: %d %d %d \r\n",g_us1,g_us2,g_us3);

会打印什么数据?

 

问题不在g_us2本身,而是g_us3的值,莫名的被改变了,而且是悄无声息的被改掉。

这种结果 就不是我们自己想要的。

 

如果g_us3是一个循环判断,很有可能导致死循环;

 

这个问题就严重了,程序也很容易就挂掉的。

 

为了搞清楚这类事情,再改进下代码:

//参数定义:注意123的顺序(相对上面的,调整为312)
static USHORT g_us3 = 3203;

static USHORT g_us1 = 1201;
static USHORT g_us2= 2202;

//实际调用代码:
void TestInput(int *nGet)
{
    *nGet = 0;
}

printf("ori: %d %d %d \r\n",g_us1,g_us2,g_us3);
TestInput((int *)&g_us2);  //这里我用了强制转换,是因为我当前的IDE是编译有错误,实际很多IDE是不用强制转换,也不会报错的。
printf("TestInput: %d %d %d \r\n",g_us1,g_us2,g_us3);

此时输出:

这样貌似没有问题了,实际隐藏更深的问题,因为你都不知道哪个值被改掉了!!

 

为了弄得明白,再改进下:

//申明变量依然跟第一种一样:
static USHORT g_us1 = 1201;
static USHORT g_us2= 2202;
static USHORT g_us3 = 3203;

void TestInput(int *nGet)
{
    *nGet = 0x12345678;   //这里不再0,而是一个比较有特色的值
}

printf("ori: %d %d %d \r\n",g_us1,g_us2,g_us3);
TestInput((int *)&g_us2);
printf("TestInput: %d %d %d \r\n",g_us1,g_us2,g_us3);

而他本身存在低2字节

 

这个要跟具体编译,大小端问题(这点要说明下,单片机可能跟PC不一样)

 

再看一个例子:

static USHORT g_us1 = 1201;

static USHORT g_us2= 2202;

static UINT g_us3 = 0x09999999;  //这里变成UINT类型,且给个比较大值



void TestInput(int *nGet)

{

    *nGet = 0x12345678;

}



printf("ori: %d %d %d \r\n",g_us1,g_us2,g_us3);

TestInput((int *)&g_us2);

printf("TestInput: %d %d %d \r\n",g_us1,g_us2,g_us3);

 

大家可以思考下,会打印什么数据?

对的,一半的值被改掉了!这种人马组合,最恶心了,不仔细点还真发现不了

 

好了,对于一般情况下,当然这样使用也不多,但一个软件多多少少会涉及到一点,特别是一些单片机程序,有时候为了节省点2字节空间,减少点类型转换,就可能不小心就写成这样了。(挖了一个坑,而且不一定会有问题,比如说TestInput函数里并不是每次都赋值一个固定的值,比如说赋值0是在安全范围内,而1233333可能就是异常的源头)

        这样的情况,危险也是隐藏的,或大或没有问题。比如说,上面的g_us3,本身在每次应用的时候,都是重新赋值的(用的时候,也做了一些有效性的判断),当然问题也没有那么大了,但不可以乐观!

       我们实际项目中还存在一个前提:“TestInput”是个多处调用函数(通用函数),由于传入参数有些地方是4字节,有些是2字节,从而一股脑改成4字节,所以才会带来问题。

 

       经过以上分析与举例,相信大家应该明白,这个隐藏问题产生的原理以及影响的范围。

       自己也思考过,我们在设计的时候,如何避免:

  1. 这种作为“out”的参数(引用),必须类型一致,不要强制转换!此原则,同样适用于结构体!
  2. 如果实在不能做到类型一致,可以写两个不一样“TestInput”的函数。也可以用个临时变量先转换,转换成一致再调用。
  3. 如果可以的话,多用4字节类型,这两个字节省也省不了哪里去,反而带来隐患。毕竟4字节转2字节还是可控的,2字节转4字节就会有点隐患!

 

好了,以上就是我的一点思考与建议,我觉得比较有意思,希望对大家有所帮助启发。大家也可以回过头去看看,自己的代码是否存在类似情况!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
结构体字节对齐引起的访问越界问题通常出现在以下情况下: 1. 结构体中存在字节对齐的成员变量,访问该成员变量时,可能会访问到结构体之外的内存空间。例如,如果某个成员变量的长度是4字节,但该结构体的字节对齐方式是8字节,那么访问该成员变量时,可能会访问到结构体之外的4字节内存空间,从而导致访问越界。 2. 结构体中存在数组类型的成员变量,访问该数组时,可能会访问到数组之外的内存空间。例如,如果一个数组的长度是10,但该结构体的字节对齐方式是16字节,那么访问该数组时,可能会访问到数组之外的6字节内存空间,从而导致访问越界。 为了避免结构体字节对齐引起的访问越界问题,可以采取以下措施: 1. 使用编译器提供的或自定义的对齐方式,以确保结构体中成员变量的对齐方式符合要求。 2. 避免在结构体中使用数组类型的成员变量,或者使用动态内存分配等方法来管理数组,以确保数组访问不会越界。 3. 避免在结构体中使用位域类型的成员变量,或者使用位运算等方法来确保位域访问不会越界。 4. 在编写代码时,注意检查结构体中的成员变量访问是否越界,可以使用断言等方法来检查访问边界。 总之,结构体字节对齐引起的访问越界问题需要引起重视,需要在编写代码时注意结构体成员变量的对齐方式和访问边界,以确保程序的正确性和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值