serge1 / ELFIO

ELFIO - ELF (Executable and Linkable Format) reader and producer implemented as a header only C++ library
http://serge1.github.io/ELFIO
MIT License
706 stars 152 forks source link

Alloc-dealloc-mismatch detected when build with ASAN #107

Closed bladchan closed 1 year ago

bladchan commented 1 year ago

Hi,

I am fuzzing this project with ASAN and find alloc-dealloc-mismatch issue when intializing elfio structure data.

Environment: Ubuntu 20.04 + g++ 9.4.0

The target: (attached: file named as "test_mismatch.c")

#include <iostream>
#include <elfio/elfio.hpp>

using namespace ELFIO;

int main(int argc, char** argv) {

    if(argc != 2) return 0;
    elfio reader;   // <----- Alloc-dealloc-mismatch detected here

    if(!reader.load(argv[1])){
        std::cout << "Can't find or process ELF file " << argv[1] << std::endl;
        return 0;
    }

    std::cout << "ELF file class : ";
    if ( reader.get_class() == ELFCLASS32 )
        std::cout << "ELF32" << std::endl;
    else
        std::cout << "ELF64" << std::endl;  

    std::cout << "ELF file encoding : ";
    if ( reader.get_encoding() == ELFDATA2LSB )
        std::cout << "Little endian" << std::endl;
    else
        std::cout << "Big endian" << std::endl;

    // Print ELF file sections info

    Elf_Half sec_num = reader.sections.size(); 

    std::cout << "Number of sections: " << sec_num << std::endl;

    for ( int i = 0; i < sec_num; ++i ) {

        const section* psec = reader.sections[i]; 

        std::cout << " [" << i << "] "
                  << psec->get_name()
                  << "\t"
                  << psec->get_size()
                  << std::endl;
        // Access section's data

        const char* p = reader.sections[i]->get_data();
    }

    // Print ELF file segments info

    Elf_Half seg_num = reader.segments.size();
    std::cout << "Number of segments: " << seg_num << std::endl;
    for ( int i = 0; i < seg_num; ++i ) {
        const segment* pseg = reader.segments[i];
        std::cout << " [" << i << "] 0x" << std::hex
                 << pseg->get_flags()
                 << "\t0x"
                 << pseg->get_virtual_address()
                 << "\t0x"
                 << pseg->get_file_size()
                 << "\t0x"
                 << pseg->get_memory_size()
                 << std::endl;
        // Access segments's data
        const char* p = reader.segments[i]->get_data();
    }

    return 0;
}

You should build it below:

g++ -fsanitize=address -g -o test_mismatch test_mismatch.c

Run it:

./test_mismatch some_elf_path # ./test_mismatch hello_32

Poc: Poc1.zip

ASAN says:

=================================================================
==2783165==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x602000000050
    #0 0x7fd7d0728c65 in operator delete(void*, unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:177
    #1 0x561c99888de7 in std::default_delete<char>::operator()(char*) const /usr/include/c++/9/bits/unique_ptr.h:81
    #2 0x561c99898706 in std::unique_ptr<char, std::default_delete<char> >::reset(char*) /usr/include/c++/9/bits/unique_ptr.h:402
    #3 0x561c99898841 in std::unique_ptr<char, std::default_delete<char> >::operator=(std::unique_ptr<char, std::default_delete<char> >&&) /usr/include/c++/9/bits/unique_ptr.h:307
    #4 0x561c99891584 in ELFIO::section_impl<ELFIO::Elf32_Shdr>::append_data(char const*, unsigned int) /usr/local/include/elfio/elfio_section.hpp:160
    #5 0x561c998888af in ELFIO::string_section_accessor_template<ELFIO::section>::add_string(char const*) /usr/local/include/elfio/elfio_strings.hpp:73
    #6 0x561c9988564b in ELFIO::string_section_accessor_template<ELFIO::section>::add_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /usr/local/include/elfio/elfio_strings.hpp:83
    #7 0x561c99881958 in ELFIO::elfio::Sections::add(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /usr/local/include/elfio/elfio.hpp:961
    #8 0x561c9987fd3a in ELFIO::elfio::create_mandatory_sections() /usr/local/include/elfio/elfio.hpp:460
    #9 0x561c9987e6e9 in ELFIO::elfio::create(unsigned char, unsigned char) /usr/local/include/elfio/elfio.hpp:118
    #10 0x561c9987e577 in ELFIO::elfio::elfio() /usr/local/include/elfio/elfio.hpp:68
    #11 0x561c9987cb2f in main /home/ubuntu/test/ELFIO/fuzz/test_mismatch.c:9
    #12 0x7fd7d00fe082 in __libc_start_main ../csu/libc-start.c:308
    #13 0x561c9987c98d in _start (/home/ubuntu/test/ELFIO/fuzz/test_mismatch+0x2098d)

