Merge "Fix handling of possible bad gnu_debugdata_size."
This commit is contained in:
commit
a6954573ae
5 changed files with 76 additions and 22 deletions
|
|
@ -78,10 +78,31 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
|
|||
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 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
|
|||
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 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
|
|||
} 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,12 +206,12 @@ std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_
|
|||
}
|
||||
|
||||
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 @@ size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
|
|||
}
|
||||
|
||||
uint8_t* MemoryBuffer::GetPtr(size_t offset) {
|
||||
if (offset < raw_.size()) {
|
||||
if (offset < size_) {
|
||||
return &raw_[offset];
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -29,18 +29,27 @@ namespace unwindstack {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ class ElfInterfaceFake : public ElfInterface {
|
|||
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_;
|
||||
|
|
|
|||
|
|
@ -1944,4 +1944,23 @@ TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_64) {
|
|||
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue