新闻中心

EEPW首页 > 牛人业话 > 数组越界真可怕,莫名就闯到了别人家

数组越界真可怕,莫名就闯到了别人家

作者:天雷君时间:2018-11-30来源:电子产品世界收藏

  4

本文引用地址:http://www.eepw.com.cn/article/201811/395096.htm

  笔者曾经在《天灵灵地灵灵,遥控为何会失灵》一文中讲述过报文解析程序的一部分原理和设计,

  “射频位到数据位采用了曼彻斯特编码形式,以射频位01表示数字位1,以射频位10表示数字位0,BCM采用上升沿触发中断的方式,根据相邻两个上升沿之间的时间间隔来赋值射频位。BCM根据遥控报文的格式提取出“数据场”中的射频位位流,然后进行曼彻斯特解码,计算出数据位位流,进而提取出字节形式的数据。。。。相邻两个上升沿的间隔取值只可能为2T、3T、4T,T为射频位位宽。”

  前文所述“把数据位0或1这个‘大象’放到前面打开的‘冰箱’里”,指的就是赋值射频位的过程,

  如果是2T,执行StoreRfBit(0); StoreRfBit(1);

  如果是3T,执行StoreRfBit(1); StoreRfBit(0); StoreRfBit(1); 或者StoreRfBit(0); StoreRfBit(1);

  StoreRfBit(0);

  如果是4T,执行StoreRfBit(1); StoreRfBit(0); StoreRfBit(0); StoreRfBit(1);

  遥控报文的格式非常规整,能够很容易地找到数据场的第一位,然后次序收完数据场的最后一位,本不该出现接收报文失败的问题。既然无法通过分析代码找出bug,那就只好祭出“调试大法”了。继续添加测试语句:

  if(Rf_bit_count >= RF_RAWBIT_LEN){

  Rf_frame_times++;

  StoreRfCompleteIdx();

  SetRfFrameComplete();

  }

  洒家这次没有吝惜RAM,开了个足以存储好几条报文射频位位宽数据的轮转型大,在判断接收到一帧完整的报文语句那里存储完整报文最后一个数据位在轮转型大中的下标位置,设置断点,运行到断点位置后,便可以从这个下标往前搜索,看看之前几个报文的射频位位宽数据是否有什么异常。

  聪明的读者肯定已经抢先一步意识到了,射频位位宽没有出现任何异常。报文头很规则,数据场很整齐,报文尾部也很利落。

  总之,所有的射频位数据安安静静地等待在时间的无涯荒野里,没有早一步,也没有晚一步,我遇上了,却没有轻轻地说一句:哦,原来你也在这里呵!

  洒家揉了揉因为看数据看得有些发胀的眼睛,缓缓走到窗台前,眯缝起眼睛,打量了一下远处笼罩在一片蜃气之中的青山。每每遇到难解的问题,洒家总要到窗台阳光的沐浴中,眺望一下远处的山,让脑袋放空,然后闭上眼睛,静下心来,等着灵感不请自来。

1543555761907915.gif

  中央空调沙沙的换气声音、同事的窃窃私语声不时入耳,我在心里不断回忆着有限的职业生涯里遇到的一个个bug,同时盘算着当前程序可能的缺陷。报文格式的分段解析肯定没问题,射频位位宽的判断也没问题,射频位的赋值也没问题,想来都没有问题,这个bug隐藏地够深的。

  5

  还得调试!行文至此,我不禁怀疑起了自己的智商,为什么基本上所有难题的解决都是靠调试解决的?没有一次是灵光电闪,看代码直捣黄龙,找出bug的?

  被这个难题耗损了大半豪气的我,老老实实地开始调试起来。为了定位bug,笔者特意修改了程序结构,之前的程序是边接收边解析,实时性固然好,但是接收了几个数据便解析,不利于调试,所以改为收到两三条报文后,再集中进行解析。

  洒家看代码找bug的本事没有,设计调试方案找bug的能力还是有的。修改程序之后,调试下来,问题就慢慢浮上水面了。程序解析出报文头部,进入数据场之后,在赋值射频位的过程中,结果出现了Rf_bit_count从288累加到33的情形!!!

timg (1).gif

  奇怪了,288累加一下应该是289,怎么就变成33了?莫非1+1不等于2了不成?!!此处肯定有蹊跷,洒家打眼一瞧,马上看出了一点端倪,289和33正好差了一个256,就好像是把向高字节的进位吃掉了一般。问题开始变得有趣了!

  Rf_bit_count是个16位的数据,好像突然变成了8位数据,显然,它的高位字节发生了不为人知的变化!写过多年代码的洒家,立马想到了是和Rf_bit_count临近空间的数据搞的鬼,回到定义位置一看,

  uint8_t  Rx_buffer[RF_DATA_LEN];

  uint8_t  Rx_rawbit[RF_RAWDATA_LEN];

  uint16_t Rf_bit_count;

  一切都了然了,Rf_bit_count挨着Rx_rawbit这个数组,肯定是Rx_rawbit这个数组搞的事,这个数组越了界,就会改变Rf_bit_count的数据,下面就简单了,看看是不是这回事!

  我飞速地在纸上算了一下,当Rf_bit_count=288时,右移三位为36,Rf_rawbyte_idx =36时,Rx_rawbit数组正好越界,由于Rf_bit_count的位置正好在Rx_rawbit之后,而且所使用的处理器是大端模式,大端方式将高位存放在低地址,小端方式将低位存放在低地址。

1543555836409141.jpg

所以,Rf_bit_count的高字节正好挨着Rx_rawbit数组,肯定是对Rx_rawbit[36]赋值为0了,导致Rf_bit_count本来是1的高字节变成了0,于是289就变成了33,这样一来,判断接收到一条完整报文的语句里的if语句里的条件肯定是false了,于是,好好的一帧数据就这样被漏掉了。

  if(Rf_bit_count >= RF_RAWBIT_LEN){

  SetRfFrameComplete();

  }

  当然,细心的读者可能会比较奇怪,只需要接收288个射频位,为什么Rf_bit_count还会累加到289呢?

  这是因为,最后一个数据位可能是1也可能是0,最后统计到的射频位宽可能是2T、3T或者4T,它们执行的Rf_bit_count是不一样的,有时正好统计到288,那自然万事大吉,可以接收到报文,可是有时就会超过288,如前所述,这时它的高字节就有可能会被Rx_rawbit[36]吃掉,这就接收不到报文了。

  后记

  笔者学过一段时间的Java,Java对数组进行了一定的安全处理,在运行期间会自动判断数组下标是否越界,当时看的洒家羡慕得不得了。在计算机的世界里,C语言饱经沧桑,年头太老了,这种优异的特性显然指望不上,我等嵌入式工程师只能擦亮慧眼,始终保持警惕,要知道:数组越界真可怕,莫名就闯到了别人家。


上一页 1 2 3 下一页

关键词: 数组

评论


技术专区

关闭