Vector 安全地擦除其内存

问题描述 投票:0回答:2

我需要创建一个安全的、自清理的内存缓冲区,当被清除或超出范围时,它不会在内存中留下任何内容的痕迹。

我的示例代码显示了我的第一个实验 vectorc 和 std::vector 之间的比较。您可以在输出中看到,vectorc 在调用 clear() 时覆盖了它的内存,而 std::vector 的内容在 clear() 之后仍然可以访问。

我担心编译器可能会在某些情况下优化内存覆盖,如果在 clear() 或超出范围(析构函数)后不再访问内存。使用 volatile 是否有效,就像在 clear() 中注释掉的行一样? memset() 不喜欢 volatile void*,所以我不得不再次抛弃它。必须有更好的方法来做到这一点。

我知道增长向量和调整大小可能会留下内容,因为内存块可能会移动到不同的位置,但让我们忽略这一点以保持示例简短。

我访问 size() 后面的内存直到 capacity()。这可能会导致未定义的行为,或者我这样做可以吗?

对这个话题有什么想法吗?有更好的解决方案吗?

vectorc.cpp:

#include <vector>
#include <cstring>
#include <iostream>

template<typename T> class vectorc : public std::vector<T>
{
  public:

  ~vectorc()
  {
    clear();
  }

  void clear()
  {
    // volatile T* ptr = std::vector<T>::data(); ???
    T* ptr = std::vector<T>::data();
    memset((void*)ptr, 0, std::vector<T>::capacity()); // May have been shrinked, so size may leave memory behind?!
    std::vector<T>::clear();    
  }
};

void printmem(const std::vector<char>& v)
{
   const char* ptr = v.data();
   std::cout << "capacity=" << v.capacity() << ", size=" << v.size() << ", content=\"";
   for(size_t i = 0; i < v.capacity(); ++i, ++ptr)
     std::cout << *ptr;
   std::cout << "\"" << std::endl;
}

int main()
{
  std::cout << "start" << std::endl;

  std::cout << "vectorc:" << std::endl;
  {
    vectorc<char> vs;
    vs.push_back('s');
    vs.push_back('e');
    vs.push_back('c');
    vs.push_back('r');
    vs.push_back('e');
    vs.push_back('t');

    printmem(vs);
    vs.clear();
    printmem(vs);
  }
  // vs went out of scope and I want all it's allocated memory set to zero.

  std::cout << "std::vector:" << std::endl;
  {
    std::vector<char> vp;
    vp.push_back('p');
    vp.push_back('u');
    vp.push_back('b');
    vp.push_back('l');
    vp.push_back('i');
    vp.push_back('c');

    printmem(vp);
    vp.clear();
    printmem(vp);
  }

  std::cout << "end" << std::endl;
}

编译运行:

g++ vectorc.cpp
./a.out

输出:

start
vectorc:
capacity=8, size=6, content="secret"
capacity=8, size=0, content=""
std::vector:
capacity=8, size=6, content="public"
capacity=8, size=0, content="public"
end
c++ security
2个回答
0
投票

由于搜索引擎似乎总是将这个问题放在安全擦除 std::vector 内存的结果之上,即使评论已经表明使用自定义分配器,如果有解决方案的答案也会很有帮助。

下面是一个使用自定义分配器的实现,该分配器通过在释放时提供安全的内存擦除来扩展标准 std::allocator 类。它实际上基于一篇比 OP 问题早 3 年的博客文章:https://blog.noser.com/securely-deallocate-a-stdvector-or-a-stdstring/

// Secure erazing of memory macro for Windows and non-Windows systems
#if defined(_WIN32)
#include <windows.h>
#define secure_zeromem(mem,size) do { RtlSecureZeroMemory (mem, size); } while (0)
#else
#define secure_zeromem(mem,size) do { volatile char *burnm = (volatile char *)(mem); int burnc = size; while (burnc--) *burnm++ = 0; } while (0)
#endif


template <class T>
class SecureAllocator
    : public std::allocator<T>
{
public:
    template<class U> struct rebind
    {
        typedef SecureAllocator<U> other;
    };

    SecureAllocator() throw()
        : std::allocator<T>()
    {

    }

    SecureAllocator(const SecureAllocator& other) throw()
        : std::allocator<T>(other)
    {

    }

    template <class U> SecureAllocator(const SecureAllocator<U>& other) throw()
        : std::allocator<T>(other)
    {

    }


    void deallocate(T* ptr, std::size_t n) {
        if (ptr != nullptr && n > 0) {
            secure_zeromem(ptr, n * sizeof(T));
        }
        std::allocator<T>::deallocate(ptr, n);
    }
};

typedef std::vector<unsigned char, SecureAllocator<unsigned char> > SecureVector;
typedef std::basic_string<char, std::char_traits<char>, SecureAllocator<char> > SecureString;

0
投票

首先,你不应该实现一个继承自标准容器的类,比如

std::vector
,因为它是许多错误的来源,比如内存泄漏。

std::vector
的析构函数和
clear()
成员函数的标准实现不保证容器不留下其内容的痕迹,而是要求调用元素的析构函数(cppreference)。在C++17之前,所有的标准实现都是使用allocator的
destroy()
成员函数来销毁元素,但是
std::destroy_at()
的引入让几乎所有的标准实现都使用了它。因此,您不能使用实现其
destroy()
成员函数的自定义分配器,因为不能保证标准容器会在后台使用它。 正如@Mounir IDRASSI 所建议的那样,您不能使用带有
deallocate()
成员函数的自定义分配器,在释放内存之前清理内存,因为
clear()
成员函数不保证底层缓冲区被释放。

所以,你应该做两件不同的事情:实现你的矢量容器,可能在引擎盖下使用

std::vector
容器;使用标准容器,但将元素 T 包装在自定义类中,当调用析构函数时,可以根据需要清理内存。

如果你想避免编译器的优化,你总是可以使用

volatile
关键字。没有问题。

© www.soinside.com 2019 - 2024. All rights reserved.