类的实例的私有成员变量被改写
最近业务中遇到一个很奇怪的问题,在某些状态下,一个实例的首个指针成员变量的值(即指针指向的地址)被修改。原本这个指针变量的值为 0x7555555aa5555580,结果在不知道何时被改写成了 0x7555555aa5555500。如果在这个类的声明时,在该指针成员变量之前声明一个 int,则发现那个 int 会在某刻被改写,虽然成功绕过了因指针成员变量的值被改写而导致的错误地址使用。但是,这并不是个优雅的解法。
sprintf 的锅
经过同事两三天研究尝试,终于发现,是在上层 SDK 在打印 log 时,使用了 sprintf(char *string, char *format [,argument,…]),上层 SDK 在申请 char * 时申请了 20 个,但是在 sprintf 时,char * format 转换成字符串之后的数量刚好也是 20 个,而再加上字符串结尾 “\0”,正好多写了一个字节的内存。而那个指针变量的值 0x7555555aa5555580 在内存中的实际存放是 80 55 55 … 所以,刚好把第一个 80 改写成了 00,导致了类的实例的私有成员变量被改写。
尽量使用 snprintf
参考 int snprintf(char *str, size_t size, const char *format, …) ,
(1) 如果格式化后的字符串长度小于等于 size,则会把字符串全部复制到 str 中,并给其后添加一个字符串结束符 \0;
(2) 如果格式化后的字符串长度大于 size,超过 size 的部分会被截断,只将其中的 (size-1) 个字符复制到 str 中,并给其后添加一个字符串结束符 \0,返回值为欲写入的字符串长度。
这就足够安全保证不会写多内存,导致内存错误,加强内存安全。