目录
如果裸指针被意外写坏时(写内存写越界或者非法的类型转换后非法写入等错误代码)指针会指向非法内存,并且我们很难察觉也无法辨识该指针是否是一个有效的指针,当我们使用该指针是就会发生严重的问题甚至宕机,如何检测指针被意外写坏了呢,我们可以通过特殊的方式存储指针本身的值,并且给指针添加标记位如果被指针被写坏了,通过检测标记位来辨别指针是否被写坏。
一 内存结构
假设指针32位(64系统下64位),用64位保存32位指针,上面第一排 高32位记为 hd,下面低32位记为ld
白红色 :为指针的值,数字为31,30,………,00为指针的内存结构由高32位到低0位存储
浅蓝色:指针flag,值可以任意填,图中全是1,白色部分默认是0,则上图64位数值为0Xaaaaaaaa55555555
0Xaaaaaaaa 二进制 10101010101010101010101010101010
0X55555555二进制 01010101010101010101010101010101
二 取指针内容检测指针标记
获取指针内容:把图中钱蓝色变0然后上下两个32位整数求或运算结果为指针的值,p = (ld & 0Xaaaaaaaa) | (hd & 0x55555555)
判断指针是否被写坏:((hd & 0Xaaaaaaaa) == 0Xaaaaaaaa) &&((ld &0x55555555 )== 0x55555555)
三 封装实现及测试用例
#include <cstddef>
#include <cstdio>
#include <stdexcept>
#include <sys/mman.h>
#include <cstring>
#include <signal.h>
#include <execinfo.h>
#include <sys/user.h>
#if defined(__x86_64__) || defined(WIN64)
#define SPO_MAGIC_NUM 0X8000000000000000
#define SPO_MAGIC_NUM_H 0X5555555555555555
#define SPO_MAGIC_NUM_L 0XAAAAAAAAAAAAAAAA
#define SPO_FLAG_H (0XFFFFFFFFFFFFFFFF & SPO_MAGIC_NUM_L)
#define SPO_FLAG_L (0XFFFFFFFFFFFFFFFF & SPO_MAGIC_NUM_H)
#define SPO_DATA_TYPE unsigned long long
#define SPO_ERROR_MSG "SafePointer<Tp> Is not valid,FLAGH = 0X%lx,FLAGL = 0X%lx,pointer = 0X%lx\n"
#endif
#if defined(__i386__) || defined(_WIN32)
#define SPO_MAGIC_NUM 0X80000000
#define SPO_MAGIC_NUM_H 0X55555555
#define SPO_MAGIC_NUM_L 0XAAAAAAAA
#define SPO_FLAG_H (0XFFFFFFFF & SPO_MAGIC_NUM_L)
#define SPO_FLAG_L (0XFFFFFFFF & SPO_MAGIC_NUM_H)
#define SPO_DATA_TYPE unsigned long long
#define SPO_ERROR_MSG "SafePointer<Tp> Is not valid,FLAGH = 0X%llx,FLAGL = 0X%llx,pointer = 0X%llx\n"
#endif
template<typename Tp>
class SafePointer
{
public:
SafePointer() : nDataH(SPO_FLAG_H),nDataL(SPO_FLAG_L) {}
SafePointer(Tp *pointer)
{
nDataH = SPO_FLAG_H;
nDataL = SPO_FLAG_L;
SPO_DATA_TYPE pvalue = (SPO_DATA_TYPE) pointer;
int a = sizeof(void*);
for (size_t index = 0; index < sizeof(void *) * 8;)
{
nDataL = nDataL | (pvalue & (SPO_MAGIC_NUM >> index)/*取出要保存的位,其他位置为*/);//保有当前位的值到相应位愿
index++;
nDataH = nDataH | (pvalue & (SPO_MAGIC_NUM >> index)/*取出要保存的位,其他位置为*/);//保有当前位的值到相应位愿
index++;
}
}
bool IsPointerBad()
{
char eMsg[256] = {0};
printf("SafePointer<Tp> FLAGH = 0X%lx,FLAGL = 0X%lx\n",static_cast<unsigned long>(GetFlagH()), static_cast<unsigned long>(GetFlagL()));
return (GetFlagH() != SPO_FLAG_H || GetFlagL() != SPO_FLAG_L);
}
//获取高位fag
SPO_DATA_TYPE GetFlagH() const { return nDataH & SPO_MAGIC_NUM_L; }
SPO_DATA_TYPE GetFlagL() const { return nDataL & SPO_MAGIC_NUM_H; }
inline Tp& operator*() const {return *GetThrow(); }
inline Tp* operator->() const {return GetThrow(); }
private:
Tp * GetThrow() const
{
Tp * pPoint = (Tp *) ((nDataL & SPO_MAGIC_NUM_L) | (nDataH & SPO_MAGIC_NUM_H));
if (GetFlagH() != SPO_FLAG_H || GetFlagL() != SPO_FLAG_L)
{
char eMsg[256] = {0};
sprintf(eMsg, SPO_ERROR_MSG, static_cast<unsigned long>(GetFlagH()), GetFlagL(), pPoint);
throw std::runtime_error(eMsg);
}
if (pPoint == NULL)
{
char eMsg[256] = {0};
sprintf(eMsg,SPO_ERROR_MSG,GetFlagH(),GetFlagL(),pPoint);
throw std::runtime_error(eMsg);
}
return pPoint;
}
private:
SPO_DATA_TYPE nDataH;
SPO_DATA_TYPE nDataL;
};
class Parent {};
class Boy : public Parent
{
public:
Boy(int* pb_) : a(0),pb(pb_) {}
int GetB() { return *pb;}
public:
SafePointer<int> pb;
char a;
};
class Girl : public Parent
{
public:
Girl() : a(0){}
void SetA(char a_)
{
a = a_;
}
public:
char a;
};
int main(int argc, char **argv)
{
try
{
int *pint = new int;
printf("Right pointer address = 0X%llx\n",pint);
Parent* pPar = new Boy(pint);
Girl* pGirl = (Girl*)pPar;
pGirl->SetA(155);
Boy* pBoy =(Boy*)pPar;
pBoy->GetB();
return 0;
}catch (std::exception& e)
{
printf("Catch a exception,msg = %s\n",e.what());
}
SafePointer<Boy> pTmBoy= new Boy(new int);
pTmBoy->GetB();
}
运行效果
可以看到内存被意外写坏后排成功检测到病抛出异常。