Clearing memory securely and reallocations

前端 未结 5 1777
别跟我提以往
别跟我提以往 2020-12-24 07:11

Following the discussion here, if you want to have a secure class for storing sensitive information (e.g passwords) on memory, you have to:

  • memset/clear the me
5条回答
  •  [愿得一人]
    2020-12-24 08:06

    Despite showing up in the coredump, the password isn’t actually in memory anymore after clearing the buffers. The problem is that memcpying a sufficiently long string leaks the password into SSE registers, and those are what show up in the coredump.

    When the size argument to memcpy is greater than a certain threshold—80 bytes on the mac—then SSE instructions are used to do the memory copying. These instructions are faster because they can copy 16 bytes at a time in parallel instead of going character-by-character, byte-by-byte, or word-by-word. Here’s the key part of the source code from Libc on the mac:

    LAlignedLoop:               // loop over 64-byte chunks
        movdqa  (%rsi,%rcx),%xmm0
        movdqa  16(%rsi,%rcx),%xmm1
        movdqa  32(%rsi,%rcx),%xmm2
        movdqa  48(%rsi,%rcx),%xmm3
    
        movdqa  %xmm0,(%rdi,%rcx)
        movdqa  %xmm1,16(%rdi,%rcx)
        movdqa  %xmm2,32(%rdi,%rcx)
        movdqa  %xmm3,48(%rdi,%rcx)
    
        addq    $64,%rcx
        jnz     LAlignedLoop
    
        jmp     LShort                  // copy remaining 0..63 bytes and done
    

    %rcx is the loop index register, %rsi is the source address register, and %rdi is the destination address register. Each run around the loop, 64 bytes are copied from the source buffer to the 4 16-byte SSE registers xmm{0,1,2,3}; then the values in those registers are copied to the destination buffer.

    There’s a lot more stuff in that source file to make sure that copies occur only on aligned addresses, to fill in the part of the copy that’s leftover after doing 64-byte chunks, and to handle the case where source and destination overlap.

    However—the SSE registers are not cleared after use! That means 64 bytes of the buffer that was copied is still present in the xmm{0,1,2,3} registers.

    Here’s a modification of Rasmus’s program that shows this:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    inline void SecureWipeBuffer(char* buf, size_t n){
      volatile char* p = buf;
      asm volatile("rep stosb" : "+c"(n), "+D"(p) : "a"(0) : "memory");
    }
    
    int main(){
      const size_t size1 = 200;
      const size_t size2 = 400;
    
      char* b = new char[size1];
      for(int j=0;j

    On my mac, this prints:

    0\0LOLWUT130\0LOLWUT140\0LOLWUT150\0LOLWUT160\0LOLWUT170\0LOLWUT180\0\0\0
    

    Now, examining the core dump, the password only occurs one single time, and as that exact 0\0LOLWUT130\0...180\0\0\0 string. The core dump has to contain a copy of all registers, which is why that string is there—it’s the values of the xmm{0,1,2,4} registers.

    So the password isn’t actually in RAM anymore after calling SecureWipeBuffer, it only appears to be because it is actually in some registers that only appear in the coredump. If you’re worried about memcpy having a vulnerability that could be exploited by RAM-freezing, worry no more. If having a copy of the password in registers bothers you, use a modified memcpy that doesn’t use the SSE2 registers, or clears them when it’s done. And if you’re really paranoid about this, keep testing your coredumps to make sure the compiler isn’t optimizing away your password-clearing code.

提交回复
热议问题