Skip to content
Permalink
Browse files
Fix memory leaks in the MachO Python API and create FatBinary
API Changes:

LIEF::MachO::Parser won't return a 'std::vector' of MachO::Binary*
but a pointer to MachO::FatBinary object
It's a kind of wrapper on std::vector<MachO::Binary*>
  • Loading branch information
romainthomas committed Sep 29, 2017
1 parent 554fa15 commit 3602643
Show file tree
Hide file tree
Showing 32 changed files with 431 additions and 171 deletions.
@@ -242,7 +242,8 @@ set(LIBFUZZER_SRC_FILES)
if (LIEF_FUZZING)
message(STATUS "Fuzzing Enabled")

set(LIBFUZZER_URL "http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer")
set(LIBFUZZER_VERSION 314494)
set(LIBFUZZER_URL "http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer/") #\?p=${LIBFUZZER_VERSION}")
ExternalProject_Add(lief_libfuzzer
SVN_REPOSITORY ${LIBFUZZER_URL}
CONFIGURE_COMMAND ""
@@ -23,18 +23,18 @@
using namespace LIEF::MachO;

Macho_Binary_t** macho_parse(const char *file) {
std::vector<Binary*> macho_binaries{Parser::parse(file)};
FatBinary* macho_binaries = Parser::parse(file);

Macho_Binary_t** c_macho_binaries = static_cast<Macho_Binary_t**>(
malloc((macho_binaries.size() + 1) * sizeof(Macho_Binary_t**)));
malloc((macho_binaries->size() + 1) * sizeof(Macho_Binary_t**)));

for (size_t i = 0; i < macho_binaries.size(); ++i) {
Binary* binary = macho_binaries [i];
for (size_t i = 0; i < macho_binaries->size(); ++i) {
Binary& binary = (*macho_binaries)[i];
c_macho_binaries[i] = static_cast<Macho_Binary_t*>(malloc(sizeof(Macho_Binary_t)));
init_c_binary(c_macho_binaries[i], binary);
init_c_binary(c_macho_binaries[i], &binary);
}

c_macho_binaries[macho_binaries.size()] = nullptr;
c_macho_binaries[macho_binaries->size()] = nullptr;

return c_macho_binaries;
}
@@ -2,6 +2,7 @@ set(LIEF_PYTHON_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/pyMachO.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyDylibCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyBinary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyFatBinary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyLoadCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pySegmentCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyHeader.cpp"
@@ -0,0 +1,60 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>

#include "LIEF/MachO/FatBinary.hpp"

#include "pyMachO.hpp"


void init_MachO_FatBinary_class(py::module& m) {


py::class_<FatBinary>(m, "FatBinary")

.def_property_readonly("size",
&FatBinary::size,
"Number of " RST_CLASS_REF(lief.MachO.Binary) " registred")

.def("at",
static_cast<Binary& (FatBinary::*)(size_t)>(&FatBinary::at),
"Return the " RST_CLASS_REF(lief.MachO.Binary) " at the given index",
"index"_a)

.def("__len__",
&FatBinary::size)


.def("__getitem__",
static_cast<Binary& (FatBinary::*)(size_t)>(&FatBinary::operator[]),
"",
py::return_value_policy::reference)

.def("__iter__",
static_cast<it_binaries (FatBinary::*)(void)>(&FatBinary::begin),
py::return_value_policy::reference_internal)

.def("__str__",
[] (const FatBinary& fat_binary)
{
std::ostringstream stream;
stream << fat_binary;
std::string str = stream.str();
return str;
});

}

@@ -24,14 +24,14 @@ void init_MachO_Parser_class(py::module& m) {

// Parser (Parser)
m.def("parse",
static_cast<std::vector<Binary*> (*) (const std::string&)>(&LIEF::MachO::Parser::parse),
static_cast<FatBinary* (*) (const std::string&)>(&LIEF::MachO::Parser::parse),
"Parse the given binary and return a **list** of " RST_CLASS_REF(lief.MachO.Binary) " objects",
"filename"_a,
py::return_value_policy::take_ownership);


m.def("parse",
static_cast<std::vector<Binary*> (*) (const std::vector<uint8_t>&, const std::string&)>(&LIEF::MachO::Parser::parse),
static_cast<FatBinary* (*) (const std::vector<uint8_t>&, const std::string&)>(&LIEF::MachO::Parser::parse),
"Parse the given binary (from raw) and return a **list** of " RST_CLASS_REF(lief.MachO.Binary) " objects",
py::arg("raw"), py::arg("name") = "",
py::return_value_policy::take_ownership);
@@ -23,10 +23,9 @@
void init_MachO_module(py::module& m) {
py::module LIEF_MachO_module = m.def_submodule("MachO", "Python API for MachO");

py::bind_vector<std::vector<Binary*>>(m, "macho_list");

// Objects
init_MachO_Parser_class(LIEF_MachO_module);
init_MachO_FatBinary_class(LIEF_MachO_module);
init_MachO_Binary_class(LIEF_MachO_module);
init_MachO_Header_class(LIEF_MachO_module);
init_MachO_LoadCommand_class(LIEF_MachO_module);
@@ -27,6 +27,7 @@ using namespace LIEF::MachO;
PYBIND11_MAKE_OPAQUE(std::vector<Binary*>)

void init_MachO_Parser_class(py::module&);
void init_MachO_FatBinary_class(py::module&);
void init_MachO_Binary_class(py::module&);
void init_MachO_Header_class(py::module&);
void init_MachO_LoadCommand_class(py::module&);
@@ -63,6 +63,7 @@ void init_LIEF_iterators(py::module& m) {
// MachO
// =====
#if defined(LIEF_MACHO_MODULE)
init_ref_iterator<LIEF::MachO::it_binaries>(m);
init_ref_iterator<LIEF::MachO::it_relocations>(m);
init_ref_iterator<LIEF::MachO::it_commands>(m);
init_ref_iterator<LIEF::MachO::it_symbols>(m);
@@ -13,6 +13,14 @@ Parsers

----------

FatBinary
*********

.. doxygenclass:: LIEF::MachO::FatBinary
:project: lief

----------

Binary
******

@@ -10,6 +10,17 @@ Parser

----------

FatBinary
*********

.. autoclass:: lief.MachO.FatBinary
:members:
:inherited-members:
:undoc-members:

----------


.. _python-macho-binary-api-ref:

Binary
@@ -38,13 +38,9 @@ int main(int argc, char **argv) {
return -1;
}

std::vector<MachO::Binary*> binaries = MachO::Parser::parse(argv[1]);
MachO::Binary* binary = binaries.back();
binary->write(argv[2]);

for (MachO::Binary *b : binaries) {
delete b;
}
std::unique_ptr<LIEF::MachO::FatBinary> binaries{MachO::Parser::parse(argv[1])};
MachO::Binary& binary = binaries->back();
binary.write(argv[2]);

return 0;
}
@@ -30,10 +30,10 @@ int main(int argc, char **argv) {
return -1;
}

std::vector<MachO::Binary*> binaries = MachO::Parser::parse(argv[1]);
MachO::Binary* binary = binaries.back();
auto segments = binary->segments();
auto itSegment = std::find_if(
std::unique_ptr<LIEF::MachO::FatBinary> binaries{MachO::Parser::parse(argv[1])};
MachO::Binary& binary = binaries->back();
auto&& segments = binary.segments();
auto&& itSegment = std::find_if(
std::begin(segments),
std::end(segments),
[] (const MachO::SegmentCommand& segment) {
@@ -64,7 +64,7 @@ int main(int argc, char **argv) {
segment_header.segname[segment_name.size()] = 0;
segment_header.vmaddr = 0x200050000;
segment_header.vmsize = 0x2000;
segment_header.fileoff = binary->original_size();
segment_header.fileoff = binary.original_size();
segment_header.filesize = payload.size();
segment_header.maxprot = 7;
segment_header.initprot = 3;
@@ -82,11 +82,8 @@ int main(int argc, char **argv) {
reinterpret_cast<uint8_t*>(&segment_header) + sizeof(segment_header)
});
//binary->insert_command(std::move(segment));
binary->write(argv[2]);
binary.write(argv[2]);

for (MachO::Binary *b : binaries) {
delete b;
}

return 0;
}
@@ -20,23 +20,23 @@

using namespace LIEF::MachO;

void print_binary(const Binary* binary) {
std::cout << binary->header() << std::endl;
void print_binary(const Binary& binary) {
std::cout << binary.header() << std::endl;

std::cout << "== Library ==" << std::endl;
for (const DylibCommand& library : binary->libraries()) {
for (const DylibCommand& library : binary.libraries()) {
std::cout << library << std::endl;
}
std::cout << std::endl;

std::cout << "== Sections ==" << std::endl;
for (const Section& section : binary->sections()) {
for (const Section& section : binary.sections()) {
std::cout << section << std::endl;
}

//std::cout << "== Segments ==" << std::endl;

//for (SegmentCommand& segment : binary->segments()) {
//for (SegmentCommand& segment : binary.segments()) {
// std::cout << segment << std::endl;
// if (segment.sections().size() > 0) {
// //std::cout << std::hex;
@@ -61,31 +61,31 @@ void print_binary(const Binary* binary) {
//}
//std::cout << std::endl;

//auto commands = binary->commands();
for (const LoadCommand& cmd : binary->commands()) {
//auto commands = binary.commands();
for (const LoadCommand& cmd : binary.commands()) {
std::cout << cmd << std::endl;
std::cout << "======================" << std::endl;
}

std::cout << "== Symbols ==" << std::endl;
for (const Symbol& symbol : binary->symbols()) {
for (const Symbol& symbol : binary.symbols()) {
std::cout << symbol << std::endl;
}


std::cout << "== Exported symbols ==" << std::endl;
for (const Symbol& symbol : binary->exported_symbols()) {
for (const Symbol& symbol : binary.exported_symbols()) {
std::cout << symbol << std::endl;
}

std::cout << "== Imported symbols ==" << std::endl;
for (const Symbol& symbol : binary->imported_symbols()) {
for (const Symbol& symbol : binary.imported_symbols()) {
std::cout << symbol << std::endl;
}


std::cout << "== Relocations ==" << std::endl;
for (const Relocation& relocation : binary->relocations()) {
for (const Relocation& relocation : binary.relocations()) {
std::cout << relocation << std::endl;
}

@@ -99,16 +99,12 @@ int main(int argc, char **argv) {
std::cerr << "Usage: " << argv[0] << " <MachO binary>" << std::endl;
return -1;
}
std::vector<Binary*> binaries = Parser::parse(argv[1]);
for (const Binary* binary : binaries) {
std::unique_ptr<FatBinary> binaries{Parser::parse(argv[1])};
for (const Binary& binary : *binaries) {
print_binary(binary);
std::cout << std::endl;
}

for (Binary* binary : binaries) {
delete binary;
}

return 0;
}

@@ -1,3 +1,4 @@
include(ProcessorCount)
set(LIEF_FUZZER_SRC
elf_fuzzer.cpp
pe_fuzzer.cpp
@@ -33,15 +34,18 @@ ExternalProject_Add(lief_fuzzer_corpus
INSTALL_COMMAND ""
GIT_REPOSITORY ${SAMPLES_GIT_URL}
GIT_TAG ${SAMPLES_TAG}
UPDATE_COMMAND ${GIT_EXECUTABLE} pull
#UPDATE_COMMAND ${GIT_EXECUTABLE} pull
UPDATE_COMMAND ""
)

ExternalProject_Get_Property(lief_fuzzer_corpus source_dir)
set(LIEF_CORUPUS_DIRECTORY "${source_dir}" CACHE INTERNAL "Path to LIEF samples")
message(STATUS "Samples directory: ${LIEF_CORUPUS_DIRECTORY}")



ProcessorCount(N)
set(LIB_FUZZER_ARGS -detect_leaks=1 -rss_limit_mb=0 -print_final_stats=1 -jobs=${N})
set(ASAN_OPTIONS allocator_may_return_null=1)
# ELF
# ===
set(ELF_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/elf-output)
@@ -50,7 +54,7 @@ add_custom_target(build-elf-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${ELF_WORKING_DIR})

add_custom_target("fuzz-elf"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/elf_fuzzer ${LIEF_CORUPUS_DIRECTORY}/ELF -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=${ASAN_OPTIONS} ${CMAKE_CURRENT_BINARY_DIR}/elf_fuzzer ${LIEF_CORUPUS_DIRECTORY}/ELF ${LIB_FUZZER_ARGS}
DEPENDS elf_fuzzer LIB_LIEF_STATIC build-elf-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${ELF_WORKING_DIR}
COMMENT "Run ELF fuzzer")
@@ -64,7 +68,7 @@ add_custom_target(build-pe-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${PE_WORKING_DIR})

add_custom_target("fuzz-pe"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/pe_fuzzer ${LIEF_CORUPUS_DIRECTORY}/PE -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS="${ASAN_OPTIONS}" ${CMAKE_CURRENT_BINARY_DIR}/pe_fuzzer ${LIEF_CORUPUS_DIRECTORY}/PE ${LIB_FUZZER_ARGS}
DEPENDS pe_fuzzer LIB_LIEF_STATIC build-pe-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${PE_WORKING_DIR}
COMMENT "Run PE fuzzer")
@@ -74,10 +78,10 @@ add_custom_target("fuzz-pe"
set(MACHO_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/macho-output)

add_custom_target(build-macho-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${MACHO_WORKING_DIR})
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=${ASAN_OPTIONS} ${CMAKE_COMMAND} -E make_directory ${MACHO_WORKING_DIR})

add_custom_target("fuzz-macho"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/macho_fuzzer ${LIEF_CORUPUS_DIRECTORY}/MachO -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/macho_fuzzer ${LIEF_CORUPUS_DIRECTORY}/MachO ${LIB_FUZZER_ARGS}
DEPENDS macho_fuzzer LIB_LIEF_STATIC build-macho-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${MACHO_WORKING_DIR}
COMMENT "Run MachO fuzzer")
@@ -4,14 +4,11 @@

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::vector<uint8_t> raw = {data, data + size};
std::vector<LIEF::MachO::Binary*> binaries;
std::unique_ptr<LIEF::MachO::FatBinary> binaries;
try {
binaries = LIEF::MachO::Parser::parse(raw);
binaries = std::unique_ptr<LIEF::MachO::FatBinary>(LIEF::MachO::Parser::parse(raw));
} catch (const LIEF::exception& e) {
std::cout << e.what() << std::endl;
}
for (LIEF::MachO::Binary* b: binaries) {
delete b;
}
return 0;
}

0 comments on commit 3602643

Please sign in to comment.