From e65e1417415fd03329da085d13ec45d43c0f6e3c Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Sun, 4 Apr 2021 06:47:17 +0200 Subject: [PATCH] sanitize var names, provide cached builtins for var handlers --- src/cs_gen.cc | 30 ++-------- src/cs_ident.cc | 4 +- src/cs_state.cc | 154 ++++++++++++++++++++++++++++++++++++++++++------ src/cs_state.hh | 5 ++ src/cs_vm.cc | 15 +---- 5 files changed, 152 insertions(+), 56 deletions(-) diff --git a/src/cs_gen.cc b/src/cs_gen.cc index 7c78cfe..3726a92 100644 --- a/src/cs_gen.cc +++ b/src/cs_gen.cc @@ -1204,10 +1204,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) { ); goto endstatement; case ident_type::IVAR: { - auto *hid = gs.ts.pstate->get_ident("//ivar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid ivar handler"}; - } + auto *hid = gs.ts.istate->cmd_ivar; compile_cmd( gs, static_cast(hid), id, more, rettype, 1 @@ -1215,10 +1212,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) { goto endstatement; } case ident_type::FVAR: { - auto *hid = gs.ts.pstate->get_ident("//fvar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid fvar handler"}; - } + auto *hid = gs.ts.istate->cmd_fvar; compile_cmd( gs, static_cast(hid), id, more, rettype, 1 @@ -1226,10 +1220,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) { goto endstatement; } case ident_type::SVAR: { - auto *hid = gs.ts.pstate->get_ident("//svar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid svar handler"}; - } + auto *hid = gs.ts.istate->cmd_svar; compile_cmd( gs, static_cast(hid), id, more, rettype, 1 @@ -1338,10 +1329,7 @@ noid: compile_and_or(gs, id, more, rettype); break; case ID_IVAR: { - auto *hid = gs.ts.pstate->get_ident("//ivar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid ivar handler"}; - } + auto *hid = gs.ts.istate->cmd_ivar; compile_cmd( gs, static_cast(hid), id, more, rettype @@ -1349,10 +1337,7 @@ noid: break; } case ID_FVAR: { - auto *hid = gs.ts.pstate->get_ident("//fvar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid fvar handler"}; - } + auto *hid = gs.ts.istate->cmd_fvar; compile_cmd( gs, static_cast(hid), id, more, rettype @@ -1360,10 +1345,7 @@ noid: break; } case ID_SVAR: { - auto *hid = gs.ts.pstate->get_ident("//svar"); - if (!hid || !hid->is_command()) { - throw error{gs.ts, "invalid svar handler"}; - } + auto *hid = gs.ts.istate->cmd_svar; compile_cmd( gs, static_cast(hid), id, more, rettype diff --git a/src/cs_ident.cc b/src/cs_ident.cc index cf7b923..84aa86f 100644 --- a/src/cs_ident.cc +++ b/src/cs_ident.cc @@ -83,8 +83,8 @@ command_impl::command_impl( {} void var_changed(thread_state &ts, ident *id) { - auto *cid = ts.pstate->get_ident("//var_changed"); - if (!cid || !cid->is_command()) { + auto *cid = ts.istate->cmd_var_changed; + if (!cid) { return; } auto *cimp = static_cast(cid); diff --git a/src/cs_state.cc b/src/cs_state.cc index 999113b..5195a07 100644 --- a/src/cs_state.cc +++ b/src/cs_state.cc @@ -1,4 +1,6 @@ #include +#include +#include #include "cs_bcode.hh" #include "cs_state.hh" @@ -58,7 +60,7 @@ ident *internal_state::new_ident(state &cs, std::string_view name, int flags) { if (!id) { if (!is_valid_name(name)) { throw error{ - cs, "number %s is not a valid identifier name", name.data() + cs, "'%s' is not a valid identifier name", name.data() }; } auto *inst = create( @@ -133,6 +135,55 @@ state::state(alloc_func func, void *data) { throw internal_error{"invalid dbgalias index"}; } + /* default handlers for variables */ + + statep->cmd_ivar = new_command("//ivar_builtin", "$iN", []( + auto &cs, auto args, auto & + ) { + auto *iv = args[0].get_ident()->get_ivar(); + if (args[2].get_int() <= 1) { + std::printf("%s = %d\n", iv->get_name().data(), iv->get_value()); + } else { + iv->set_value(cs, args[1].get_int()); + } + }); + + statep->cmd_fvar = new_command("//fvar_builtin", "$fN", []( + auto &cs, auto args, auto & + ) { + auto *fv = args[0].get_ident()->get_fvar(); + if (args[2].get_int() <= 1) { + auto val = fv->get_value(); + if (std::floor(val) == val) { + std::printf("%s = %.1f\n", fv->get_name().data(), val); + } else { + std::printf("%s = %.7g\n", fv->get_name().data(), val); + } + } else { + fv->set_value(cs, args[1].get_float()); + } + }); + + statep->cmd_svar = new_command("//svar_builtin", "$sN", []( + auto &cs, auto args, auto & + ) { + auto *sv = args[0].get_ident()->get_svar(); + if (args[2].get_int() <= 1) { + auto val = std::string_view{sv->get_value()}; + if (val.find('"') == val.npos) { + std::printf("%s = \"%s\"\n", sv->get_name().data(), val.data()); + } else { + std::printf("%s = [%s]\n", sv->get_name().data(), val.data()); + } + } else { + sv->set_value(cs, args[1].get_str()); + } + }); + + statep->cmd_var_changed = nullptr; + + /* builtins */ + p = new_command("do", "e", [](auto &cs, auto args, auto &res) { cs.run(args[0].get_code(), res); }); @@ -356,6 +407,21 @@ inline int var_flags(bool read_only, var_type vtp) { return ret; } +static void var_name_check( + state &cs, ident *id, std::string_view n +) { + if (id) { + throw error{ + cs, "redefinition of ident '%.*s'", int(n.size()), n.data() + }; + } else if (!is_valid_name(n)) { + throw error{ + cs, "'%.*s' is not a valid variable name", + int(n.size()), n.data() + }; + } +} + LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar( std::string_view n, integer_type v, bool read_only, var_type vtp ) { @@ -363,6 +429,12 @@ LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar( string_ref{p_tstate->istate, n}, v, var_flags(read_only, vtp) ); + try { + var_name_check(*this, p_tstate->istate->get_ident(n), n); + } catch (...) { + p_tstate->istate->destroy(iv); + throw; + } p_tstate->istate->add_ident(iv, iv); return iv; } @@ -374,6 +446,12 @@ LIBCUBESCRIPT_EXPORT float_var *state::new_fvar( string_ref{p_tstate->istate, n}, v, var_flags(read_only, vtp) ); + try { + var_name_check(*this, p_tstate->istate->get_ident(n), n); + } catch (...) { + p_tstate->istate->destroy(fv); + throw; + } p_tstate->istate->add_ident(fv, fv); return fv; } @@ -385,6 +463,12 @@ LIBCUBESCRIPT_EXPORT string_var *state::new_svar( string_ref{p_tstate->istate, n}, string_ref{p_tstate->istate, v}, var_flags(read_only, vtp) ); + try { + var_name_check(*this, p_tstate->istate->get_ident(n), n); + } catch (...) { + p_tstate->istate->destroy(sv); + throw; + } p_tstate->istate->add_ident(sv, sv); return sv; } @@ -448,6 +532,12 @@ LIBCUBESCRIPT_EXPORT void state::set_alias( } } +static char const *allowed_builtins[] = { + "//ivar", "//fvar", "//svar", "//var_changed", + "//ivar_builtin", "//fvar_builtin", "//svar_builtin", + nullptr +}; + LIBCUBESCRIPT_EXPORT command *state::new_command( std::string_view name, std::string_view args, command_func func ) { @@ -494,12 +584,49 @@ LIBCUBESCRIPT_EXPORT command *state::new_command( return nullptr; } } - auto *cmd = p_tstate->istate->create( - string_ref{p_tstate->istate, name}, - string_ref{p_tstate->istate, args}, - nargs, std::move(func) + auto &is = *p_tstate->istate; + auto *cmd = is.create( + string_ref{&is, name}, string_ref{&is, args}, nargs, std::move(func) ); - p_tstate->istate->add_ident(cmd, cmd); + /* we can set these builtins */ + command **bptrs[] = { + &is.cmd_ivar, &is.cmd_fvar, &is.cmd_svar, &is.cmd_var_changed + }; + auto nbptrs = sizeof(bptrs) / sizeof(*bptrs); + /* provided a builtin */ + if ((name.size() >= 2) && (name[0] == '/') && (name[1] == '/')) { + /* sanitize */ + for (auto **p = allowed_builtins; *p; ++p) { + if (!name.compare(*p)) { + /* if it's one of the settable ones, maybe set it */ + if (std::size_t(p - allowed_builtins) < nbptrs) { + if (!is.get_ident(name)) { + /* only set if it does not exist already */ + *bptrs[p - allowed_builtins] = cmd; + goto do_add; + } + } + /* this will ensure we're not redefining them */ + goto valid; + } + } + /* we haven't found one matching the list, so error */ + is.destroy(cmd); + throw error{ + *this, "forbidden builtin command: %.*s", + int(name.size()), name.data() + }; + } +valid: + if (is.get_ident(name)) { + is.destroy(cmd); + throw error{ + *this, "redefinition of ident '%.*s'", + int(name.size()), name.data() + }; + } +do_add: + is.add_ident(cmd, cmd); return cmd; } @@ -593,10 +720,7 @@ LIBCUBESCRIPT_EXPORT void state::run( break; } case ident_type::IVAR: { - auto *hid = get_ident("//ivar"); - if (!hid || !hid->is_command()) { - throw error{*p_tstate, "invalid ivar handler"}; - } + auto *hid = p_tstate->istate->cmd_ivar; auto *cimp = static_cast(hid); auto &targs = p_tstate->vmstack; auto osz = targs.size(); @@ -613,10 +737,7 @@ LIBCUBESCRIPT_EXPORT void state::run( break; } case ident_type::FVAR: { - auto *hid = get_ident("//fvar"); - if (!hid || !hid->is_command()) { - throw error{*p_tstate, "invalid fvar handler"}; - } + auto *hid = p_tstate->istate->cmd_fvar; auto *cimp = static_cast(hid); auto &targs = p_tstate->vmstack; auto osz = targs.size(); @@ -633,10 +754,7 @@ LIBCUBESCRIPT_EXPORT void state::run( break; } case ident_type::SVAR: { - auto *hid = get_ident("//svar"); - if (!hid || !hid->is_command()) { - throw error{*p_tstate, "invalid svar handler"}; - } + auto *hid = p_tstate->istate->cmd_svar; auto *cimp = static_cast(hid); auto &targs = p_tstate->vmstack; auto osz = targs.size(); diff --git a/src/cs_state.hh b/src/cs_state.hh index 6357a7f..aed3eec 100644 --- a/src/cs_state.hh +++ b/src/cs_state.hh @@ -52,6 +52,11 @@ struct internal_state { string_pool *strman; empty_block *empty; + command *cmd_ivar; + command *cmd_fvar; + command *cmd_svar; + command *cmd_var_changed; + internal_state() = delete; internal_state(alloc_func af, void *data); diff --git a/src/cs_vm.cc b/src/cs_vm.cc index c287de1..7233454 100644 --- a/src/cs_vm.cc +++ b/src/cs_vm.cc @@ -1146,10 +1146,7 @@ noid: return code; } case ID_IVAR: { - auto *hid = cs.get_ident("//ivar"); - if (!hid || !hid->is_command()) { - throw error{ts, "invalid ivar handler"}; - } + auto *hid = ts.istate->cmd_ivar; auto *cimp = static_cast(hid); /* the $ argument */ args.insert(offset, any_value{cs}); @@ -1164,10 +1161,7 @@ noid: continue; } case ID_FVAR: { - auto *hid = cs.get_ident("//fvar"); - if (!hid || !hid->is_command()) { - throw error{ts, "invalid fvar handler"}; - } + auto *hid = ts.istate->cmd_fvar; auto *cimp = static_cast(hid); /* the $ argument */ args.insert(offset, any_value{cs}); @@ -1182,10 +1176,7 @@ noid: continue; } case ID_SVAR: { - auto *hid = cs.get_ident("//svar"); - if (!hid || !hid->is_command()) { - throw error{ts, "invalid svar handler"}; - } + auto *hid = ts.istate->cmd_svar; auto *cimp = static_cast(hid); /* the $ argument */ args.insert(offset, any_value{cs});