Add crashes and fixes found during fuzzy testing

* Let unhandled exceptions propogate to user
 * Report eval_error when break statement is not in loop
 * Fix handling of 0 length scripts closes #193
 * Don't crash on arity mismatch - Specifically affects the case where no overloads exist for a given function
 * Fix error printing for `bind` calls
 * Handle unexpected continue statement
 * Check arity during bind
 * Don't allow arith conversion on variadic function
 * Correct `bind` parameter match count
 * Add in expected Boxed_Value exception cases
 * Check access to AST, don't allow `;` in func def
 * Don't attempt arithmetic unary & call
 * Don't crash on 0 param call to `bind`
 * Catch errors during member function dispatch
 * Properly handle type of const bool &
This commit is contained in:
Jason Turner
2015-07-13 14:44:26 -06:00
parent 2fbc377119
commit 1f72afc8f5
12 changed files with 203 additions and 97 deletions

View File

@@ -14,13 +14,14 @@ env:
before_install:
- export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER"
- if [ "$GCC_VER" = "4.8" ]; then export COVERAGE=1 CPPCHECK=1; fi
- if [ ${COVERAGE} = 1 ]; then export FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE"; fi
- sudo pip install cpp-coveralls
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update
- sudo apt-get install -qq g++-$GCC_VER
script:
- if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug . ; fi
- if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD . ; fi
- if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make -j2 ; fi
- make test
- if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi

View File

