Merge "Fix handling of possible bad gnu_debugdata_size." am: a6954573ae am: 9f36c47cf9 am: 7e9e15835c
Change-Id: Ide1503a2e16865823788dbaaeed513076bb63651
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 7676289..341275d 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -78,10 +78,31 @@
CrcGenerateTable();
Crc64GenerateTable();
- std::vector<uint8_t> src(gnu_debugdata_size_);
- if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
- gnu_debugdata_offset_ = 0;
- gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+ // Verify the request is not larger than the max size_t value.
+ if (gnu_debugdata_size_ > SIZE_MAX) {
+ return nullptr;
+ }
+ size_t initial_buffer_size;
+ if (__builtin_mul_overflow(5, gnu_debugdata_size_, &initial_buffer_size)) {
+ return nullptr;
+ }
+
+ size_t buffer_increment;
+ if (__builtin_mul_overflow(2, gnu_debugdata_size_, &buffer_increment)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<uint8_t[]> src(new (std::nothrow) uint8_t[gnu_debugdata_size_]);
+ if (src.get() == nullptr) {
+ return nullptr;
+ }
+
+ std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+ if (!dst->Resize(initial_buffer_size)) {
+ return nullptr;
+ }
+
+ if (!memory_->ReadFully(gnu_debugdata_offset_, src.get(), gnu_debugdata_size_)) {
return nullptr;
}
@@ -89,21 +110,23 @@
CXzUnpacker state;
alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
XzUnpacker_Construct(&state, &alloc);
- std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
int return_val;
size_t src_offset = 0;
size_t dst_offset = 0;
ECoderStatus status;
- dst->Resize(5 * gnu_debugdata_size_);
do {
- size_t src_remaining = src.size() - src_offset;
+ size_t src_remaining = gnu_debugdata_size_ - src_offset;
size_t dst_remaining = dst->Size() - dst_offset;
- if (dst_remaining < 2 * gnu_debugdata_size_) {
- dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
- dst_remaining += 2 * gnu_debugdata_size_;
+ if (dst_remaining < buffer_increment) {
+ size_t new_size;
+ if (__builtin_add_overflow(dst->Size(), buffer_increment, &new_size) ||
+ !dst->Resize(new_size)) {
+ XzUnpacker_Free(&state);
+ return nullptr;
+ }
+ dst_remaining += buffer_increment;
}
return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
&src_remaining, true, CODER_FINISH_ANY, &status);
@@ -112,13 +135,13 @@
} while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
XzUnpacker_Free(&state);
if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
- gnu_debugdata_offset_ = 0;
- gnu_debugdata_size_ = static_cast<uint64_t>(-1);
return nullptr;
}
// Shrink back down to the exact size.
- dst->Resize(dst_offset);
+ if (!dst->Resize(dst_offset)) {
+ return nullptr;
+ }
return dst.release();
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index a66cd5b..8de3d98 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -206,12 +206,12 @@
}
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
- if (addr >= raw_.size()) {
+ if (addr >= size_) {
return 0;
}
- size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
- const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
+ size_t bytes_left = size_ - static_cast<size_t>(addr);
+ const unsigned char* actual_base = static_cast<const unsigned char*>(raw_) + addr;
size_t actual_len = std::min(bytes_left, size);
memcpy(dst, actual_base, actual_len);
@@ -219,7 +219,7 @@
}
uint8_t* MemoryBuffer::GetPtr(size_t offset) {
- if (offset < raw_.size()) {
+ if (offset < size_) {
return &raw_[offset];
}
return nullptr;
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
index 3fe4bbb..a91e59f 100644
--- a/libunwindstack/MemoryBuffer.h
+++ b/libunwindstack/MemoryBuffer.h
@@ -29,18 +29,27 @@
class MemoryBuffer : public Memory {
public:
MemoryBuffer() = default;
- virtual ~MemoryBuffer() = default;
+ virtual ~MemoryBuffer() { free(raw_); }
size_t Read(uint64_t addr, void* dst, size_t size) override;
uint8_t* GetPtr(size_t offset);
- void Resize(size_t size) { raw_.resize(size); }
+ bool Resize(size_t size) {
+ raw_ = reinterpret_cast<uint8_t*>(realloc(raw_, size));
+ if (raw_ == nullptr) {
+ size_ = 0;
+ return false;
+ }
+ size_ = size;
+ return true;
+ }
- uint64_t Size() { return raw_.size(); }
+ uint64_t Size() { return size_; }
private:
- std::vector<uint8_t> raw_;
+ uint8_t* raw_ = nullptr;
+ size_t size_ = 0;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index c33908d..fc90dab 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -105,6 +105,9 @@
void FakeSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
void FakeSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
+ void FakeSetGnuDebugdataOffset(uint64_t offset) { gnu_debugdata_offset_ = offset; }
+ void FakeSetGnuDebugdataSize(uint64_t size) { gnu_debugdata_size_ = size; }
+
private:
std::unordered_map<std::string, uint64_t> globals_;
std::string fake_build_id_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index ea27e3e..3cf90fe 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1944,4 +1944,23 @@
CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
}
+TEST_F(ElfInterfaceTest, huge_gnu_debugdata_size) {
+ ElfInterfaceFake interface(nullptr);
+
+ interface.FakeSetGnuDebugdataOffset(0x1000);
+ interface.FakeSetGnuDebugdataSize(0xffffffffffffffffUL);
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+ interface.FakeSetGnuDebugdataSize(0x4000000000000UL);
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+ // This should exceed the size_t value of the first allocation.
+#if defined(__LP64__)
+ interface.FakeSetGnuDebugdataSize(0x3333333333333334ULL);
+#else
+ interface.FakeSetGnuDebugdataSize(0x33333334);
+#endif
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+}
+
} // namespace unwindstack