From e034a7834c981a104c9785d1bed51f0a90436308 Mon Sep 17 00:00:00 2001 From: Louis Rubet Date: Thu, 24 Feb 2022 11:31:27 +0100 Subject: [PATCH] Harden branch checks on command flow indexes --- TODO.md | 2 +- src/object.hpp | 10 +-- src/program.cpp | 15 ++-- src/program.hpp | 29 +++---- src/rpn-branch.cpp | 204 ++++++++++++++++++++++++++++----------------- src/rpn-test.cpp | 6 +- 6 files changed, 155 insertions(+), 111 deletions(-) diff --git a/TODO.md b/TODO.md index ba08ba0..0412908 100644 --- a/TODO.md +++ b/TODO.md @@ -3,4 +3,4 @@ TODO missing tests / problems - les arguments d'une fonction en erreur doivent ils ĂȘtre consommĂ©s ? ex embettant : sto+ -- `1 'i' sto while i <= 2 repeat 0 'j' sto while j <= 1 repeat i (1,0) * j (0,1) * + 1 'j' sto+ end 1 'i' sto+ end` plante +- `1 'i' sto while i 2 <= repeat 0 'j' sto while 1 <= j repeat i (1,0) * j (0,1) * + 1 'j' sto+ end 1 'i' sto+ end` plante diff --git a/src/object.hpp b/src/object.hpp index 6b83186..994e1ab 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -31,7 +31,7 @@ class program; class branch; typedef void (program::*program_fn_t)(void); -typedef int (program::*branch_fn_t)(branch&); +typedef size_t (program::*branch_fn_t)(branch&); /// @brief object - a generic stack object /// @@ -174,9 +174,9 @@ struct branch : object { branch() : object(cmd_branch) {} branch(branch_fn_t fn_, const string& value_) : object(cmd_branch) { fn = fn_; - arg1 = -1; - arg2 = -1; - arg3 = -1; + arg1 = (size_t)-1; + arg2 = (size_t)-1; + arg3 = (size_t)-1; arg_bool = 0; value = value_; } @@ -191,7 +191,7 @@ struct branch : object { virtual object* clone() { return new branch(*this); } virtual string name() { return string("branch"); } branch_fn_t fn; - int arg1, arg2, arg3; + size_t arg1, arg2, arg3; mpreal firstIndex, lastIndex; bool arg_bool; string value; diff --git a/src/program.cpp b/src/program.cpp index 01e176f..5ae1757 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -236,8 +236,8 @@ ret_value program::run() { } // iterate commands - for (int i = 0; (go_out == false) && (interrupt_now == false) && (i < (int)size());) { - object* o = (*this)[i]; + for (size_t i = 0; (go_out == false) && (interrupt_now == false) && (i < size());) { + object* o = at(i); switch (o->_type) { // could be an auto-evaluated symbol case cmd_symbol: @@ -281,18 +281,17 @@ ret_value program::run() { case cmd_branch: { // call matching function branch* b = (branch*)o; - int next_cmd = (this->*(b->fn))(*b); + size_t next_cmd = (this->*(b->fn))(*b); switch (next_cmd) { - case -1: + case step_out: // step out i++; // meaning 'next command' break; - case -(int)ret_runtime_error: - // error: show it + case runtime_error: // runtime error (void)show_error(_err, _err_context); - go_out = true; // end of run + go_out = true; break; default: - i = next_cmd; // new direction + i = next_cmd; break; } break; diff --git a/src/program.hpp b/src/program.hpp index 1c98f09..02b5eb9 100644 --- a/src/program.hpp +++ b/src/program.hpp @@ -13,9 +13,9 @@ using namespace mpfr; // internal includes #include "constant.h" +#include "lexer.hpp" #include "object.hpp" #include "stack.hpp" -#include "lexer.hpp" //< convinient structure to preprocess a program struct if_layout_t { @@ -32,7 +32,7 @@ struct if_layout_t { //< program class: the class containing a string parser, all the programs keywords, a stack for running the program class program : public deque, public Lexer { public: - program(rpnstack& stk, heap& hp, program* parent = nullptr):_stack(stk),_heap(hp),_parent(parent) { + program(rpnstack& stk, heap& hp, program* parent = nullptr) : _stack(stk), _heap(hp), _parent(parent) { interrupt_now = false; } virtual ~program() { @@ -99,20 +99,21 @@ class program : public deque, public Lexer { //// // branch - int rpn_if(branch& myobj); - int rpn_then(branch& myobj); - int rpn_else(branch& myobj); - int rpn_end(branch& myobj); - int rpn_do(branch& myobj); - int rpn_until(branch& myobj); + size_t rpn_if(branch& myobj); + size_t rpn_then(branch& myobj); + size_t rpn_else(branch& myobj); + size_t rpn_end(branch& myobj); + size_t rpn_do(branch& myobj); + size_t rpn_until(branch& myobj); void rpn_ift(void); void rpn_ifte(void); - int rpn_while(branch& myobj); - int rpn_repeat(branch& myobj); - int rpn_start(branch& myobj); - int rpn_for(branch& myobj); - int rpn_next(branch& myobj); - int rpn_step(branch& myobj); + size_t rpn_while(branch& myobj); + size_t rpn_repeat(branch& myobj); + size_t rpn_start(branch& myobj); + size_t rpn_for(branch& myobj); + size_t rpn_next(branch& myobj); + size_t rpn_step(branch& myobj); + enum { step_out = (size_t)-1, runtime_error = (size_t)-2 }; // complex void rpn_re(); diff --git a/src/rpn-branch.cpp b/src/rpn-branch.cpp index 167758b..d9c42e0 100644 --- a/src/rpn-branch.cpp +++ b/src/rpn-branch.cpp @@ -3,34 +3,42 @@ /// @brief if keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_if(branch& myobj) { +size_t program::rpn_if(branch& myobj) { // myobj.arg1 = 'if' condition evaluation value - MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); + MIN_ARGUMENTS_RET(1, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); if (_stack.value(0) != 0) myobj.arg1 = 1; else myobj.arg1 = 0; _stack.pop(); - return -1; + return step_out; } /// @brief then keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_then(branch& myobj) { +size_t program::rpn_then(branch& myobj) { // myobj.arg1 = index of then + 1 // myobj.arg2 = index of else + 1 or end + 1 // myobj.arg3 = index of if // if condition is true -> arg1 (= jump to then + 1) // else -> arg2 (= jump to else + 1 or end + 1) - branch* if_cmd = (branch*)at(myobj.arg3); + branch* if_cmd; + if (myobj.arg3 >= size() || at(myobj.arg3)->_type != cmd_branch) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + if_cmd = (branch*)at(myobj.arg3); if (if_cmd->arg1 == 1) return myobj.arg1; else @@ -40,16 +48,22 @@ int program::rpn_then(branch& myobj) { /// @brief else keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_else(branch& myobj) { +size_t program::rpn_else(branch& myobj) { // myobj.arg1 = index of else + 1 // myobj.arg2 = index of end + 1 // myobj.arg3 = index of if // if condition was false -> arg1 (= jump to else + 1) // if condition was true -> arg2 (= jump to end + 1) - branch* if_cmd = (branch*)at(myobj.arg3); + branch* if_cmd; + if (myobj.arg3 >= size() || at(myobj.arg3)->_type != cmd_branch) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + if_cmd = (branch*)at(myobj.arg3); if (if_cmd->arg1 == 1) return myobj.arg2; else @@ -59,24 +73,25 @@ int program::rpn_else(branch& myobj) { /// @brief end keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_end(branch& myobj) { - int ret = -1; +size_t program::rpn_end(branch& myobj) { + size_t ret = step_out; // arg1 = index of do+1 in case of do..unti..end if (myobj.arg1 != -1) { - // in a template do..unti..end - MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); + // in a template do..until..end + MIN_ARGUMENTS_RET(1, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); // check arg if (_stack.value(0) == 0) ret = myobj.arg1; _stack.pop(); } // arg2 = index of while+1 in case of while..repeat..end - else if (myobj.arg2 != -1) + else if (myobj.arg2 != step_out) ret = myobj.arg2; return ret; @@ -85,30 +100,33 @@ int program::rpn_end(branch& myobj) { /// @brief do keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_do(branch& myobj) { +size_t program::rpn_do(branch& myobj) { // nothing - return -1; + return step_out; } /// @brief until keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_until(branch& myobj) { +size_t program::rpn_until(branch& myobj) { // nothing - return -1; + return step_out; } /// @brief ift keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// void program::rpn_ift(void) { MIN_ARGUMENTS(2); @@ -126,8 +144,9 @@ void program::rpn_ift(void) { /// @brief ifte keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// void program::rpn_ifte(void) { MIN_ARGUMENTS(3); @@ -146,25 +165,27 @@ void program::rpn_ifte(void) { /// @brief while keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_while(branch& myobj) { +size_t program::rpn_while(branch& myobj) { // nothing - return -1; + return step_out; } /// @brief repeat keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_repeat(branch& myobj) { - int ret = -1; +size_t program::rpn_repeat(branch& myobj) { + size_t ret = step_out; - MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); + MIN_ARGUMENTS_RET(1, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); // check arg // myobj.arg1 is end+1 @@ -177,15 +198,16 @@ int program::rpn_repeat(branch& myobj) { /// @brief start keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_start(branch& myobj) { - int ret = -1; +size_t program::rpn_start(branch& myobj) { + size_t ret = step_out; - MIN_ARGUMENTS_RET(2, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, -(int)ret_runtime_error); + MIN_ARGUMENTS_RET(2, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, runtime_error); // loop boundaries myobj.firstIndex = _stack.value(1); @@ -205,17 +227,23 @@ int program::rpn_start(branch& myobj) { /// @brief for keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_for(branch& myobj) { - int ret; +size_t program::rpn_for(branch& myobj) { + size_t ret; - MIN_ARGUMENTS_RET(2, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, -(int)ret_runtime_error); + MIN_ARGUMENTS_RET(2, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, runtime_error); - symbol* sym = (symbol*)at(myobj.arg1); // arg1 = loop variable index + symbol* sym; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_symbol) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + sym = (symbol*)at(myobj.arg1); // arg1 = loop variable index // loop boundaries myobj.firstIndex = _stack.value(1); @@ -246,13 +274,19 @@ int program::rpn_for(branch& myobj) { /// @brief next keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_next(branch& myobj) { +size_t program::rpn_next(branch& myobj) { // arg1 = loop variable index // firstIndex = current point in the loop - branch* start_or_for = (branch*)at(myobj.arg1); + branch* start_or_for; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_branch) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + start_or_for = (branch*)at(myobj.arg1); if (!myobj.arg_bool) { myobj.arg_bool = true; myobj.firstIndex = start_or_for->firstIndex; @@ -265,8 +299,12 @@ int program::rpn_next(branch& myobj) { // for command: increment symbol too if (start_or_for->arg1 != -1) { object* obj; - unsigned int size; - symbol* var = (symbol*)at(start_or_for->arg1); + symbol* var; + if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != cmd_symbol) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + var = (symbol*)at(start_or_for->arg1); // store symbol variable (asserted existing in the local heap) ((number*)_local_heap[var->value])->value = myobj.firstIndex; @@ -276,7 +314,7 @@ int program::rpn_next(branch& myobj) { if (myobj.firstIndex > start_or_for->lastIndex) { // end of loop myobj.arg_bool = false; // init again next time - return -1; + return step_out; } else { // for command: next instruction will be after symbol variable if (start_or_for->arg1 != -1) return start_or_for->arg1 + 1; @@ -289,24 +327,30 @@ int program::rpn_next(branch& myobj) { /// @brief step keyword (branch) implementation /// /// @param myobj the current branch object -/// @return int index of the next object to run in the current program -/// @return -1 the next object index to run in the current program is the current+1 +/// @return size_t index of the next object to run in the current program +/// @return step_out next object to run in the current program is current + 1 +/// @return runtime_error something went wrong with preprocess, abort branch /// -int program::rpn_step(branch& myobj) { - int ret; - MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error); +size_t program::rpn_step(branch& myobj) { + size_t ret; + MIN_ARGUMENTS_RET(1, runtime_error); + ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); mpreal step = _stack.value(0); _stack.pop(); // end of loop if step is negative or zero if (step <= 0) - ret = -1; + ret = step_out; else { // arg1 = loop variable index // firstIndex = current count - branch* start_or_for = (branch*)at(myobj.arg1); + branch* start_or_for; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_branch) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + start_or_for = (branch*)at(myobj.arg1); if (!myobj.arg_bool) { myobj.arg_bool = true; myobj.firstIndex = start_or_for->firstIndex; @@ -316,21 +360,25 @@ int program::rpn_step(branch& myobj) { // carefull: round toward minus infinity to avoid missing last boundary (because growing step) mpfr_add(myobj.firstIndex.mpfr_ptr(), myobj.firstIndex.mpfr_srcptr(), step.mpfr_srcptr(), MPFR_RNDD); - // for command: increment symbol too if (start_or_for->arg1 != -1) { object* obj; - unsigned int size; - symbol* var = (symbol*)at(start_or_for->arg1); + symbol* var; + // for command: increment symbol too + if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != cmd_symbol) { + setErrorContext(ret_missing_operand); + return runtime_error; + } + var = (symbol*)at(start_or_for->arg1); // increase symbol variable ((number*)_local_heap[var->value])->value = myobj.firstIndex; - } + } // test loop value is out of range if (myobj.firstIndex > start_or_for->lastIndex) { // end of loop myobj.arg_bool = false; // init again next time - ret = -1; + ret = step_out; } else { // for command: next instruction will be after symbol variable if (start_or_for->arg1 != -1) ret = start_or_for->arg1 + 1; diff --git a/src/rpn-test.cpp b/src/rpn-test.cpp index 09ec315..351e890 100644 --- a/src/rpn-test.cpp +++ b/src/rpn-test.cpp @@ -17,7 +17,6 @@ long program::cmp_strings_on_stack_top() { /// void program::rpn_sup(void) { MIN_ARGUMENTS(2); - // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) > _stack.value(0))); @@ -35,7 +34,6 @@ void program::rpn_sup(void) { /// void program::rpn_sup_eq(void) { MIN_ARGUMENTS(2); - // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) >= _stack.value(0))); @@ -53,7 +51,6 @@ void program::rpn_sup_eq(void) { /// void program::rpn_inf(void) { MIN_ARGUMENTS(2); - // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) < _stack.value(0))); @@ -70,6 +67,7 @@ void program::rpn_inf(void) { /// @brief <= keyword implementation /// void program::rpn_inf_eq(void) { + MIN_ARGUMENTS(2); // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) <= _stack.value(0))); @@ -87,7 +85,6 @@ void program::rpn_inf_eq(void) { /// void program::rpn_diff(void) { MIN_ARGUMENTS(2); - // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) != _stack.value(0))); @@ -110,7 +107,6 @@ void program::rpn_diff(void) { /// void program::rpn_eq(void) { MIN_ARGUMENTS(2); - // numbers if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { _stack.push_front(new number(_stack.value(1) == _stack.value(0)));