[Lldb-commits] [PATCH] D75200: AddressSanitizer failure in MemoryCache

Paolo Severini via Phabricator via lldb-commits lldb-commits at lists.llvm.org
Wed Feb 26 11:04:37 PST 2020


paolosev created this revision.
paolosev added reviewers: labath, vsk, JDevlieghere, clayborg.
paolosev added a project: LLDB.
Herald added subscribers: lldb-commits, sunfish, aheejin.

The original discussion for this issues is here <https://reviews.llvm.org/D72751#1892647>.

The lldb sanitized bot is flagging a container-overflow error after we introduced test `TestWasm.py`:
http://green.lab.llvm.org/green/view/LLDB/job/lldb-cmake-sanitized/992/testReport/junit/lldb-api/functionalities_gdb_remote_client/TestWasm_py/




11283==ERROR: AddressSanitizer: container-overflow on address 0x615000016184 at pc 0x00010b4608f0 bp 0x7ffee4f00130 sp 0x7ffee4eff8f8
-------------------------------------------------------------------------------------------------------------------------------------

READ of size 512 at 0x615000016184 thread T0

  #0 0x10b4608ef in __asan_memcpy+0x1af (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x418ef)
  #1 0x1116486d5 in lldb_private::MemoryCache::Read(unsigned long long, void*, unsigned long, lldb_private::Status&) Memory.cpp:189
  #2 0x11119d0e9 in lldb_private::Module::GetMemoryObjectFile(std::__1::shared_ptr<lldb_private::Process> const&, unsigned long long, lldb_private::Status&, unsigned long) Module.cpp:298
  #3 0x11169eeef in lldb_private::Process::ReadModuleFromMemory(lldb_private::FileSpec const&, unsigned long long, unsigned long) Process.cpp:2402
  #4 0x11113337b in lldb_private::DynamicLoader::LoadModuleAtAddress(lldb_private::FileSpec const&, unsigned long long, unsigned long long, bool) DynamicLoader.cpp:212
  #5 0x111ed53da in lldb_private::process_gdb_remote::ProcessGDBRemote::LoadModuleAtAddress(lldb_private::FileSpec const&, unsigned long long, unsigned long long, bool) ProcessGDBRemote.cpp:4767
  #6 0x111ed59b8 in lldb_private::process_gdb_remote::ProcessGDBRemote::LoadModules() ProcessGDBRemote.cpp:4801
  #7 0x1119c59aa in lldb_private::wasm::DynamicLoaderWasmDYLD::DidAttach() DynamicLoaderWasmDYLD.cpp:63
  #8 0x1116a3a97 in lldb_private::Process::CompleteAttach() Process.cpp:2930
  #9 0x1116a6bdf in lldb_private::Process::ConnectRemote(lldb_private::Stream*, llvm::StringRef) Process.cpp:3015
  #10 0x110a362ee in lldb::SBTarget::ConnectRemote(lldb::SBListener&, char const*, char const*, lldb::SBError&) SBTarget.cpp:559

There is actually a problem in the MemoryCache code. From ProcessGDBRemote::LoadModules we read the initial chunk (512 bytes) of an object file. In MemoryCache::Read, since this data is not cached yet, we call m_process.ReadMemoryFromInferior to actually read the memory (lines 174-241), look at the bottom of:

  size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len,
                           Status &error) {
      [...]
      while (bytes_left > 0) {
        [...]
        BlockMap::const_iterator pos = m_L2_cache.find(curr_addr);
        BlockMap::const_iterator end = m_L2_cache.end();
  
        if (pos != end) {
          size_t curr_read_size = cache_line_byte_size - cache_offset;
          if (curr_read_size > bytes_left)
            curr_read_size = bytes_left;
  
          memcpy(dst_buf + dst_len - bytes_left,
                 pos->second->GetBytes() + cache_offset, curr_read_size);
          [...]
        }
  
        // We need to read from the process
  
        if (bytes_left > 0) {
          assert((curr_addr % cache_line_byte_size) == 0);
          std::unique_ptr<DataBufferHeap> data_buffer_heap_up(
              new DataBufferHeap(cache_line_byte_size, 0));
          size_t process_bytes_read = m_process.ReadMemoryFromInferior(
              curr_addr, data_buffer_heap_up->GetBytes(),
              data_buffer_heap_up->GetByteSize(), error);
          if (process_bytes_read == 0)
            return dst_len - bytes_left;
  
          if (process_bytes_read != cache_line_byte_size)
            data_buffer_heap_up->SetByteSize(process_bytes_read);
          m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release());
          // We have read data and put it into the cache, continue through the
          // loop again to get the data out of the cache...
        }

First, we allocate a DataBufferHeap with the size of our cache_line_byte_size, 512 bytes, and we pass it to ReadMemoryFromInferior().
The problem is that in this test the whole object file is only 0x84 bytes, so we resize data_buffer_heap_up to a smaller size with data_buffer_heap_up->SetByteSize(process_bytes_read).
Then we iterate back up in the while loop, and try to read from this reallocated buffer. But we still try to read curr_read_size==512 bytes, so read past the buffer size. In fact the overflow is at address 0x615000016184 for a buffer that starts at 0x615000016100.

Note that the simple fix of just reading the available bytes doesn't work: Module::GetMemoryObjectFile expects to always be able to read at least 512 bytes, and it fails if the object file is smaller:

  ObjectFile *Module::GetMemoryObjectFile(const lldb::ProcessSP &process_sp,
                                          lldb::addr_t header_addr, Status &error,
                                          size_t size_to_read) {
        [...]
        const size_t bytes_read =
            process_sp->ReadMemory(header_addr, data_up->GetBytes(),
                                   data_up->GetByteSize(), readmem_error);
        if (bytes_read == size_to_read) {
            [...] // ok...
        } else {
          error.SetErrorStringWithFormat("unable to read header from memory: %s",
                                         readmem_error.AsCString());
        }
        [...]

To fix the issue, this patch avoids to resize the DataBufferHeap to a smaller size and pads it with a filler (0xdd) if necessary.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D75200

Files:
  lldb/source/Target/Memory.cpp


Index: lldb/source/Target/Memory.cpp
===================================================================
--- lldb/source/Target/Memory.cpp
+++ lldb/source/Target/Memory.cpp
@@ -232,8 +232,17 @@
         if (process_bytes_read == 0)
           return dst_len - bytes_left;
 
-        if (process_bytes_read != cache_line_byte_size)
-          data_buffer_heap_up->SetByteSize(process_bytes_read);
+        if (process_bytes_read != cache_line_byte_size) {
+          // If the data available to read is less than an entire page, we don't
+          // resize the buffer because we still want to return at least
+          // 'dst_len' bytes. In that case fill the rest of the buffer with a
+          // fail-fill value (0xdd).
+          if (process_bytes_read >= data_buffer_heap_up->GetByteSize())
+            data_buffer_heap_up->SetByteSize(process_bytes_read);
+          else
+            memset(data_buffer_heap_up->GetBytes() + process_bytes_read, 0xdd,
+                   data_buffer_heap_up->GetByteSize() - process_bytes_read);
+        }
         m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release());
         // We have read data and put it into the cache, continue through the
         // loop again to get the data out of the cache...


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D75200.246794.patch
Type: text/x-patch
Size: 1267 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20200226/8956cdc7/attachment.bin>


More information about the lldb-commits mailing list