一 memcpy缺陷分析
1.memcpy原型:
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n); #函数返回dest指针
2.缺陷分析:
(1)memcpy容易发生缓冲区溢出,如果dest长度小于待拷贝的字节数n,那么程序将发生缓冲区溢出;
(2)如果dest是高地址,src是低地址,拷贝可能发生错误,即源地址和目的地址重合部分在拷贝时被修改。
综上所述:memcpy参数较为简单,没有对程序安全性做进一步限制,将导致某些致命错误。
二 safe_memcpy
1.原型:
bool safe_memcpy(void *dst, const void *src, size_t len, const void *start, const void *end);
// dst指向目的地址
// src指向源地址
// len是待拷贝的字节数
// start指向目的地址开始地址
// end指向目的地址结束地址 即最后一个元素的后一个地址
2.实现分析:
safe_memcpy.hpp
#pragma once
#include <stdint.h>
#include <string.h>
static inline bool between(const uint8_t *start, const uint8_t *end, const uint8_t *p) {
return (p < end) && (p >= start); // 注意是p < end不是p <= end
}
static inline bool safe_check(void *dst, size_t len, const void *start, const void *end) {
if (len < 1) {
return false;
}
if (!dst || !start || !end) {
return false;
}
void *last_pos = ((uint8_t *)dst) + len - 1;
if (last_pos < dst) { // last_pos不能小于dst
return false;
}
return between((uint8_t *)start, (uint8_t *)end, (uint8_t *)dst) && // dst在start与end之间
between((uint8_t *)start, (uint8_t *)end, (uint8_t *)last_pos); // last_pos即待拷贝区间最后元素地址也必须在start与end之间
}
static inline bool safe_memcpy(void *dst, const void *src, size_t len, const void *start, const void *end) {
if (!safe_check(dst, len, start, end)) {
return false;
}
if (!src) {
return false;
}
memcpy(dst, src, len);
return true;
}
(1)解决缓冲区溢出问题:调用safe_memcpy需要指定目的地址的开始和结束地址,safe_memcpy会检查l目标空间是否能够容纳待拷贝字节数;
(2)解决低地址向高地址拷贝错误:如果源地址和目的地址部分区间重叠,通过指定start和end,safe_memcpy使用between((uint8_t *)start, (uint8_t *)end, (uint8_t *)last_pos)可以进行安全性判断。
3.测试:
test.cpp
#include <iostream>
#include "safe_memcpy.hpp"
int main() {
char src[64] = "123";
char dst[64] = "";
safe_memcpy(dst, src, strlen(src), dst, dst + 64);
std::cout << dst << std::endl;
return 0;
}