@@ -18,6 +18,7 @@ option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE)
option(BUILD_MODULES "Build Extra Modules (stl)" TRUE)
option(BUILD_SAMPLES "Build Samples Folder" FALSE)
option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE)
option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE)
mark_as_advanced(USE_STD_MAKE_SHARED)
@@ -269,9 +270,51 @@ if(BUILD_MODULES)
endif()
file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CURRENT_SOURCE_DIR}/unittests/*.chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/3.x/*.chai)
list(SORT UNIT_TESTS)
if (RUN_FUZZY_TESTS)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2015-07-16.tar.bz2
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests
)
file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*)
list(SORT FUZZY_CRASH_TESTS)
file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*)
list(SORT FUZZY_EXCEPTION_TESTS)
foreach(filename ${FUZZY_CRASH_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_CRASH_TESTS}
PROPERTY ENVIRONMENT
"CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
)
foreach(filename ${FUZZY_EXCEPTION_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_EXCEPTION_TESTS}
PROPERTY ENVIRONMENT
"CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
)
endif()
if(BUILD_TESTING)
# Add catch tests macro

View File

@@ -304,13 +304,17 @@ namespace chaiscript
/// the remaining parameters are the args to bind into the result
static Boxed_Value bind_function(const std::vector<Boxed_Value> &params)
{
if (params.size() < 2)
{
throw exception::arity_error(static_cast<int>(params.size()), 2);
if (params.empty()) {
throw exception::arity_error(0, 1);
}
Const_Proxy_Function f = boxed_cast<Const_Proxy_Function>(params[0]);
if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1)
{
throw exception::arity_error(static_cast<int>(params.size()), f->get_arity());
}
return Boxed_Value(Const_Proxy_Function(std::make_shared<dispatch::Bound_Function>(std::move(f),
std::vector<Boxed_Value>(params.begin() + 1, params.end()))));
}

View File

@@ -22,6 +22,7 @@
#include "../chaiscript_defines.hpp"
#include "../chaiscript_threading.hpp"
#include "bad_boxed_cast.hpp"
#include "boxed_cast.hpp"
#include "boxed_cast_helper.hpp"
#include "boxed_value.hpp"
@@ -873,7 +874,14 @@ namespace chaiscript
std::vector<Boxed_Value> remaining_params{l_params.begin() + l_num_params, l_params.end()};
Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions);
if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type<dispatch::Proxy_Function_Base>())) {
return (*boxed_cast<const dispatch::Proxy_Function_Base *>(bv))(remaining_params, l_conversions);
auto func = boxed_cast<std::shared_ptr<const dispatch::Proxy_Function_Base>>(bv);
try {
return (*func)(remaining_params, l_conversions);
} catch (const chaiscript::exception::bad_boxed_cast &) {
} catch (const chaiscript::exception::arity_error &) {
} catch (const chaiscript::exception::guard_error &) {
}
throw chaiscript::exception::dispatch_error(remaining_params, std::vector<Const_Proxy_Function>{func});
} else {
return bv;
}

View File

@@ -149,7 +149,11 @@ namespace chaiscript
Boxed_Value operator()(const std::vector<Boxed_Value> &params, const chaiscript::Type_Conversions &t_conversions) const
{
return do_call(params, t_conversions);
if (m_arity < 0 || size_t(m_arity) == params.size()) {
return do_call(params, t_conversions);
} else {
throw exception::arity_error(static_cast<int>(params.size()), m_arity);
}
}
/// Returns a vector containing all of the types of the parameters the function returns/takes
@@ -420,17 +424,11 @@ namespace chaiscript
protected:
virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
if (m_arity < 0 || params.size() == size_t(m_arity))
if (call_match(params, t_conversions) && test_guard(params, t_conversions))
{
if (call_match(params, t_conversions) && test_guard(params, t_conversions))
{
return m_f(params);
} else {
throw exception::guard_error();
}
return m_f(params);
} else {
throw exception::arity_error(static_cast<int>(params.size()), m_arity);
throw exception::guard_error();
}
}
@@ -728,19 +726,14 @@ namespace chaiscript
protected:
virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
if (params.size() == 1)
const Boxed_Value &bv = params[0];
if (bv.is_const())
{
const Boxed_Value &bv = params[0];
if (bv.is_const())
{
const Class *o = boxed_cast<const Class *>(bv, &t_conversions);
return detail::Handle_Return<const typename std::add_lvalue_reference<T>::type>::handle(o->*m_attr);
} else {
Class *o = boxed_cast<Class *>(bv, &t_conversions);
return detail::Handle_Return<typename std::add_lvalue_reference<T>::type>::handle(o->*m_attr);
}
const Class *o = boxed_cast<const Class *>(bv, &t_conversions);
return detail::Handle_Return<const typename std::add_lvalue_reference<T>::type>::handle(o->*m_attr);
} else {
throw exception::arity_error(static_cast<int>(params.size()), 1);
Class *o = boxed_cast<Class *>(bv, &t_conversions);
return detail::Handle_Return<typename std::add_lvalue_reference<T>::type>::handle(o->*m_attr);
}
}
@@ -787,6 +780,9 @@ namespace chaiscript
const Type_Conversions &t_conversions)
{
const std::vector<Type_Info> &types = t_func->get_param_types();
if (t_func->get_arity() == -1) return false;
assert(plist.size() == types.size() - 1);
return std::mismatch(plist.begin(), plist.end(),

View File

@@ -29,7 +29,7 @@ namespace chaiscript
class Type_Info
{
public:
CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void,
CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void,
bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti)
: m_type_info(t_ti), m_bare_type_info(t_bare_ti),
m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer),
@@ -38,7 +38,7 @@ namespace chaiscript
{
}
CHAISCRIPT_CONSTEXPR Type_Info()
CHAISCRIPT_CONSTEXPR Type_Info()
: m_type_info(nullptr), m_bare_type_info(nullptr),
m_is_const(false), m_is_reference(false), m_is_pointer(false),
m_is_void(false), m_is_arithmetic(false),
@@ -134,13 +134,13 @@ namespace chaiscript
{
typedef T type;
static Type_Info get()
static Type_Info get()
{
return Type_Info(std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value,
std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
(std::is_arithmetic<T>::value || std::is_arithmetic<typename std::remove_reference<T>::type>::value)
&& !std::is_same<typename std::remove_const<T>::type, bool>::value,
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(T),
&typeid(typename Bare_Type<T>::type));
}
@@ -151,11 +151,11 @@ namespace chaiscript
{
typedef T type;
static Type_Info get()
static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<T>::type, bool>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(std::shared_ptr<T> ),
&typeid(typename Bare_Type<T>::type));
}
@@ -166,11 +166,11 @@ namespace chaiscript
{
typedef T type;
static Type_Info get()
static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<T>::type, bool>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(const std::shared_ptr<T> &),
&typeid(typename Bare_Type<T>::type));
}
@@ -181,11 +181,11 @@ namespace chaiscript
{
typedef T type;
static Type_Info get()
static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<T>::type, bool>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(std::reference_wrapper<T> ),
&typeid(typename Bare_Type<T>::type));
}
@@ -196,11 +196,11 @@ namespace chaiscript
{
typedef T type;
static Type_Info get()
static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<T>::type, bool>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(const std::reference_wrapper<T> &),
&typeid(typename Bare_Type<T>::type));
}

View File

@@ -276,10 +276,13 @@ namespace chaiscript
template<typename T>
static std::string format_location(const T &t)
{
std::ostringstream oss;
oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")";
return oss.str();
if (t) {
std::ostringstream oss;
oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")";
return oss.str();
} else {
return "(internal)";
}
}

View File

@@ -1117,11 +1117,22 @@ namespace chaiscript
AST_Node(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { }
virtual ~File_AST_Node() {}
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE {
const auto num_children = children.size();
for (size_t i = 0; i < num_children-1; ++i) {
children[i]->eval(t_ss);
try {
const auto num_children = children.size();
if (num_children > 0) {
for (size_t i = 0; i < num_children-1; ++i) {
children[i]->eval(t_ss);
}
return children.back()->eval(t_ss);
} else {
return Boxed_Value();
}
} catch (const detail::Continue_Loop &) {
throw exception::eval_error("Unexpected `continue` statement outside of a loop");
} catch (const detail::Break_Loop &) {
throw exception::eval_error("Unexpected `break` statement outside of a loop");
}
return children.back()->eval(t_ss);
}
};
@@ -1158,7 +1169,7 @@ namespace chaiscript
try {
// short circuit arithmetic operations
if (m_oper != Operators::invalid && bv.get_type_info().is_arithmetic())
if (m_oper != Operators::invalid && m_oper != Operators::bitwise_and && bv.get_type_info().is_arithmetic())
{
return Boxed_Number::do_oper(m_oper, bv);
} else {

View File

@@ -194,6 +194,7 @@ namespace chaiscript
/// Returns the front-most AST node
AST_NodePtr ast() const {
if (m_match_stack.empty()) throw exception::eval_error("Attempted to access AST of failed parse.");
return m_match_stack.front();
}
@@ -261,7 +262,7 @@ namespace chaiscript
}
AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true) {
AST_NodePtr p = m_match_stack.front();
AST_NodePtr p = ast();
//Note, optimize_blocks is currently broken; it breaks stack management
if (t_optimize_blocks) { optimize_blocks(p); }
if (t_optimize_returns) { optimize_returns(p); }
@@ -1208,37 +1209,32 @@ namespace chaiscript
}
/// Reads an end-of-line group from input, without skipping initial whitespace
bool Eol_() {
bool Eol_(const bool t_eos = false) {
bool retval = false;
if (has_more_input() && (Symbol_("\r\n") || Char_('\n'))) {
retval = true;
++m_line;
m_col = 1;
} else if (has_more_input() && Char_(';')) {
} else if (has_more_input() && !t_eos && Char_(';')) {
retval = true;
}
return retval;
}
/// Reads (and potentially captures) an end-of-line group from input
bool Eol(const bool t_capture = false) {
/// Reads until the end of the current statement
bool Eos() {
SkipWS();
if (!t_capture) {
return Eol_();
} else {
const auto start = m_input_pos;
const auto prev_col = m_col;
const auto prev_line = m_line;
if (Eol_()) {
m_match_stack.push_back(make_node<eval::Eol_AST_Node>(std::string(start, m_input_pos), prev_line, prev_col));
return true;
} else {
return false;
}
}
return Eol_(true);
}
/// Reads (and potentially captures) an end-of-line group from input
bool Eol() {
SkipWS();
return Eol_();
}
/// Reads a comma-separated list of values from input. Id's only, no types allowed
@@ -1441,7 +1437,7 @@ namespace chaiscript
}
}
while (Eol()) {}
while (Eos()) {}
if (Char(':')) {
if (!Operator()) {

View File

@@ -297,6 +297,8 @@ int main(int argc, char *argv[])
chai.add(chaiscript::fun(&get_eval_error), "get_eval_error");
chai.add(chaiscript::fun(&now), "now");
bool eval_error_ok = false;
bool boxed_exception_ok = false;
for (int i = 0; i < argc; ++i) {
if ( i == 0 && argc > 1 ) {
@@ -327,6 +329,12 @@ int main(int argc, char *argv[])
arg = "print(version())" ;
} else if ( arg == "-h" || arg == "--help" ) {
arg = "help(-1)";
} else if ( arg == "-e" || arg == "--evalerrorok" ) {
eval_error_ok = true;
continue;
} else if ( arg == "--exception" ) {
boxed_exception_ok = true;
continue;
} else if ( arg == "-i" || arg == "--interactive" ) {
mode = eInteractive ;
} else if ( arg.find('-') == 0 ) {
@@ -352,12 +360,23 @@ int main(int argc, char *argv[])
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.pretty_print();
std::cout << '\n';
return EXIT_FAILURE;
if (!eval_error_ok) {
return EXIT_FAILURE;
}
}
catch (std::exception &e) {
std::cout << e.what() << '\n';
return EXIT_FAILURE;
catch (const chaiscript::Boxed_Value &e) {
std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n';
if (!boxed_exception_ok) {
return EXIT_FAILURE;
}
}
// catch (std::exception &e) {
// std::cout << e.what() << '\n';
// return EXIT_FAILURE;
// }
}
return EXIT_SUCCESS;

Binary file not shown.

View File

@@ -5,33 +5,58 @@
#include <iostream>
#include <cstdlib>
void test_type(const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void,
bool t_is_undef)
#ifdef CHAISCRIPT_MSVC
#pragma warning(push)
#pragma warning(disable : 4190 4640 28251 4702 6330)
#endif
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#ifdef __llvm__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("Type_Info objects generate expected results")
{
if (ti.is_const() == t_is_const
&& ti.is_pointer() == t_is_pointer
&& ti.is_reference() == t_is_reference
&& ti.is_void() == t_is_void
&& ti.is_undef() == t_is_undef)
const auto test_type = [](const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void,
bool t_is_undef, bool t_is_arithmetic)
{
return;
} else {
exit(EXIT_FAILURE);
}
}
CHECK(ti.is_const() == t_is_const);
CHECK(ti.is_pointer() == t_is_pointer);
CHECK(ti.is_reference() == t_is_reference);
CHECK(ti.is_void() == t_is_void);
CHECK(ti.is_undef() == t_is_undef);
CHECK(ti.is_arithmetic() == t_is_arithmetic);
};
int main()
{
test_type(chaiscript::user_type<void>(), false, false, false, true, false);
test_type(chaiscript::user_type<const int>(), true, false, false, false, false);
test_type(chaiscript::user_type<const int &>(), true, false, true, false, false);
test_type(chaiscript::user_type<int>(), false, false, false, false, false);
test_type(chaiscript::user_type<int *>(), false, true, false, false, false);
test_type(chaiscript::user_type<const int *>(), true, true, false, false, false);
test_type(chaiscript::Type_Info(), false, false, false, false, true);
SECTION("void") { test_type(chaiscript::user_type<void>(), false, false, false, true, false, false); }
SECTION("const int") { test_type(chaiscript::user_type<const int>(), true, false, false, false, false, true); }
SECTION("const int &") { test_type(chaiscript::user_type<const int &>(), true, false, true, false, false, true); }
SECTION("int") { test_type(chaiscript::user_type<int>(), false, false, false, false, false, true); }
SECTION("int *") { test_type(chaiscript::user_type<int *>(), false, true, false, false, false, false); }
SECTION("const int *") { test_type(chaiscript::user_type<const int *>(), true, true, false, false, false, false); }
SECTION("const bool &") { test_type(chaiscript::user_type<const bool &>(), true, false, true, false, false, false); }
SECTION("default") { test_type(chaiscript::Type_Info(), false, false, false, false, true, false); }
std::cout << "Size of Type_Info " << sizeof(chaiscript::Type_Info) << '\n';
return EXIT_SUCCESS;
}