Skip to content

Commit

Permalink
Merge pull request #45 from fuzzuf/feature/environment_variables0
Browse files Browse the repository at this point in the history
Make native linux executor to accept extra environment variables
  • Loading branch information
Fadis committed Mar 10, 2022
2 parents 03e2096 + 9090c12 commit 23b3e52
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 5 deletions.
40 changes: 37 additions & 3 deletions executor/native_linux_executor.cpp
Expand Up @@ -67,7 +67,8 @@ NativeLinuxExecutor::NativeLinuxExecutor(
const fs::path &path_to_write_input,
u32 afl_shm_size,
u32 bb_shm_size,
bool record_stdout_and_err
bool record_stdout_and_err,
std::vector< std::string > &&environment_variables_
) :
Executor( argv, exec_timelimit_ms, exec_memlimit, path_to_write_input.string() ),
forksrv( forksrv ),
Expand Down Expand Up @@ -98,6 +99,7 @@ NativeLinuxExecutor::NativeLinuxExecutor(
// It is sufficient if each NativeLinuxExecutor::Run() can refer the memory
SetupSharedMemories();
SetupEnvironmentVariablesForTarget();
CreateJoinedEnvironmentVariables( std::move( environment_variables_ ) );

if (forksrv) {
SetupForkServer();
Expand Down Expand Up @@ -487,7 +489,11 @@ void NativeLinuxExecutor::Run(const u8 *buf, u32 len, u32 timeout_ms) {
}
// Execute new executable binary on the child process.
// If failed, that matter is recorded to child_state.
child_state->exec_result = execv(cargv[0], (char**)cargv.data());
child_state->exec_result = execve(
cargv[0],
(char**)cargv.data(),
const_cast< char** >( raw_environment_variables.data() )
);
child_state->exec_errno = errno;

/* Use a distinctive bitmap value to tell the parent about execv()
Expand Down Expand Up @@ -813,7 +819,31 @@ void NativeLinuxExecutor::SetupEnvironmentVariablesForTarget() {
"handle_sigfpe=0:"
"handle_sigill=0",
0);
}


void NativeLinuxExecutor::CreateJoinedEnvironmentVariables(
std::vector< std::string > &&extra
) {
environment_variables.clear();
raw_environment_variables.clear();
for( auto e = environ; *e; ++e )
environment_variables.push_back( *e );
std::move(
extra.begin(),
extra.end(),
std::back_inserter( environment_variables )
);
environment_variables.shrink_to_fit();
raw_environment_variables.reserve( environment_variables.size() );
std::transform(
environment_variables.begin(),
environment_variables.end(),
std::back_inserter( raw_environment_variables ),
[]( const auto &e ) { return e.c_str(); }
);
raw_environment_variables.push_back( nullptr );
raw_environment_variables.shrink_to_fit();
}

/*
Expand Down Expand Up @@ -904,7 +934,11 @@ void NativeLinuxExecutor::SetupForkServer() {
close(chld2par[0]);
close(chld2par[1]);

execve(cargv[0], (char**)cargv.data(), environ);
execve(
cargv[0],
(char**)cargv.data(),
const_cast< char** >( raw_environment_variables.data() )
);
// TODO: It must be discussed whether it is needed that equivalent to EXEC_FAIL_SIG that is used in non-fork server mode.
exit(0);
}
Expand Down
28 changes: 26 additions & 2 deletions include/fuzzuf/executor/native_linux_executor.hpp
Expand Up @@ -84,7 +84,8 @@ class NativeLinuxExecutor : public Executor {
// In the future, we should generalize this flag so that we can arbitrarily specify
// which fd should be recorded. For example, by passing std::vector<int>{1, 2} to this class,
// we would tell that we would like to record stdout and stderr.
bool record_stdout_and_err = false
bool record_stdout_and_err = false,
std::vector< std::string > &&environment_variables_ = {}
);
~NativeLinuxExecutor();

Expand Down Expand Up @@ -128,7 +129,13 @@ class NativeLinuxExecutor : public Executor {
fuzzuf::executor::output_t MoveStdOut();
// InplaceMemoryFeedback made of GetStdErr before calling this function becomes invalid after Run()
fuzzuf::executor::output_t MoveStdErr();
private:
private:
/**
* Take snapshot of environment variables.
* This updates both environment_variables and raw_environment_variables.
* @param extra Executor specific environment variables those are set only on the child process of this executor.
*/
void CreateJoinedEnvironmentVariables( std::vector< std::string > &&extra );
PUTExitReasonType last_exit_reason;
u8 last_signal;
fuzzuf::executor::output_t stdout_buffer;
Expand All @@ -141,4 +148,21 @@ class NativeLinuxExecutor : public Executor {
epoll_event fork_server_read_event;

bool record_stdout_and_err;

/**
* Snapshot of environment variables.
* This contains following values.
* * All global environment variables available whien the constructor is executed.
* * Executor specific environment variables specified on the constructor argument.
* The child process invoked by this executor will take these values as environment variables.
* (This means the executor created prior to modification of environment variables will take old environment variables)
*/
std::vector< std::string > environment_variables;

/**
* Since some C APIs require environment variables in null terminated array of C-string, environment_variables is transformed into that form.
* Each element of raw_environment_variables points value of environment_variables except last value that points nullptr.
* raw_environment_variables should be rebuilt if environment_variables is modified.
*/
std::vector< const char* > raw_environment_variables;
};
2 changes: 2 additions & 0 deletions test/executor/CMakeLists.txt
Expand Up @@ -43,6 +43,8 @@ add_executable( segmentation_fault segmentation_fault.cpp )
set_target_properties( segmentation_fault PROPERTIES COMPILE_FLAGS "" )
add_executable( illegal_instruction illegal_instruction.cpp )
set_target_properties( illegal_instruction PROPERTIES COMPILE_FLAGS "" )
add_executable( print_env print_env.cpp )
set_target_properties( print_env PROPERTIES COMPILE_FLAGS "" )

subdirs(
non_fork_server_mode
Expand Down
32 changes: 32 additions & 0 deletions test/executor/fork_server_mode/CMakeLists.txt
Expand Up @@ -32,3 +32,35 @@ endif()
add_test( NAME "native_linux_executor.native_linux_context.non-fork.run"
COMMAND test-executor-run-non-fork )

add_executable( test-executor-fork_server_mode-environment_variables environment_variables.cpp )
target_link_libraries(
test-executor-fork_server_mode-environment_variables
test-common
fuzzuf
${FUZZUF_LIBRARIES}
Boost::unit_test_framework
)
target_include_directories(
test-executor-fork_server_mode-environment_variables
PRIVATE
${FUZZUF_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/test/common
)
set_target_properties(
test-executor-fork_server_mode-environment_variables
PROPERTIES COMPILE_FLAGS "${ADDITIONAL_COMPILE_FLAGS_STR}"
)
set_target_properties(
test-executor-fork_server_mode-environment_variables
PROPERTIES LINK_FLAGS "${ADDITIONAL_COMPILE_FLAGS_STR}"
)
if( ENABLE_CLANG_TIDY )
set_target_properties(
test-executor-fork_server_mode-environment_variables
PROPERTIES
CXX_CLANG_TIDY "${CLANG_TIDY};${CLANG_TIDY_CONFIG_FOR_TEST}"
)
endif()
add_test( NAME "executor.fork_server_mode.environment_variables"
COMMAND test-executor-fork_server_mode-environment_variables )

90 changes: 90 additions & 0 deletions test/executor/fork_server_mode/environment_variables.cpp
@@ -0,0 +1,90 @@
/*
* fuzzuf
* Copyright (C) 2022 Ricerca Security
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
#define BOOST_TEST_MODULE \
native_linux_executor.fork_server_mode.environment_variables
#define BOOST_TEST_DYN_LINK

#include <boost/scope_exit.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>

#include "fuzzuf/executor/native_linux_executor.hpp"
#include "fuzzuf/feedback/put_exit_reason_type.hpp"
#include "fuzzuf/utils/filesystem.hpp"

BOOST_AUTO_TEST_CASE(NativeLinuxExecutorWithEnvironmentVariables) {
// Setup root directory
std::string root_dir_template("/tmp/fuzzuf_test.XXXXXX");
auto *const raw_dirname = mkdtemp(root_dir_template.data());
BOOST_CHECK(raw_dirname != nullptr);
auto root_dir = fs::path(raw_dirname);
BOOST_SCOPE_EXIT(&root_dir) { fs::remove_all(root_dir); }
BOOST_SCOPE_EXIT_END
auto input_dir = root_dir / "input";
auto output_dir = root_dir / "output";
BOOST_CHECK_EQUAL(fs::create_directory(input_dir), true);
BOOST_CHECK_EQUAL(fs::create_directory(output_dir), true);
auto path_to_write_seed = output_dir / "cur_input";

// Create executor
NativeLinuxExecutor executor({TEST_BINARY_DIR "/put_binaries/command_wrapper",
TEST_BINARY_DIR "/executor/print_env", "@@"},
1000, 10000, true, path_to_write_seed, 0, 0,
true, {"FOO=World"});

// Run executor
executor.Run(nullptr, 0);

// The execution should success due to the required environment variable is
// set
BOOST_CHECK_EQUAL(executor.GetExitStatusFeedback().exit_reason,
PUTExitReasonType::FAULT_NONE);

// Check if appropriate value is set on the environment variable
const auto standard_output = executor.MoveStdOut();
const auto expected_output = std::string("World\n");
BOOST_CHECK_EQUAL_COLLECTIONS(standard_output.begin(), standard_output.end(),
expected_output.begin(), expected_output.end());
}

BOOST_AUTO_TEST_CASE(NativeLinuxExecutorWithoutEnvironmentVariables) {
// Setup root directory
std::string root_dir_template("/tmp/fuzzuf_test.XXXXXX");
auto *const raw_dirname = mkdtemp(root_dir_template.data());
BOOST_CHECK(raw_dirname != nullptr);
auto root_dir = fs::path(raw_dirname);
BOOST_SCOPE_EXIT(&root_dir) { fs::remove_all(root_dir); }
BOOST_SCOPE_EXIT_END
auto output_dir = root_dir / "output";
BOOST_CHECK_EQUAL(fs::create_directory(output_dir), true);
auto path_to_write_seed = output_dir / "cur_input";

// Create executor
NativeLinuxExecutor executor({TEST_BINARY_DIR "/put_binaries/command_wrapper",
TEST_BINARY_DIR "/executor/print_env", "@@"},
1000, 10000, true, path_to_write_seed, 0, 0,
true);

// Run executor
executor.Run(nullptr, 0);

// The execution should fail due to the required environment variable is not
// set
BOOST_CHECK_EQUAL(executor.GetExitStatusFeedback().exit_reason,
PUTExitReasonType::FAULT_CRASH);
}
32 changes: 32 additions & 0 deletions test/executor/non_fork_server_mode/CMakeLists.txt
Expand Up @@ -29,3 +29,35 @@ if( ENABLE_CLANG_TIDY )
endif()
add_test( NAME "native_linux_executor.native_linux_context.non_fork.run"
COMMAND test-executor-run-non-fork )

add_executable( test-executor-non_fork_server_mode-environment_variables environment_variables.cpp )
target_link_libraries(
test-executor-non_fork_server_mode-environment_variables
test-common
fuzzuf
${FUZZUF_LIBRARIES}
Boost::unit_test_framework
)
target_include_directories(
test-executor-non_fork_server_mode-environment_variables
PRIVATE
${FUZZUF_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/test/common
)
set_target_properties(
test-executor-non_fork_server_mode-environment_variables
PROPERTIES COMPILE_FLAGS "${ADDITIONAL_COMPILE_FLAGS_STR}"
)
set_target_properties(
test-executor-non_fork_server_mode-environment_variables
PROPERTIES LINK_FLAGS "${ADDITIONAL_COMPILE_FLAGS_STR}"
)
if( ENABLE_CLANG_TIDY )
set_target_properties(
test-executor-non_fork_server_mode-environment_variables
PROPERTIES
CXX_CLANG_TIDY "${CLANG_TIDY};${CLANG_TIDY_CONFIG_FOR_TEST}"
)
endif()
add_test( NAME "executor.non_fork_server_mode.environment_variables"
COMMAND test-executor-non_fork_server_mode-environment_variables )
85 changes: 85 additions & 0 deletions test/executor/non_fork_server_mode/environment_variables.cpp
@@ -0,0 +1,85 @@
/*
* fuzzuf
* Copyright (C) 2022 Ricerca Security
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
#define BOOST_TEST_MODULE \
native_linux_executor.non_fork_server_mode.environment_variables
#define BOOST_TEST_DYN_LINK

#include <boost/scope_exit.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>

#include "fuzzuf/executor/native_linux_executor.hpp"
#include "fuzzuf/feedback/put_exit_reason_type.hpp"
#include "fuzzuf/utils/filesystem.hpp"

BOOST_AUTO_TEST_CASE(NativeLinuxExecutorWithEnvironmentVariables) {
// Setup root directory
std::string root_dir_template("/tmp/fuzzuf_test.XXXXXX");
auto *const raw_dirname = mkdtemp(root_dir_template.data());
BOOST_CHECK(raw_dirname != nullptr);
auto root_dir = fs::path(raw_dirname);
BOOST_SCOPE_EXIT(&root_dir) { fs::remove_all(root_dir); }
BOOST_SCOPE_EXIT_END
auto output_dir = root_dir / "output";
BOOST_CHECK_EQUAL(fs::create_directory(output_dir), true);
auto path_to_write_seed = output_dir / "cur_input";

// Create executor
NativeLinuxExecutor executor({TEST_BINARY_DIR "/executor/print_env"}, 1000,
10000, false, path_to_write_seed, 0, 0, true,
{"FOO=World"});

// Run executor
executor.Run(nullptr, 0);

// The execution should success due to the required environment variable is
// set
BOOST_CHECK_EQUAL(executor.GetExitStatusFeedback().exit_reason,
PUTExitReasonType::FAULT_NONE);

// Check if appropriate value is set on the environment variable
const auto standard_output = executor.MoveStdOut();
const auto expected_output = std::string("World\n");
BOOST_CHECK_EQUAL_COLLECTIONS(standard_output.begin(), standard_output.end(),
expected_output.begin(), expected_output.end());
}

BOOST_AUTO_TEST_CASE(NativeLinuxExecutorWithoutEnvironmentVariables) {
// Setup root directory
std::string root_dir_template("/tmp/fuzzuf_test.XXXXXX");
auto *const raw_dirname = mkdtemp(root_dir_template.data());
BOOST_CHECK(raw_dirname != nullptr);
auto root_dir = fs::path(raw_dirname);
BOOST_SCOPE_EXIT(&root_dir) { fs::remove_all(root_dir); }
BOOST_SCOPE_EXIT_END
auto output_dir = root_dir / "output";
BOOST_CHECK_EQUAL(fs::create_directory(output_dir), true);
auto path_to_write_seed = output_dir / "cur_input";

// Create executor
NativeLinuxExecutor executor({TEST_BINARY_DIR "/executor/print_env"}, 1000,
10000, false, path_to_write_seed, 0, 0, true);

// Run executor
executor.Run(nullptr, 0);

// The execution should fail due to the required environment variable is not
// set
BOOST_CHECK_EQUAL(executor.GetExitStatusFeedback().exit_reason,
PUTExitReasonType::FAULT_CRASH);
}

0 comments on commit 23b3e52

Please sign in to comment.