0x602000000050 is located 0 bytes inside of 2-byte region [0x602000000050,0x602000000052)
allocated by thread T0 here:
    #0 0x7fd7d0727b4f in operator new[](unsigned long, std::nothrow_t const&) ../../../../src/libsanitizer/asan/asan_new_delete.cc:113
    #1 0x561c998913eb in ELFIO::section_impl<ELFIO::Elf32_Shdr>::append_data(char const*, unsigned int) /usr/local/include/elfio/elfio_section.hpp:153
    #2 0x561c998887f1 in ELFIO::string_section_accessor_template<ELFIO::section>::add_string(char const*) /usr/local/include/elfio/elfio_strings.hpp:70
    #3 0x561c9988564b in ELFIO::string_section_accessor_template<ELFIO::section>::add_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /usr/local/include/elfio/elfio_strings.hpp:83
    #4 0x561c99881958 in ELFIO::elfio::Sections::add(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /usr/local/include/elfio/elfio.hpp:961
    #5 0x561c9987fd3a in ELFIO::elfio::create_mandatory_sections() /usr/local/include/elfio/elfio.hpp:460
    #6 0x561c9987e6e9 in ELFIO::elfio::create(unsigned char, unsigned char) /usr/local/include/elfio/elfio.hpp:118
    #7 0x561c9987e577 in ELFIO::elfio::elfio() /usr/local/include/elfio/elfio.hpp:68
    #8 0x561c9987cb2f in main /home/ubuntu/test/ELFIO/fuzz/test_mismatch.c:9
    #9 0x7fd7d00fe082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ../../../../src/libsanitizer/asan/asan_new_delete.cc:177 in operator delete(void*, unsigned long)
==2783165==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==2783165==ABORTING

What ASAN shows indicate that the problem occurs Line 153 and Line 160 elfio/elfio_section.hpp.

    void append_data( const char* raw_data, Elf_Word size ) override
    {
        if ( get_type() != SHT_NOBITS ) {
            if ( get_size() + size < data_size ) {
                std::copy( raw_data, raw_data + size, data.get() + get_size() );
            }
            else {
                data_size = 2 * ( data_size + size );
                std::unique_ptr<char> new_data(
                    new ( std::nothrow ) char[data_size] );   //  <----- Line 153

                if ( nullptr != new_data ) {
                    std::copy( data.get(), data.get() + get_size(),
                               new_data.get() );
                    std::copy( raw_data, raw_data + size,
                               new_data.get() + get_size() );
                    data = std::move( new_data );   //   <----- Line 160
                }
                else {
                    size = 0;
                }
            }
            set_size( get_size() + size );
            if ( translator->empty() ) {
                set_stream_size( get_stream_size() + size );
            }
        }
    }

🤔 new_data is a std::unique_ptr type but store char*(char[]), when calling std::move, std::move will delete new_data instead of delete[] new_data, which cause alloc-dealloc-mismatch issue.

The same problem happens in /elfio/elfio_segment.hpp when I fix the first issue.

😄 I will pull a request to fix these problems later.

serge1 commented 1 year ago

Fixed in #108