diff --git a/src/script_opt/ScriptOpt.h b/src/script_opt/ScriptOpt.h index 76126a1fe5..1f58c99a9a 100644 --- a/src/script_opt/ScriptOpt.h +++ b/src/script_opt/ScriptOpt.h @@ -248,6 +248,10 @@ extern void clear_script_analysis(); // Called when Zeek is terminating. extern void finish_script_execution(); +// Returns true if the given call has a specialized ZAM equivalent when +// used in a conditional. +extern bool IsZAM_BuiltInCond(const CallExpr* c); + // Used for C++-compiled scripts to signal their presence, by setting this // to a non-empty value. extern void (*CPP_init_hook)(); diff --git a/src/script_opt/ZAM/BuiltIn.cc b/src/script_opt/ZAM/BuiltIn.cc index 8e9b5fd9a8..85bae5130f 100644 --- a/src/script_opt/ZAM/BuiltIn.cc +++ b/src/script_opt/ZAM/BuiltIn.cc @@ -3,196 +3,142 @@ // ZAM methods associated with instructions that replace calls to // built-in functions. +#include "zeek/script_opt/ZAM/BuiltIn.h" + #include "zeek/Func.h" #include "zeek/Reporter.h" #include "zeek/script_opt/ZAM/Compile.h" namespace zeek::detail { -bool ZAMCompiler::IsZAM_BuiltIn(const Expr* e) { - // The expression e is either directly a call (in which case there's - // no return value), or an assignment to a call. - const CallExpr* c; +// Maps BiF names to their associated ZBI class. +std::unordered_map builtins; - if ( e->Tag() == EXPR_CALL ) - c = e->AsCallExpr(); - else - c = e->GetOp2()->AsCallExpr(); - - auto func_expr = c->Func(); - if ( func_expr->Tag() != EXPR_NAME ) - // An indirect call. - return false; - - auto func_val = func_expr->AsNameExpr()->Id()->GetVal(); - if ( ! func_val ) - // A call to a function that hasn't been defined. - return false; - - auto func = func_val->AsFunc(); - if ( func->GetKind() != BuiltinFunc::BUILTIN_FUNC ) - return false; - - auto& args = c->Args()->Exprs(); - - const NameExpr* n = nullptr; // name to assign to, if any - - if ( e->Tag() != EXPR_CALL ) - n = e->GetOp1()->AsRefExpr()->GetOp1()->AsNameExpr(); - - using GenBuiltIn = bool (ZAMCompiler::*)(const NameExpr* n, const ExprPList& args); - static std::vector> builtins = { - {"Analyzer::__name", &ZAMCompiler::BuiltIn_Analyzer__name}, - {"Broker::__flush_logs", &ZAMCompiler::BuiltIn_Broker__flush_logs}, - {"Files::__enable_reassembly", &ZAMCompiler::BuiltIn_Files__enable_reassembly}, - {"Files::__set_reassembly_buffer", &ZAMCompiler::BuiltIn_Files__set_reassembly_buffer}, - {"Log::__write", &ZAMCompiler::BuiltIn_Log__write}, - {"cat", &ZAMCompiler::BuiltIn_cat}, - {"current_time", &ZAMCompiler::BuiltIn_current_time}, - {"get_port_transport_proto", &ZAMCompiler::BuiltIn_get_port_etc}, - {"network_time", &ZAMCompiler::BuiltIn_network_time}, - {"reading_live_traffic", &ZAMCompiler::BuiltIn_reading_live_traffic}, - {"reading_traces", &ZAMCompiler::BuiltIn_reading_traces}, - {"strstr", &ZAMCompiler::BuiltIn_strstr}, - {"sub_bytes", &ZAMCompiler::BuiltIn_sub_bytes}, - {"to_lower", &ZAMCompiler::BuiltIn_to_lower}, - }; - - for ( auto& b : builtins ) - if ( util::streq(func->Name(), b.first) ) - return (this->*(b.second))(n, args); - - return false; +ZAMBuiltIn::ZAMBuiltIn(std::string name, bool _ret_val_matters) : ret_val_matters(_ret_val_matters) { + builtins[name] = this; } -bool ZAMCompiler::BuiltIn_Analyzer__name(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } +SimpleZBI::SimpleZBI(std::string name, ZOp _op, int _nargs, bool _ret_val_matters) + : ZAMBuiltIn(std::move(name), _ret_val_matters), op(_op), nargs(_nargs) {} - if ( args[0]->Tag() == EXPR_CONST ) - // Doesn't seem worth developing a variant for this weird - // usage case. - return false; - - int nslot = Frame1Slot(n, OP1_WRITE); - auto arg_t = args[0]->AsNameExpr(); - - auto z = ZInstI(OP_ANALYZER__NAME_VV, nslot, FrameSlot(arg_t)); - z.SetType(args[0]->GetType()); - - AddInst(z); - - return true; -} - -bool ZAMCompiler::BuiltIn_Broker__flush_logs(const NameExpr* n, const ExprPList& args) { - if ( n ) - AddInst(ZInstI(OP_BROKER_FLUSH_LOGS_V, Frame1Slot(n, OP1_WRITE))); - else - AddInst(ZInstI(OP_BROKER_FLUSH_LOGS_X)); - - return true; -} - -bool ZAMCompiler::BuiltIn_Files__enable_reassembly(const NameExpr* n, const ExprPList& args) { - if ( n ) - // While this built-in nominally returns a value, existing - // script code ignores it, so for now we don't bother - // special-casing the possibility that it doesn't. - return false; - - if ( args[0]->Tag() == EXPR_CONST ) - // Weird! - return false; - - auto arg_f = args[0]->AsNameExpr(); - - AddInst(ZInstI(OP_FILES__ENABLE_REASSEMBLY_V, FrameSlot(arg_f))); - - return true; -} - -bool ZAMCompiler::BuiltIn_Files__set_reassembly_buffer(const NameExpr* n, const ExprPList& args) { - if ( n ) - // See above for enable_reassembly - return false; - - if ( args[0]->Tag() == EXPR_CONST ) - // Weird! - return false; - - auto arg_f = FrameSlot(args[0]->AsNameExpr()); +SimpleZBI::SimpleZBI(std::string name, ZOp _const_op, ZOp _op, bool _ret_val_matters) + : ZAMBuiltIn(std::move(name), _ret_val_matters), op(_op), const_op(_const_op), nargs(1) {} +bool SimpleZBI::Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const { ZInstI z; - - if ( args[1]->Tag() == EXPR_CONST ) { - auto arg_cnt = args[1]->AsConstExpr()->Value()->AsCount(); - z = ZInstI(OP_FILES__SET_REASSEMBLY_BUFFER_VC, arg_f, arg_cnt); - z.op_type = OP_VV_I2; - } - else - z = ZInstI(OP_FILES__SET_REASSEMBLY_BUFFER_VV, arg_f, FrameSlot(args[1]->AsNameExpr())); - - AddInst(z); - - return true; -} - -bool ZAMCompiler::BuiltIn_Log__write(const NameExpr* n, const ExprPList& args) { - auto id = args[0]; - auto columns = args[1]; - - if ( columns->Tag() != EXPR_NAME ) - return false; - - auto columns_n = columns->AsNameExpr(); - auto col_slot = FrameSlot(columns_n); - - bool const_id = (id->Tag() == EXPR_CONST); - - ZInstAux* aux = nullptr; - - if ( const_id ) { - aux = new ZInstAux(1); - aux->Add(0, id->AsConstExpr()->ValuePtr()); - } - - ZInstI z; - - if ( n ) { - int nslot = Frame1Slot(n, OP1_WRITE); - if ( const_id ) { - z = ZInstI(OP_LOG_WRITEC_VV, nslot, col_slot); - z.aux = aux; - } + if ( nargs == 0 ) { + if ( n ) + z = ZInstI(op, zam->Frame1Slot(n, OP1_WRITE)); else - z = ZInstI(OP_LOG_WRITE_VVV, nslot, FrameSlot(id->AsNameExpr()), col_slot); + z = ZInstI(op); } else { - if ( const_id ) { - z = ZInstI(OP_LOG_WRITEC_V, col_slot, id->AsConstExpr()); - z.aux = aux; + ASSERT(nargs == 1); + auto& t = args[0]->GetType(); + + if ( args[0]->Tag() == EXPR_NAME ) { + auto a0 = zam->FrameSlot(args[0]->AsNameExpr()); + if ( n ) + z = ZInstI(op, zam->Frame1Slot(n, OP1_WRITE), a0); + else + z = ZInstI(op, a0); } - else - z = ZInstI(OP_LOG_WRITE_VV, FrameSlot(id->AsNameExpr()), col_slot); + + else { + if ( const_op == OP_NOP ) + // This can happen for BiFs that aren't foldable, and for + // which it's implausible they'll be called with a constant + // argument. + return false; + + if ( n ) + z = ZInstI(const_op, zam->Frame1Slot(n, OP1_WRITE)); + else + z = ZInstI(const_op); + + z.c = ZVal(args[0]->AsConstExpr()->ValuePtr(), t); + } + + z.t = t; } - z.SetType(columns_n->GetType()); + if ( n ) + z.is_managed = ZVal::IsManagedType(n->GetType()); - AddInst(z); + zam->AddInst(z); return true; } -bool ZAMCompiler::BuiltIn_cat(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); +CondZBI::CondZBI(std::string name, ZOp _op, ZOp _cond_op, int _nargs) + : SimpleZBI(std::move(name), _op, _nargs, true), cond_op(_cond_op) {} + +bool CondZBI::BuildCond(ZAMCompiler* zam, const ExprPList& args, int& branch_v) const { + if ( cond_op == OP_NOP ) + return false; + + if ( nargs == 1 && args[0]->Tag() != EXPR_NAME ) + // ZBI-worthy predicates called with constant arguments will generally + // have been folded. If not, for simplicity we don't support the + // flavor where they're called with a constant. + return false; + + // If we get here, then the ZBI is good-to-go. + + if ( ! zam ) + // This was just a check, not an actual build. return true; + + ZInstI z; + + if ( nargs == 0 ) { + z = ZInstI(cond_op, 0); + z.op_type = OP_V_I1; + branch_v = 1; } - int nslot = Frame1Slot(n, OP1_WRITE); + else { + ASSERT(nargs == 1); + + auto a0 = args[0]; + auto a0_slot = zam->FrameSlot(a0->AsNameExpr()); + z = ZInstI(cond_op, a0_slot, 0); + z.op_type = OP_VV_I2; + z.t = a0->GetType(); + branch_v = 2; + } + + zam->AddInst(z); + + return true; +} + +OptAssignZBI::OptAssignZBI(std::string name, ZOp _op, ZOp _op2, int _nargs) + : SimpleZBI(std::move(name), _op, _nargs, false), op2(_op2) { + have_both = true; +} + +bool OptAssignZBI::Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const { + if ( n ) + return SimpleZBI::Build(zam, n, args); + + ZInstI z; + if ( nargs == 0 ) + z = ZInstI(op2); + else { + ASSERT(nargs == 1); + auto a0 = zam->FrameSlot(args[0]->AsNameExpr()); + z = ZInstI(op2, a0); + z.t = args[0]->GetType(); + } + + zam->AddInst(z); + + return true; +} + +bool CatZBI::Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const { + auto nslot = zam->Frame1Slot(n, OP1_WRITE); auto& a0 = args[0]; ZInstI z; @@ -205,23 +151,23 @@ bool ZAMCompiler::BuiltIn_cat(const NameExpr* n, const ExprPList& args) { else if ( args.size() > 1 ) { switch ( args.size() ) { - case 2: z = GenInst(OP_CAT2_V, n); break; - case 3: z = GenInst(OP_CAT3_V, n); break; - case 4: z = GenInst(OP_CAT4_V, n); break; - case 5: z = GenInst(OP_CAT5_V, n); break; - case 6: z = GenInst(OP_CAT6_V, n); break; - case 7: z = GenInst(OP_CAT7_V, n); break; - case 8: z = GenInst(OP_CAT8_V, n); break; + case 2: z = zam->GenInst(OP_CAT2_V, n); break; + case 3: z = zam->GenInst(OP_CAT3_V, n); break; + case 4: z = zam->GenInst(OP_CAT4_V, n); break; + case 5: z = zam->GenInst(OP_CAT5_V, n); break; + case 6: z = zam->GenInst(OP_CAT6_V, n); break; + case 7: z = zam->GenInst(OP_CAT7_V, n); break; + case 8: z = zam->GenInst(OP_CAT8_V, n); break; - default: z = GenInst(OP_CATN_V, n); break; + default: z = zam->GenInst(OP_CATN_V, n); break; } - z.aux = BuildCatAux(args); + z.aux = BuildCatAux(zam, args); } else if ( a0->GetType()->Tag() != TYPE_STRING ) { if ( a0->Tag() == EXPR_NAME ) { - z = GenInst(OP_CAT1FULL_VV, n, a0->AsNameExpr()); + z = zam->GenInst(OP_CAT1FULL_VV, n, a0->AsNameExpr()); z.t = a0->GetType(); } else { @@ -232,19 +178,19 @@ bool ZAMCompiler::BuiltIn_cat(const NameExpr* n, const ExprPList& args) { } else if ( a0->Tag() == EXPR_CONST ) { - z = GenInst(OP_CAT1_VC, n, a0->AsConstExpr()); + z = zam->GenInst(OP_CAT1_VC, n, a0->AsConstExpr()); z.t = n->GetType(); } else - z = GenInst(OP_CAT1_VV, n, a0->AsNameExpr()); + z = zam->GenInst(OP_CAT1_VV, n, a0->AsNameExpr()); - AddInst(z); + zam->AddInst(z); return true; } -ZInstAux* ZAMCompiler::BuildCatAux(const ExprPList& args) { +ZInstAux* CatZBI::BuildCatAux(ZAMCompiler* zam, const ExprPList& args) const { auto n = args.size(); auto aux = new ZInstAux(n); aux->cat_args = new std::unique_ptr[n]; @@ -257,7 +203,9 @@ ZInstAux* ZAMCompiler::BuildCatAux(const ExprPList& args) { if ( a_i->Tag() == EXPR_CONST ) { auto c = a_i->AsConstExpr()->ValuePtr(); - aux->Add(i, c); // it will be ignored + aux->Add(i, c); // we add it to consume a slot, but it'll be ignored + + // Convert it up front and transform into a fixed string. auto sv = ZAM_val_cat(c); auto s = sv->AsString(); auto b = reinterpret_cast(s->Bytes()); @@ -265,7 +213,7 @@ ZInstAux* ZAMCompiler::BuildCatAux(const ExprPList& args) { } else { - auto slot = FrameSlot(a_i->AsNameExpr()); + auto slot = zam->FrameSlot(a_i->AsNameExpr()); aux->Add(i, slot, t); switch ( t->Tag() ) { @@ -293,215 +241,395 @@ ZInstAux* ZAMCompiler::BuildCatAux(const ExprPList& args) { return aux; } -bool ZAMCompiler::BuiltIn_current_time(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - int nslot = Frame1Slot(n, OP1_WRITE); - - AddInst(ZInstI(OP_CURRENT_TIME_V, nslot)); - - return true; -} - -bool ZAMCompiler::BuiltIn_get_port_etc(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - auto p = args[0]; - - if ( p->Tag() != EXPR_NAME ) +bool SortZBI::Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const { + // The checks the sort() BiF does can all be computed statically. + if ( args.size() > 2 ) return false; - auto pn = p->AsNameExpr(); - int nslot = Frame1Slot(n, OP1_WRITE); + auto v = args[0]->AsNameExpr(); + if ( v->GetType()->Tag() != TYPE_VECTOR ) + return false; - AddInst(ZInstI(OP_GET_PORT_TRANSPORT_PROTO_VV, nslot, FrameSlot(pn))); + const auto& elt_type = v->GetType()->Yield(); - return true; -} + if ( args.size() == 1 ) { + if ( ! IsIntegral(elt_type->Tag()) && elt_type->InternalType() != TYPE_INTERNAL_DOUBLE ) + return false; -bool ZAMCompiler::BuiltIn_network_time(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; + return OptAssignZBI::Build(zam, n, args); } - int nslot = Frame1Slot(n, OP1_WRITE); + // If we get here, then there's a comparison function. + const auto& comp_val = args[1]; + if ( ! IsFunc(comp_val->GetType()->Tag()) ) + return false; - AddInst(ZInstI(OP_NETWORK_TIME_V, nslot)); + if ( comp_val->Tag() != EXPR_NAME ) + return false; - return true; -} + auto comp_func = comp_val->AsNameExpr(); + auto comp_type = comp_func->GetType()->AsFuncType(); -bool ZAMCompiler::BuiltIn_reading_live_traffic(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - int nslot = Frame1Slot(n, OP1_WRITE); - - AddInst(ZInstI(OP_READING_LIVE_TRAFFIC_V, nslot)); - - return true; -} - -bool ZAMCompiler::BuiltIn_reading_traces(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - int nslot = Frame1Slot(n, OP1_WRITE); - - AddInst(ZInstI(OP_READING_TRACES_V, nslot)); - - return true; -} - -bool ZAMCompiler::BuiltIn_strstr(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - auto big = args[0]; - auto little = args[1]; - - auto big_n = big->Tag() == EXPR_NAME ? big->AsNameExpr() : nullptr; - auto little_n = little->Tag() == EXPR_NAME ? little->AsNameExpr() : nullptr; + if ( comp_type->Yield()->Tag() != TYPE_INT || ! comp_type->ParamList()->AllMatch(elt_type, 0) || + comp_type->ParamList()->GetTypes().size() != 2 ) + return false; ZInstI z; - if ( big_n && little_n ) - z = GenInst(OP_STRSTR_VVV, n, big_n, little_n); - else if ( big_n ) - z = GenInst(OP_STRSTR_VVC, n, big_n, little->AsConstExpr()); - else if ( little_n ) - z = GenInst(OP_STRSTR_VCV, n, little_n, big->AsConstExpr()); + if ( n ) + z = ZInstI(OP_SORT_WITH_CMP_VVV, zam->Frame1Slot(n, OP1_WRITE), zam->FrameSlot(v), zam->FrameSlot(comp_func)); else - return false; + z = ZInstI(OP_SORT_WITH_CMP_VV, zam->FrameSlot(v), zam->FrameSlot(comp_func)); - AddInst(z); + zam->AddInst(z); return true; } -bool ZAMCompiler::BuiltIn_sub_bytes(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; +MultiZBI::MultiZBI(std::string name, bool _ret_val_matters, BiFArgsInfo _args_info, int _type_arg) + : ZAMBuiltIn(std::move(name), _ret_val_matters), args_info(std::move(_args_info)), type_arg(_type_arg) {} + +MultiZBI::MultiZBI(std::string name, BiFArgsInfo _args_info, BiFArgsInfo _assign_args_info, int _type_arg) + : MultiZBI(std::move(name), false, _args_info, _type_arg) { + assign_args_info = std::move(_assign_args_info); + have_both = true; +} + +bool MultiZBI::Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const { + auto ai = &args_info; + if ( n && have_both ) { + ai = &assign_args_info; + ASSERT(! ai->empty()); } - auto arg_s = args[0]; - auto arg_start = args[1]; - auto arg_n = args[2]; + auto bif_arg_info = ai->find(ComputeArgsType(args)); + if ( bif_arg_info == ai->end() ) + // Not a Constant/Variable combination this ZBI supports. + return false; - int nslot = Frame1Slot(n, OP1_WRITE); + const auto& bi = bif_arg_info->second; + auto op = bi.op; - int v2 = FrameSlotIfName(arg_s); - int v3 = ConvertToCount(arg_start); - int v4 = ConvertToInt(arg_n); + std::vector consts; + std::vector v; - auto c = arg_s->Tag() == EXPR_CONST ? arg_s->AsConstExpr() : nullptr; + for ( auto i = 0U; i < args.size(); ++i ) { + auto a = args[i]; + if ( a->Tag() == EXPR_NAME ) + v.push_back(zam->FrameSlot(a->AsNameExpr())); + else + consts.push_back(a->AsConstExpr()->ValuePtr()); + } + + auto nslot = n ? zam->Frame1Slot(n, OP1_WRITE) : -1; ZInstI z; - switch ( ConstArgsMask(args, 3) ) { - case 0x0: // all variable - z = ZInstI(OP_SUB_BYTES_VVVV, nslot, v2, v3, v4); - z.op_type = OP_VVVV; - break; - - case 0x1: // last argument a constant - z = ZInstI(OP_SUB_BYTES_VVVi, nslot, v2, v3, v4); - z.op_type = OP_VVVV_I4; - break; - - case 0x2: // 2nd argument a constant; flip! - z = ZInstI(OP_SUB_BYTES_VViV, nslot, v2, v4, v3); - z.op_type = OP_VVVV_I4; - break; - - case 0x3: // both 2nd and third are constants - z = ZInstI(OP_SUB_BYTES_VVii, nslot, v2, v3, v4); - z.op_type = OP_VVVV_I3_I4; - break; - - case 0x4: // first argument a constant - ASSERT(c); - z = ZInstI(OP_SUB_BYTES_VVVC, nslot, v3, v4, c); - z.op_type = OP_VVVC; - break; - - case 0x5: // first and third constant - ASSERT(c); - z = ZInstI(OP_SUB_BYTES_VViC, nslot, v3, v4, c); - z.op_type = OP_VVVC_I3; - break; - - case 0x6: // first and second constant - flip! - ASSERT(c); - z = ZInstI(OP_SUB_BYTES_ViVC, nslot, v4, v3, c); - z.op_type = OP_VVVC_I3; - break; - - case 0x7: // whole shebang - ASSERT(c); - z = ZInstI(OP_SUB_BYTES_ViiC, nslot, v3, v4, c); - z.op_type = OP_VVVC_I2_I3; - break; - - default: reporter->InternalError("bad constant mask"); + if ( args.size() == 2 ) { + if ( consts.empty() ) { + if ( n ) + z = ZInstI(op, nslot, v[0], v[1]); + else + z = ZInstI(op, v[0], v[1]); + } + else { + ASSERT(consts.size() == 1); + if ( n ) + z = ZInstI(op, nslot, v[0]); + else + z = ZInstI(op, v[0]); + } } - AddInst(z); + else if ( args.size() == 3 ) { + switch ( consts.size() ) { + case 0: + if ( n ) + z = ZInstI(op, nslot, v[0], v[1], v[2]); + else + z = ZInstI(op, v[0], v[1], v[2]); + break; + + case 1: + if ( n ) + z = ZInstI(op, nslot, v[0], v[1]); + else + z = ZInstI(op, v[0], v[1]); + break; + + case 2: { + auto c2 = consts[1]; + auto c2_t = c2->GetType()->Tag(); + + ASSERT(c2_t == TYPE_BOOL || c2_t == TYPE_INT || c2_t == TYPE_COUNT); + int slot_val; + if ( c2_t == TYPE_COUNT ) + slot_val = static_cast(c2->AsCount()); + else + slot_val = c2->AsInt(); + + if ( n ) + z = ZInstI(op, nslot, v[0], slot_val); + else + z = ZInstI(op, v[0], slot_val); + break; + } + + default: reporter->InternalError("inconsistency in MultiZBI::Build"); + } + } + + else + reporter->InternalError("inconsistency in MultiZBI::Build"); + + z.op_type = bi.op_type; + + if ( n ) + z.is_managed = ZVal::IsManagedType(n->GetType()); + + if ( ! consts.empty() ) { + z.t = consts[0]->GetType(); + z.c = ZVal(consts[0], z.t); + } + + if ( type_arg >= 0 && ! z.t ) + z.t = args[type_arg]->GetType(); + + zam->AddInst(z); return true; } -bool ZAMCompiler::BuiltIn_to_lower(const NameExpr* n, const ExprPList& args) { - if ( ! n ) { - reporter->Warning("return value from built-in function ignored"); - return true; - } - - int nslot = Frame1Slot(n, OP1_WRITE); - - if ( args[0]->Tag() == EXPR_CONST ) { - auto arg_c = args[0]->AsConstExpr()->Value()->AsStringVal(); - ValPtr arg_lc = {AdoptRef{}, ZAM_to_lower(arg_c)}; - auto arg_lce = make_intrusive(arg_lc); - auto z = ZInstI(OP_ASSIGN_CONST_VC, nslot, arg_lce.get()); - z.is_managed = true; - AddInst(z); - } - - else { - auto arg_s = args[0]->AsNameExpr(); - AddInst(ZInstI(OP_TO_LOWER_VV, nslot, FrameSlot(arg_s))); - } - - return true; -} - -zeek_uint_t ZAMCompiler::ConstArgsMask(const ExprPList& args, int nargs) const { - ASSERT(args.length() == nargs); - +BiFArgsType MultiZBI::ComputeArgsType(const ExprPList& args) const { zeek_uint_t mask = 0; - for ( int i = 0; i < nargs; ++i ) { + for ( auto i = 0U; i < args.size(); ++i ) { mask <<= 1; if ( args[i]->Tag() == EXPR_CONST ) mask |= 1; } - return mask; + return BiFArgsType(mask); +} + +//////////////////////////////////////////////////////////////////////// + +// To create a new built-in, add it to the following collection. We chose +// this style with an aim to making the entries both easy to update & readable. +// The names of the variables don't matter, so we keep them short to aid +// readability. + +SimpleZBI an_ZBI{"Analyzer::__name", OP_ANALYZER_NAME_VC, OP_ANALYZER_NAME_VV}; +SimpleZBI ae_ZBI{"Files::__analyzer_enabled", OP_ANALYZER_ENABLED_VC, OP_ANALYZER_ENABLED_VV}; +SimpleZBI fan_ZBI{"Files::__analyzer_name", OP_FILE_ANALYZER_NAME_VC, OP_FILE_ANALYZER_NAME_VV}; +SimpleZBI fer_ZBI{"Files::__enable_reassembly", OP_FILES_ENABLE_REASSEMBLY_V, 1, false}; +SimpleZBI ct_ZBI{"clear_table", OP_CLEAR_TABLE_V, 1, false}; +SimpleZBI currt_ZBI{"current_time", OP_CURRENT_TIME_V, 0}; +SimpleZBI gptp_ZBI{"get_port_transport_proto", OP_GET_PORT_TRANSPORT_PROTO_VV, 1}; +SimpleZBI ipa_ZBI{"is_protocol_analyzer", OP_IS_PROTOCOL_ANALYZER_VC, OP_IS_PROTOCOL_ANALYZER_VV, true}; +SimpleZBI lc_ZBI{"lookup_connection", OP_LOOKUP_CONN_VV, 1}; +SimpleZBI nt_ZBI{"network_time", OP_NETWORK_TIME_V, 0}; +SimpleZBI sfh_ZBI{"set_file_handle", OP_SET_FILE_HANDLE_V, 1, false}; +SimpleZBI sta_ZBI{"subnet_to_addr", OP_SUBNET_TO_ADDR_VV, 1}; +SimpleZBI ttd_ZBI{"time_to_double", OP_TIME_TO_DOUBLE_VV, 1}; +SimpleZBI tl_ZBI{"to_lower", OP_TO_LOWER_VV, 1}; + +CondZBI ce_ZBI{"connection_exists", OP_CONN_EXISTS_VV, OP_CONN_EXISTS_COND_VV, 1}; +CondZBI iip_ZBI{"is_icmp_port", OP_IS_ICMP_PORT_VV, OP_IS_ICMP_PORT_COND_VV, 1}; +CondZBI itp_ZBI{"is_tcp_port", OP_IS_TCP_PORT_VV, OP_IS_TCP_PORT_COND_VV, 1}; +CondZBI iup_ZBI{"is_udp_port", OP_IS_UDP_PORT_VV, OP_IS_UDP_PORT_COND_VV, 1}; +CondZBI iv4_ZBI{"is_v4_addr", OP_IS_V4_ADDR_VV, OP_IS_V4_ADDR_COND_VV, 1}; +CondZBI iv6_ZBI{"is_v6_addr", OP_IS_V6_ADDR_VV, OP_IS_V6_ADDR_COND_VV, 1}; +CondZBI rlt_ZBI{"reading_live_traffic", OP_READING_LIVE_TRAFFIC_V, OP_READING_LIVE_TRAFFIC_COND_V, 0}; +CondZBI rt_ZBI{"reading_traces", OP_READING_TRACES_V, OP_READING_TRACES_COND_V, 0}; + +// These have a different form to avoid invoking copy constructors. +auto cat_ZBI = CatZBI(); +auto sort_ZBI = SortZBI(); + +// For the following, clang-format makes them hard to follow compared to +// a manual layout. +// +// clang-format off + +OptAssignZBI bfl_ZBI{ "Broker::__flush_logs", + OP_BROKER_FLUSH_LOGS_V, OP_BROKER_FLUSH_LOGS_X, + 0 +}; + +OptAssignZBI rgc_ZBI{ "PacketAnalyzer::GTPV1::remove_gtpv1_connection", + OP_REMOVE_GTPV1_VV, OP_REMOVE_GTPV1_V, + 1 +}; +OptAssignZBI rtc_ZBI{ "PacketAnalyzer::TEREDO::remove_teredo_connection", + OP_REMOVE_TEREDO_VV, OP_REMOVE_TEREDO_V, + 1 +}; + +MultiZBI faa_ZBI{ "Files::__add_analyzer", + {{{VVV}, {OP_FILES_ADD_ANALYZER_VVV, OP_VVV}}, + {{VCV}, {OP_FILES_ADD_ANALYZER_ViV, OP_VVC}}}, + {{{VVV}, {OP_FILES_ADD_ANALYZER_VVVV, OP_VVVV}}, + {{VCV}, {OP_FILES_ADD_ANALYZER_VViV, OP_VVVC}}}, + 1 +}; + +MultiZBI fra_ZBI{ "Files::__remove_analyzer", + {{{VVV}, {OP_FILES_REMOVE_ANALYZER_VVV, OP_VVV}}, + {{VCV}, {OP_FILES_REMOVE_ANALYZER_ViV, OP_VVC}}}, + {{{VVV}, {OP_FILES_REMOVE_ANALYZER_VVVV, OP_VVVV}}, + {{VCV}, {OP_FILES_REMOVE_ANALYZER_VViV, OP_VVVC}}}, + 1 +}; + +MultiZBI fsrb_ZBI{ "Files::__set_reassembly_buffer", + {{{VV}, {OP_FILES_SET_REASSEMBLY_BUFFER_VV, OP_VV}}, + {{VC}, {OP_FILES_SET_REASSEMBLY_BUFFER_VC, OP_VV_I2}}}, + {{{VV}, {OP_FILES_SET_REASSEMBLY_BUFFER_VVV, OP_VVV}}, + {{VC}, {OP_FILES_SET_REASSEMBLY_BUFFER_VVC, OP_VVV_I3}}} +}; + +MultiZBI lw_ZBI{ "Log::__write", + {{{VV}, {OP_LOG_WRITE_VV, OP_VV}}, + {{CV}, {OP_LOG_WRITEC_V, OP_V}}}, + {{{VV}, {OP_LOG_WRITE_VVV, OP_VVV}}, + {{CV}, {OP_LOG_WRITEC_VV, OP_VV}}} +}; + +MultiZBI gccbt_ZBI{ "get_current_conn_bytes_threshold", true, + {{{VV}, {OP_GET_BYTES_THRESH_VVV, OP_VVV}}, + {{VC}, {OP_GET_BYTES_THRESH_VVi, OP_VVC}}} +}; + +MultiZBI sccbt_ZBI{ "set_current_conn_bytes_threshold", + {{{VVV}, {OP_SET_BYTES_THRESH_VVV, OP_VVV}}, + {{VVC}, {OP_SET_BYTES_THRESH_VVi, OP_VVC}}, + {{VCV}, {OP_SET_BYTES_THRESH_ViV, OP_VVC}}, + {{VCC}, {OP_SET_BYTES_THRESH_Vii, OP_VVC_I2}}}, + {{{VVV}, {OP_SET_BYTES_THRESH_VVVV, OP_VVVV}}, + {{VVC}, {OP_SET_BYTES_THRESH_VVVi, OP_VVVC}}, + {{VCV}, {OP_SET_BYTES_THRESH_VViV, OP_VVVC}}, + {{VCC}, {OP_SET_BYTES_THRESH_VVii, OP_VVVC_I3}}} +}; + +MultiZBI sw_ZBI{ "starts_with", true, + {{{VV}, {OP_STARTS_WITH_VVV, OP_VVV}}, + {{VC}, {OP_STARTS_WITH_VVC, OP_VVC}}, + {{CV}, {OP_STARTS_WITH_VCV, OP_VVC}}} +}; + +MultiZBI strcmp_ZBI{ "strcmp", true, + {{{VV}, {OP_STRCMP_VVV, OP_VVV}}, + {{VC}, {OP_STRCMP_VVC, OP_VVC}}, + {{CV}, {OP_STRCMP_VCV, OP_VVC}}} +}; + +MultiZBI strstr_ZBI{ "strstr", true, + {{{VV}, {OP_STRSTR_VVV, OP_VVV}}, + {{VC}, {OP_STRSTR_VVC, OP_VVC}}, + {{CV}, {OP_STRSTR_VCV, OP_VVC}}} +}; + +MultiZBI sb_ZBI{ "sub_bytes", true, + {{{VVV}, {OP_SUB_BYTES_VVVV, OP_VVVV}}, + {{VVC}, {OP_SUB_BYTES_VVVi, OP_VVVC}}, + {{VCV}, {OP_SUB_BYTES_VViV, OP_VVVC}}, + {{VCC}, {OP_SUB_BYTES_VVii, OP_VVVC_I3}}, + {{CVV}, {OP_SUB_BYTES_VVVC, OP_VVVC}}, + {{CVC}, {OP_SUB_BYTES_VViC, OP_VVVC_I3}}, + {{CCV}, {OP_SUB_BYTES_ViVC, OP_VVVC_I3}}} +}; + +// clang-format on + +//////////////////////////////////////////////////////////////////////// + +// Helper function that extracts the underlying Func* from a CallExpr +// node. Returns nil if it's not accessible. +static const Func* get_func(const CallExpr* c) { + auto func_expr = c->Func(); + if ( func_expr->Tag() != EXPR_NAME ) + // An indirect call. + return nullptr; + + auto func_val = func_expr->AsNameExpr()->Id()->GetVal(); + if ( ! func_val ) + // A call to a function that hasn't been defined. + return nullptr; + + return func_val->AsFunc(); +} + +bool IsZAM_BuiltIn(ZAMCompiler* zam, const Expr* e) { + // The expression e is either directly a call (in which case there's + // no return value), or an assignment to a call. + const CallExpr* c; + + if ( e->Tag() == EXPR_CALL ) + c = e->AsCallExpr(); + else + c = e->GetOp2()->AsCallExpr(); + + auto func = get_func(c); + if ( ! func ) + return false; + + std::string fn = func->Name(); + + // It's useful to intercept any lingering calls to the script-level + // Log::write as well as the Log::__write BiF. When inlining there can + // still be script-level calls if the calling function got too big to + // inline them. We could do this for other script-level functions that + // are simply direct wrappers for BiFs, but this is only one that has + // turned up as significant in profiling. + if ( fn == "Log::write" ) + fn = "Log::__write"; + + auto b = builtins.find(fn); + if ( b == builtins.end() ) + return false; + + const auto& bi = b->second; + + const NameExpr* n = nullptr; // name to assign to, if any + if ( e->Tag() != EXPR_CALL ) + n = e->GetOp1()->AsRefExpr()->GetOp1()->AsNameExpr(); + + if ( bi->ReturnValMatters() ) { + if ( ! n ) { + reporter->Warning("return value from built-in function ignored"); + + // The call is a no-op. We could return false here and have it + // execute (for no purpose). We can also return true, which will + // have the effect of just ignoring the statement. + return true; + } + } + else if ( n && ! bi->HaveBothReturnValAndNon() ) + // Because the return value "doesn't matter", we've built the + // corresponding ZIB assuming we don't need a version that does + // the assignment. If we *do* have an assignment, let the usual + // call take place. + return false; + + return bi->Build(zam, n, c->Args()->Exprs()); +} + +bool IsZAM_BuiltInCond(ZAMCompiler* zam, const CallExpr* c, int& branch_v) { + auto func = get_func(c); + if ( ! func ) + return false; + + auto b = builtins.find(func->Name()); + if ( b == builtins.end() ) + return false; + + return b->second->BuildCond(zam, c->Args()->Exprs(), branch_v); +} + +bool IsZAM_BuiltInCond(const CallExpr* c) { + int branch_v; // ignored + return IsZAM_BuiltInCond(nullptr, c, branch_v); } } // namespace zeek::detail diff --git a/src/script_opt/ZAM/BuiltIn.h b/src/script_opt/ZAM/BuiltIn.h index 3482a2cd7f..20ee5a7d18 100644 --- a/src/script_opt/ZAM/BuiltIn.h +++ b/src/script_opt/ZAM/BuiltIn.h @@ -1,29 +1,195 @@ // See the file "COPYING" in the main distribution directory for copyright. -// ZAM compiler method declarations for built-in functions. -// -// This file is only included by ZAM.h, in the context of the ZAM class -// declaration (so these are methods, not standalone functions). We maintain -// it separately so that the conceptual overhead of adding a new built-in -// is lower. +// ZAM classes for built-in functions. We refer to the script-level notion +// as a BiF, and the (potential) ZAM-level replacement as a ZBI = ZAM built-in. -// If the given expression corresponds to a call to a ZAM built-in, -// then compiles the call and returns true. Otherwise, returns false. -bool IsZAM_BuiltIn(const Expr* e); +#pragma once -// Built-ins return true if able to compile the call, false if not. -bool BuiltIn_Analyzer__name(const NameExpr* n, const ExprPList& args); -bool BuiltIn_Broker__flush_logs(const NameExpr* n, const ExprPList& args); -bool BuiltIn_Files__enable_reassembly(const NameExpr* n, const ExprPList& args); -bool BuiltIn_Files__set_reassembly_buffer(const NameExpr* n, const ExprPList& args); -bool BuiltIn_Log__write(const NameExpr* n, const ExprPList& args); -bool BuiltIn_cat(const NameExpr* n, const ExprPList& args); -ZInstAux* BuildCatAux(const ExprPList& args); -bool BuiltIn_current_time(const NameExpr* n, const ExprPList& args); -bool BuiltIn_get_port_etc(const NameExpr* n, const ExprPList& args); -bool BuiltIn_network_time(const NameExpr* n, const ExprPList& args); -bool BuiltIn_reading_live_traffic(const NameExpr* n, const ExprPList& args); -bool BuiltIn_reading_traces(const NameExpr* n, const ExprPList& args); -bool BuiltIn_strstr(const NameExpr* n, const ExprPList& args); -bool BuiltIn_sub_bytes(const NameExpr* n, const ExprPList& args); -bool BuiltIn_to_lower(const NameExpr* n, const ExprPList& args); +#include "zeek/Expr.h" +#include "zeek/script_opt/ZAM/Compile.h" + +namespace zeek::detail { + +// Base class for analyzing function calls to BiFs to see if they can +// be replaced with ZBIs. +class ZAMBuiltIn { +public: + // Constructed using the name of the BiF and a flag that if true means + // that the point of calling the BiF is to do something with its return + // value (in particular, the BiF does not have side-effects). + ZAMBuiltIn(std::string name, bool _ret_val_matters); + virtual ~ZAMBuiltIn() = default; + + bool ReturnValMatters() const { return ret_val_matters; } + bool HaveBothReturnValAndNon() const { return have_both; } + + // Called to compile, if appropriate, a call to the BiF into the + // corresponding specialized instruction. "n", if non-nil, provides + // the assignment target for the return value. "args" are the (reduced) + // arguments in the call, all either names or constants. + // + // Returns true if the replacement was successful, false if it's not + // appropriate. + virtual bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const = 0; + + // Similar to Build(), but done in the context of a conditional. If + // successful, "branch_v" is updated with the slot in the newly added + // instruction where the branch target lives. + // + // If "zam" is nil then does the true/false checking but not the actual + // compilation. In this case, "branch_v" is unchanged. + virtual bool BuildCond(ZAMCompiler* zam, const ExprPList& args, int& branch_v) const { return false; }; + +protected: + bool ret_val_matters = true; + + // If true, then there are two versions of the ZBI, one for returning + // a value and one for when the value is ignored. + bool have_both = false; +}; + +// Class for dealing with simple 0- or 1-argument ZBIs that don't have +// any special considerations for applicability or compiling. These are +// quite common. +class SimpleZBI : public ZAMBuiltIn { +public: + // This constructor is for ZBIs that either take no arguments, or always + // take a single variable as their argument. + SimpleZBI(std::string name, ZOp _op, int _nargs, bool _ret_val_matters = true); + + // A version for supporting a single argument that can be either a + // constant (first operand) or a variable (second operand). + SimpleZBI(std::string name, ZOp _const_op, ZOp _op, bool _ret_val_matters = true); + + bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const override; + +protected: + // Operand used for the 0-argument or 1-argument-that's-a-variable case. + ZOp op; + + // Operand used for the 1-argument-that's-a-constant case. + ZOp const_op = OP_NOP; + + int nargs; +}; + +// A form of simple ZBIs that also support calling the BiF in a conditional. +class CondZBI : public SimpleZBI { +public: + CondZBI(std::string name, ZOp _op, ZOp _cond_op, int _nargs); + + bool BuildCond(ZAMCompiler* zam, const ExprPList& args, int& branch_v) const override; + +protected: + ZOp cond_op; +}; + +// A form of simple ZBIs that support assignment but do not require it. +class OptAssignZBI : public SimpleZBI { +public: + // Second argument is assignment flavor, third is assignment-less flavor. + OptAssignZBI(std::string name, ZOp _op, ZOp _op2, int _nargs); + + bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const override; + +protected: + ZOp op2; +}; + +// The cat() ZBI has an involved build process that can employ a number +// of different ZAM operations. +class CatZBI : public ZAMBuiltIn { +public: + CatZBI() : ZAMBuiltIn("cat", true) {} + + bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const override; + +private: + // cat() ZBIs can have complex auxiliary information capturing the various + // transformations (and fixed strings) to compute for each call. + ZInstAux* BuildCatAux(ZAMCompiler* zam, const ExprPList& args) const; +}; + +// The sort() ZBI needs to refrain from replacing the BiF call if the +// arguments will generate an error (which can be determined at compile-time). +// Doing so enables us to streamline the corresponding ZAM operations. +class SortZBI : public OptAssignZBI { +public: + SortZBI() : OptAssignZBI("sort", OP_SORT_VV, OP_SORT_V, 1) {} + + bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const override; +}; + + +// The last form of ZBI is for more complex BiFs that take multiple arguments, +// which vary in whether some of them can be constants or have to be variables. +// Currently, 2- and 3-argument BiFs are supported. + +// The following encodes the possible patterns of 2- and 3-argument calls +// to BiFs. V = Variable argument, C = Constant argument. The enums have +// values assigned to them reflecting the bit-pattern of the arguments from +// left (most significant) to right (least), with a 1-bit encoding Constant, +// 0-bit for Variable. +enum BiFArgsType { + VV = 0x0, + VC = 0x1, + CV = 0x2, + CC = 0x3, + + VVV = 0x0, + VVC = 0x1, + VCV = 0x2, + VCC = 0x3, + CVV = 0x4, + CVC = 0x5, + CCV = 0x6, + CCC = 0x7, +}; + +// The following captures a ZAM operation and its associated operand type. +struct BiFArgInfo { + ZOp op; + ZAMOpType op_type; +}; + +// A map that associates ZAM operations (and types) with particular +// argument patterns. +using BiFArgsInfo = std::map; + +// Class for supporting ZBIs that take multiple (i.e., > 1) arguments. +class MultiZBI : public ZAMBuiltIn { +public: + // This first constructor is for ZBIs that either always have return + // values or never do, and thus need just one BiFArgsInfo map. + // If "_type_arg" is non-negative, then it specifies which argument + // (numbered left-to-right, starting at 0) should be used to set the + // Zeek type associated with the generated ZAM instruction. + MultiZBI(std::string name, bool _ret_val_matters, BiFArgsInfo _args_info, int _type_arg = -1); + + // Alternative constructor for ZBIs that have optional return values. + // The first map is for the non-assignment case, the second for the + // assignment case. + MultiZBI(std::string name, BiFArgsInfo _args_info, BiFArgsInfo _assign_args_info, int _type_arg = -1); + + bool Build(ZAMCompiler* zam, const NameExpr* n, const ExprPList& args) const override; + +private: + // Returns an enum describing the pattern of Constants/Variables in the + // given argument list. + BiFArgsType ComputeArgsType(const ExprPList& args) const; + + BiFArgsInfo args_info; + BiFArgsInfo assign_args_info; + int type_arg; +}; + +// If the given expression corresponds to a call to a ZAM built-in, then +// compiles the call and returns true. Otherwise, returns false. +extern bool IsZAM_BuiltIn(ZAMCompiler* zam, const Expr* e); + +// If the given expression corresponds to a call to a ZAM built-in that has +// a conditional version, compiles the conditional and returns true, and +// updates branch_v to reflect the branch slot. Otherwise, returns false. +extern bool IsZAM_BuiltInCond(ZAMCompiler* zam, const CallExpr* c, int& branch_v); + +} // namespace zeek::detail diff --git a/src/script_opt/ZAM/Compile.h b/src/script_opt/ZAM/Compile.h index 47c34e4a7f..2ee365e64c 100644 --- a/src/script_opt/ZAM/Compile.h +++ b/src/script_opt/ZAM/Compile.h @@ -85,6 +85,13 @@ public: void Dump(); private: + friend class SimpleZBI; + friend class CondZBI; + friend class OptAssignZBI; + friend class SortZBI; + friend class CatZBI; + friend class MultiZBI; + void Init(); void InitGlobals(); void InitArgs(); @@ -182,6 +189,7 @@ private: const ZAMStmt CompileAddToExpr(const AddToExpr* e); const ZAMStmt CompileRemoveFromExpr(const RemoveFromExpr* e); const ZAMStmt CompileAssignExpr(const AssignExpr* e); + const ZAMStmt CompileZAMBuiltin(const NameExpr* lhs, const ScriptOptBuiltinExpr* zbi); const ZAMStmt CompileAssignToIndex(const NameExpr* lhs, const IndexExpr* rhs); const ZAMStmt CompileFieldLHSAssignExpr(const FieldLHSAssignExpr* e); const ZAMStmt CompileScheduleExpr(const ScheduleExpr* e); @@ -246,17 +254,8 @@ private: const ZAMStmt Is(const NameExpr* n, const Expr* e); -#include "zeek/script_opt/ZAM/BuiltIn.h" #include "zeek/script_opt/ZAM/Inst-Gen.h" - // A bit weird, but handy for switch statements used in built-in - // operations: returns a bit mask of which of the arguments in the - // given list correspond to constants, with the high-ordered bit - // being the first argument (argument "0" in the list) and the - // low-ordered bit being the last. Second parameter is the number - // of arguments that should be present. - zeek_uint_t ConstArgsMask(const ExprPList& args, int nargs) const; - int ConvertToInt(const Expr* e) { if ( e->Tag() == EXPR_NAME ) return FrameSlot(e->AsNameExpr()->Id()); diff --git a/src/script_opt/ZAM/Expr.cc b/src/script_opt/ZAM/Expr.cc index 2b0e161e44..facc6952ff 100644 --- a/src/script_opt/ZAM/Expr.cc +++ b/src/script_opt/ZAM/Expr.cc @@ -971,7 +971,7 @@ const ZAMStmt ZAMCompiler::AssignToCall(const ExprStmt* e) { } bool ZAMCompiler::CheckForBuiltIn(const ExprPtr& e, CallExprPtr c) { - if ( ! IsZAM_BuiltIn(e.get()) ) + if ( ! IsZAM_BuiltIn(this, e.get()) ) return false; auto ret = LastInst(); diff --git a/src/script_opt/ZAM/Ops.in b/src/script_opt/ZAM/Ops.in index dae3885904..0c6c5d1847 100644 --- a/src/script_opt/ZAM/Ops.in +++ b/src/script_opt/ZAM/Ops.in @@ -2360,21 +2360,76 @@ macro EvalSubBytes(arg1, arg2, arg3) frame[z.v1].string_val = sv; } +internal-op Remove-Teredo +op1-read +type V +eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo"); + if ( teredo ) + { + zeek::detail::ConnKey conn_key(frame[z.v1].record_val); + static_cast(teredo.get())->RemoveConnection(conn_key); + } + +internal-op Remove-Teredo +side-effects OP_REMOVE_TEREDO_V OP_V +type VV +eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo"); + if ( teredo ) + { + zeek::detail::ConnKey conn_key(frame[z.v2].record_val); + static_cast(teredo.get())->RemoveConnection(conn_key); + } + frame[z.v1].int_val = 1; + +internal-op Remove-GTPv1 +op1-read +type V +eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1"); + if ( gtpv1 ) + { + zeek::detail::ConnKey conn_key(frame[z.v1].record_val); + static_cast(gtpv1.get())->RemoveConnection(conn_key); + } + +internal-op Remove-GTPv1 +side-effects OP_REMOVE_GTPV1_V OP_V +type VV +eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1"); + if ( gtpv1 ) + { + zeek::detail::ConnKey conn_key(frame[z.v2].record_val); + static_cast(gtpv1.get())->RemoveConnection(conn_key); + } + frame[z.v1].int_val = 1; + +internal-op Set-File-Handle +op1-read +type V +eval auto handle = frame[z.v1].string_val; + auto bytes = reinterpret_cast(handle->Bytes()); + auto h = std::string(bytes, handle->Len()); + zeek::file_mgr->SetHandle(h); + +internal-op Subnet-To-Addr +type VV +eval Unref(frame[z.v1].addr_val); + frame[z.v1] = ZVal(make_intrusive(frame[z.v2].subnet_val->Prefix())); + internal-op Sub-Bytes type VVVV eval EvalSubBytes(frame[z.v2], frame[z.v3].uint_val, frame[z.v4].int_val) internal-op Sub-Bytes type VVVi -eval EvalSubBytes(frame[z.v2], frame[z.v3].uint_val, z.v4) +eval EvalSubBytes(frame[z.v2], frame[z.v3].uint_val, z.c.int_val) internal-op Sub-Bytes type VViV -eval EvalSubBytes(frame[z.v2], zeek_uint_t(z.v4), frame[z.v3].int_val) +eval EvalSubBytes(frame[z.v2], z.c.uint_val, frame[z.v3].int_val) internal-op Sub-Bytes type VVii -eval EvalSubBytes(frame[z.v2], zeek_uint_t(z.v3), z.v4) +eval EvalSubBytes(frame[z.v2], z.c.uint_val, z.v3) internal-op Sub-Bytes type VVVC @@ -2388,9 +2443,9 @@ internal-op Sub-Bytes type ViVC eval EvalSubBytes(z.c, zeek_uint_t(z.v3), frame[z.v2].uint_val) -internal-op Sub-Bytes -type ViiC -eval EvalSubBytes(z.c, zeek_uint_t(z.v2), z.v3) +internal-op Time-To-Double +type VV +eval frame[z.v1] = frame[z.v2]; internal-op To-Lower @@ -2412,8 +2467,8 @@ eval auto sv = ZAM_to_lower(frame[z.v2].string_val); # actually used. macro LogWritePre(id_val, columns_slot) - auto id = id_val; - auto columns = frame[z.columns_slot].ToVal(z.t); + auto id = id_val.ToVal(ZAM::log_ID_enum_type); + auto columns = frame[z.columns_slot].record_val; macro LogWriteResPost() bool result = log_mgr->Write(id->AsEnumVal(), columns->AsRecordVal()); @@ -2425,13 +2480,13 @@ macro LogWriteNoResPost() internal-op Log-Write side-effects OP_LOG_WRITE_VV OP_VV type VVV -eval LogWritePre(frame[z.v2].ToVal(ZAM::log_ID_enum_type), v3) +eval LogWritePre(frame[z.v2], v3) LogWriteResPost() internal-op Log-WriteC side-effects OP_LOG_WRITEC_V OP_V type VV -eval LogWritePre(z.aux->elems[0].Constant(), v2) +eval LogWritePre(z.c, v2) LogWriteResPost() # Versions that discard the return value. @@ -2439,14 +2494,14 @@ internal-op Log-Write side-effects op1-read type VV -eval LogWritePre(frame[z.v1].ToVal(ZAM::log_ID_enum_type), v2) +eval LogWritePre(frame[z.v1], v2) LogWriteNoResPost() internal-op Log-WriteC side-effects op1-read type V -eval LogWritePre(z.aux->elems[0].Constant(), v1) +eval LogWritePre(z.c, v1) LogWriteNoResPost() internal-op Broker-Flush-Logs @@ -2471,6 +2526,116 @@ eval auto mask = frame[z.v2].uint_val & PORT_SPACE_MASK; v = 3; frame[z.v1].uint_val = v; +internal-op Conn-Exists +type VV +eval frame[z.v1].int_val = session_mgr->FindConnection(frame[z.v2].record_val) != nullptr; + +internal-op Conn-Exists-Cond +op1-read +type VV +eval if ( ! session_mgr->FindConnection(frame[z.v1].record_val) ) + BRANCH(v2) + +internal-op Not-Conn-Exists-Cond +op1-read +type VV +eval if ( session_mgr->FindConnection(frame[z.v1].record_val) ) + BRANCH(v2) + +internal-op Lookup-Conn +type VV +eval auto cid = frame[z.v2].record_val; + Connection* conn = session_mgr->FindConnection(cid); + ValPtr res; + if ( conn ) + res = conn->GetVal(); + else + { + ZAM_run_time_error(z.loc, "connection ID not a known connection", cid); + res = build_dummy_conn_record(); + } + AssignV1(ZVal(res, res->GetType())); + +internal-op Is-ICMP-Port +type VV +eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == ICMP_PORT_MASK; + +internal-op Is-ICMP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != ICMP_PORT_MASK ) + BRANCH(v2) + +internal-op Not-Is-ICMP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == ICMP_PORT_MASK ) + BRANCH(v2) + +internal-op Is-TCP-Port +type VV +eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == TCP_PORT_MASK; + +internal-op Is-TCP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != TCP_PORT_MASK ) + BRANCH(v2) + +internal-op Not-Is-TCP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == TCP_PORT_MASK ) + BRANCH(v2) + +internal-op Is-UDP-Port +type VV +eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == UDP_PORT_MASK; + +internal-op Is-UDP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != UDP_PORT_MASK ) + BRANCH(v2) + +internal-op Not-Is-UDP-Port-Cond +op1-read +type VV +eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == UDP_PORT_MASK ) + BRANCH(v2) + +internal-op Is-V4-Addr +type VV +eval frame[z.v1].int_val = frame[z.v2].addr_val->AsAddr().GetFamily() == IPv4; + +internal-op Is-V4-Addr-Cond +op1-read +type VV +eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() != IPv4 ) + BRANCH(v2) + +internal-op Not-Is-V4-Addr-Cond +op1-read +type VV +eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() == IPv4 ) + BRANCH(v2) + +internal-op Is-V6-Addr +type VV +eval frame[z.v1].int_val = frame[z.v2].addr_val->AsAddr().GetFamily() == IPv6; + +internal-op Is-V6-Addr-Cond +op1-read +type VV +eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() != IPv6 ) + BRANCH(v2) + +internal-op Not-Is-V6-Addr-Cond +op1-read +type VV +eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() == IPv6 ) + BRANCH(v2) + internal-op Network-Time type V eval frame[z.v1].double_val = run_state::network_time; @@ -2483,19 +2648,120 @@ internal-op Reading-Live-Traffic type V eval frame[z.v1].int_val = run_state::reading_live; +internal-op Reading-Live-Traffic-Cond +op1-read +type V +eval if ( ! run_state::reading_live ) + BRANCH(v1) + +internal-op Not-Reading-Live-Traffic-Cond +op1-read +type V +eval if ( run_state::reading_live ) + BRANCH(v1) + internal-op Reading-Traces type V eval frame[z.v1].int_val = run_state::reading_traces; -internal-op StrStr +internal-op Reading-Traces-Cond +op1-read +type V +eval if ( ! run_state::reading_traces ) + BRANCH(v1) + +internal-op Not-Reading-Traces-Cond +op1-read +type V +eval if ( run_state::reading_traces ) + BRANCH(v1) + +internal-op Sort +op1-read +type V +eval if ( frame[z.v1].vector_val->Size() > 1 ) + frame[z.v1].vector_val->Sort(); + +internal-op Sort +type VV +eval auto vv = frame[z.v2].vector_val; + if ( vv->Size() > 1 ) + vv->Sort(); + zeek::Ref(vv); + Unref(frame[z.v1].vector_val); + frame[z.v1].vector_val = vv; + +internal-op Sort-With-Cmp +op1-read +type VV +eval if ( frame[z.v1].vector_val->Size() > 1 ) + frame[z.v1].vector_val->Sort(frame[z.v2].func_val); + +internal-op Sort-With-Cmp type VVV -eval EvalStrStr(frame[z.v2], frame[z.v3]) +eval auto vv = frame[z.v2].vector_val; + if ( vv->Size() > 1 ) + vv->Sort(frame[z.v3].func_val); + zeek::Ref(vv); + Unref(frame[z.v1].vector_val); + frame[z.v1].vector_val = vv; + +macro EvalStartsWith(str_val, sub_val) + auto str = str_val.string_val; + auto sub = sub_val.string_val; + auto str_n = str->Len(); + auto sub_n = sub->Len(); + if ( str_n < sub_n ) + frame[z.v1].int_val = 0; + else + { + auto str_b = str->Bytes(); + auto sub_b = sub->Bytes(); + int i; + for ( i = 0; i < sub_n; ++i ) + if ( str_b[i] != sub_b[i] ) + break; + frame[z.v1].int_val = i == sub_n; + } + +internal-op Starts-With +type VVV +eval EvalStartsWith(frame[z.v2], frame[z.v3]) + +internal-op Starts-With +type VCV +eval EvalStartsWith(z.c, frame[z.v2]) + +internal-op Starts-With +type VVC +eval EvalStartsWith(frame[z.v2], z.c) + +macro EvalStrCmp(s1_src, s2_src) + auto s1 = s1_src.string_val; + auto s2 = s2_src.string_val; + frame[z.v1].int_val = Bstr_cmp(s1->AsString(), s2->AsString()); + +internal-op StrCmp +type VVV +eval EvalStrCmp(frame[z.v2], frame[z.v3]) + +internal-op StrCmp +type VCV +eval EvalStrCmp(z.c, frame[z.v2]) + +internal-op StrCmp +type VVC +eval EvalStrCmp(frame[z.v2], z.c) macro EvalStrStr(big_value, little_value) auto big = big_value.string_val; auto little = little_value.string_val; frame[z.v1].int_val = 1 + big->AsString()->FindSubstring(little->AsString()); +internal-op StrStr +type VVV +eval EvalStrStr(frame[z.v2], frame[z.v3]) + internal-op StrStr type VCV eval EvalStrStr(z.c, frame[z.v2]) @@ -2674,9 +2940,8 @@ eval CatNPre() ca[7]->RenderInto(frame, aux->elems[7].Slot(), res_p); CatNPost() -internal-op Analyzer--Name -type VV -eval auto atype = frame[z.v2].ToVal(z.t); +macro AnalyzerName(tag) + auto atype = tag.ToVal(z.t); auto val = atype->AsEnumVal(); Unref(frame[z.v1].string_val); plugin::Component* component = zeek::analyzer_mgr->Lookup(val); @@ -2689,20 +2954,291 @@ eval auto atype = frame[z.v2].ToVal(z.t); else frame[z.v1].string_val = new StringVal(""); -internal-op Files--Enable-Reassembly +internal-op Analyzer-Name +type VV +eval AnalyzerName(frame[z.v2]) + +internal-op Analyzer-Name +type VC +eval AnalyzerName(z.c) + +macro FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, METHOD) + auto file_id = frame[z.file_id_slot].string_val; + using zeek::BifType::Record::Files::AnalyzerArgs; + auto rv = frame[z.args_slot].record_val->CoerceTo(AnalyzerArgs); + bool result = zeek::file_mgr->METHOD( + file_id->CheckString(), + zeek::file_mgr->GetComponentTag(tag.ToVal(z.t).get()), + std::move(rv)); + +macro FilesAddAnalyzer(file_id_slot, tag, args_slot) + FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, AddAnalyzer) + +internal-op Files-Add-Analyzer +op1-read +type VVV +eval FilesAddAnalyzer(v1, frame[z.v2], v3) + +internal-op Files-Add-Analyzer +op1-read +type ViV +eval FilesAddAnalyzer(v1, z.c, v2) + +internal-op Files-Add-Analyzer +type VVVV +side-effects OP_FILES_ADD_ANALYZER_VVV OP_VVV +eval FilesAddAnalyzer(v2, frame[z.v3], v4) + frame[z.v1].int_val = result; + +internal-op Files-Add-Analyzer +type VViV +side-effects OP_FILES_ADD_ANALYZER_ViV OP_VVC +eval FilesAddAnalyzer(v2, z.c, v3) + frame[z.v1].int_val = result; + +macro FilesRemoveAnalyzer(file_id_slot, tag, args_slot) + FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, RemoveAnalyzer) + +internal-op Files-Remove-Analyzer +op1-read +type VVV +eval FilesRemoveAnalyzer(v1, frame[z.v2], v3) + +internal-op Files-Remove-Analyzer +op1-read +type ViV +eval FilesRemoveAnalyzer(v1, z.c, v2) + +internal-op Files-Remove-Analyzer +type VVVV +side-effects OP_FILES_REMOVE_ANALYZER_VVV OP_VVV +eval FilesRemoveAnalyzer(v2, frame[z.v3], v4) + frame[z.v1].int_val = result; + +internal-op Files-Remove-Analyzer +type VViV +side-effects OP_FILES_REMOVE_ANALYZER_ViV OP_VVC +eval FilesRemoveAnalyzer(v2, z.c, v3) + frame[z.v1].int_val = result; + +macro AnalyzerEnabled(tag) + auto atype = tag.ToVal(z.t); + auto c = zeek::file_mgr->Lookup(atype->AsEnumVal()); + frame[z.v1].int_val = c && c->Enabled(); + +internal-op Analyzer-Enabled +type VV +eval AnalyzerEnabled(frame[z.v2]) + +internal-op Analyzer-Enabled +type VC +eval AnalyzerEnabled(z.c) + +macro FileAnalyzerName(tag) + auto atype = tag.ToVal(z.t); + Unref(frame[z.v1].string_val); + frame[z.v1] = ZVal(file_mgr->GetComponentNameVal({NewRef{}, atype->AsEnumVal()})); + +internal-op File-Analyzer-Name +type VV +eval FileAnalyzerName(frame[z.v2]) + +internal-op File-Analyzer-Name +type VC +eval FileAnalyzerName(z.c) + +macro IsProtocolAnalyzer(tag) + auto atype = tag.ToVal(z.t); + frame[z.v1].int_val = analyzer_mgr->Lookup(atype->AsEnumVal()) != nullptr; + +internal-op Is-Protocol-Analyzer +type VV +eval IsProtocolAnalyzer(frame[z.v2]) + +internal-op Is-Protocol-Analyzer +type VC +eval IsProtocolAnalyzer(z.c) + +internal-op Clear-Table +op1-read +type V +eval frame[z.v1].table_val->RemoveAll(); + +internal-op Files-Enable-Reassembly op1-read type V eval auto f = frame[z.v1].string_val->CheckString(); file_mgr->EnableReassembly(f); -internal-op Files--Set-Reassembly-Buffer +internal-op Files-Set-Reassembly-Buffer op1-read type VV eval auto f = frame[z.v1].string_val->CheckString(); file_mgr->SetReassemblyBuffer(f, frame[z.v2].uint_val); -internal-op Files--Set-Reassembly-Buffer +internal-op Files-Set-Reassembly-Buffer +type VVV +side-effects OP_FILES_SET_REASSEMBLY_BUFFER_VV OP_VV +eval auto f = frame[z.v2].string_val->CheckString(); + frame[z.v1].int_val = file_mgr->SetReassemblyBuffer(f, frame[z.v3].uint_val); + +internal-op Files-Set-Reassembly-Buffer op1-read type VC eval auto f = frame[z.v1].string_val->CheckString(); file_mgr->SetReassemblyBuffer(f, zeek_uint_t(z.v2)); + +internal-op Files-Set-Reassembly-Buffer +type VVC +side-effects OP_FILES_SET_REASSEMBLY_BUFFER_VC OP_VC +eval auto f = frame[z.v2].string_val->CheckString(); + frame[z.v1].int_val = file_mgr->SetReassemblyBuffer(f, zeek_uint_t(z.v2)); + +macro GetBytesThresh(cid, is_orig) + zeek::analyzer::Analyzer* a = analyzer::conn_size::GetConnsizeAnalyzer(cid); + auto res = 0U; + if ( a ) + res = static_cast(a)->GetByteAndPacketThreshold(true, is_orig); + frame[z.v1].uint_val = res; + +internal-op Get-Bytes-Thresh +type VVV +eval GetBytesThresh(frame[z.v2].record_val, frame[z.v3].int_val) + +internal-op Get-Bytes-Thresh +type VVi +eval GetBytesThresh(frame[z.v2].record_val, z.c.uint_val) + +macro SetBytesThresh(cid, threshold, is_orig) + bool res = false; + zeek::analyzer::Analyzer* a = analyzer::conn_size::GetConnsizeAnalyzer(cid); + if ( a ) + { + static_cast(a)->SetByteAndPacketThreshold(threshold, true, is_orig); + res = true; + } + +internal-op Set-Bytes-Thresh +op1-read +type VVV +eval SetBytesThresh(frame[z.v1].record_val, frame[z.v2].uint_val, frame[z.v3].int_val) + +internal-op Set-Bytes-Thresh +op1-read +type VVi +eval SetBytesThresh(frame[z.v1].record_val, frame[z.v2].uint_val, z.c.int_val) + +internal-op Set-Bytes-Thresh +op1-read +type ViV +eval SetBytesThresh(frame[z.v1].record_val, z.c.uint_val, frame[z.v2].int_val) + +internal-op Set-Bytes-Thresh +op1-read +type Vii +eval SetBytesThresh(frame[z.v1].record_val, z.c.uint_val, z.v2) + +internal-op Set-Bytes-Thresh +type VVVV +side-effects OP_SET_BYTES_THRESH_VVV OP_VVV +eval SetBytesThresh(frame[z.v2].record_val, frame[z.v3].uint_val, frame[z.v4].int_val) + frame[z.v1].int_val = res; + +internal-op Set-Bytes-Thresh +type VVVi +side-effects OP_SET_BYTES_THRESH_VVi OP_VVV_I3 +eval SetBytesThresh(frame[z.v2].record_val, frame[z.v3].uint_val, z.c.int_val) + frame[z.v1].int_val = res; + +internal-op Set-Bytes-Thresh +type VViV +side-effects OP_SET_BYTES_THRESH_ViV OP_VVV_I3 +eval SetBytesThresh(frame[z.v2].record_val, z.c.uint_val, frame[z.v3].int_val) + frame[z.v1].int_val = res; + +internal-op Set-Bytes-Thresh +type VVii +side-effects OP_SET_BYTES_THRESH_Vii OP_VVC_I2 +eval SetBytesThresh(frame[z.v2].record_val, z.c.uint_val, zeek_uint_t(z.v3)) + frame[z.v1].int_val = res; + +######################################## +# Instructions for known script functions +######################################## + +internal-op Func-Id-String +type VV +eval auto id_rec = frame[z.v2].record_val; + auto orig_h = id_rec->RawField(0).addr_val->AsAddr().AsString(); + auto resp_h = id_rec->RawField(2).addr_val->AsAddr().AsString(); + auto orig_p = static_cast(id_rec->RawField(1).uint_val) & ~PORT_SPACE_MASK; + auto resp_p = static_cast(id_rec->RawField(3).uint_val) & ~PORT_SPACE_MASK; + /* Maximum address size is for IPv6 with no compression. Each + * 8 16-bit hex elements plus 7 colons between them plus the two []'s + * = 8*4 + 7 + 2 = 41 characters. + * + * Maximum port size is 5. + * + * Two of these = 2*41 + 2*5 = 92. + * Other delimiters: two ':', one ' < ' for 5 more. + * + * TOTAL: 97 characters. + * + * We use considerably more for safety. + */ + char buf[128]; + snprintf(buf, sizeof buf, "%s:%u > %s:%u", orig_h.c_str(), orig_p, resp_h.c_str(), resp_p); + Unref(frame[z.v1].string_val); + frame[z.v1].string_val = new StringVal(buf); + +######################################## +# Instructions for script-level idioms +######################################## + +internal-op MinU +type VVC +eval frame[z.v1].uint_val = std::min(frame[z.v2].uint_val, z.c.uint_val); + +internal-op MinI +type VVC +eval frame[z.v1].int_val = std::min(frame[z.v2].int_val, z.c.int_val); + +internal-op MinD +type VVC +eval frame[z.v1].double_val = std::min(frame[z.v2].double_val, z.c.double_val); + +internal-op MinU +type VVV +eval frame[z.v1].uint_val = std::min(frame[z.v2].uint_val, frame[z.v3].uint_val); + +internal-op MinI +type VVV +eval frame[z.v1].int_val = std::min(frame[z.v2].int_val, frame[z.v3].int_val); + +internal-op MinD +type VVV +eval frame[z.v1].double_val = std::min(frame[z.v2].double_val, frame[z.v3].double_val); + +internal-op MaxU +type VVC +eval frame[z.v1].uint_val = std::max(frame[z.v2].uint_val, z.c.uint_val); + +internal-op MaxI +type VVC +eval frame[z.v1].int_val = std::max(frame[z.v2].int_val, z.c.int_val); + +internal-op MaxD +type VVC +eval frame[z.v1].double_val = std::max(frame[z.v2].double_val, z.c.double_val); + +internal-op MaxU +type VVV +eval frame[z.v1].uint_val = std::max(frame[z.v2].uint_val, frame[z.v3].uint_val); + +internal-op MaxI +type VVV +eval frame[z.v1].int_val = std::max(frame[z.v2].int_val, frame[z.v3].int_val); + +internal-op MaxD +type VVV +eval frame[z.v1].double_val = std::max(frame[z.v2].double_val, frame[z.v3].double_val); diff --git a/src/script_opt/ZAM/Stmt.cc b/src/script_opt/ZAM/Stmt.cc index 4ee998d87d..425062157d 100644 --- a/src/script_opt/ZAM/Stmt.cc +++ b/src/script_opt/ZAM/Stmt.cc @@ -5,6 +5,7 @@ #include "zeek/IPAddr.h" #include "zeek/Reporter.h" #include "zeek/ZeekString.h" +#include "zeek/script_opt/ZAM/BuiltIn.h" #include "zeek/script_opt/ZAM/Compile.h" namespace zeek::detail { @@ -179,6 +180,36 @@ const ZAMStmt ZAMCompiler::IfElse(const Expr* e, const Stmt* s1, const Stmt* s2) case OP_HAS_FIELD_COND_VVV: z->op = OP_NOT_HAS_FIELD_COND_VVV; break; case OP_NOT_HAS_FIELD_COND_VVV: z->op = OP_HAS_FIELD_COND_VVV; break; + case OP_CONN_EXISTS_COND_VV: z->op = OP_NOT_CONN_EXISTS_COND_VV; break; + case OP_NOT_CONN_EXISTS_COND_VV: z->op = OP_CONN_EXISTS_COND_VV; break; + + case OP_IS_ICMP_PORT_COND_VV: z->op = OP_NOT_IS_ICMP_PORT_COND_VV; break; + case OP_NOT_IS_ICMP_PORT_COND_VV: z->op = OP_IS_ICMP_PORT_COND_VV; break; + + case OP_IS_TCP_PORT_COND_VV: z->op = OP_NOT_IS_TCP_PORT_COND_VV; break; + case OP_NOT_IS_TCP_PORT_COND_VV: z->op = OP_IS_TCP_PORT_COND_VV; break; + + case OP_IS_UDP_PORT_COND_VV: z->op = OP_NOT_IS_UDP_PORT_COND_VV; break; + case OP_NOT_IS_UDP_PORT_COND_VV: z->op = OP_IS_UDP_PORT_COND_VV; break; + + case OP_IS_V4_ADDR_COND_VV: z->op = OP_NOT_IS_V4_ADDR_COND_VV; break; + case OP_NOT_IS_V4_ADDR_COND_VV: z->op = OP_IS_V4_ADDR_COND_VV; break; + + case OP_IS_V6_ADDR_COND_VV: z->op = OP_NOT_IS_V6_ADDR_COND_VV; break; + case OP_NOT_IS_V6_ADDR_COND_VV: z->op = OP_IS_V6_ADDR_COND_VV; break; + + case OP_READING_LIVE_TRAFFIC_COND_V: z->op = OP_NOT_READING_LIVE_TRAFFIC_COND_V; break; + case OP_NOT_READING_LIVE_TRAFFIC_COND_V: z->op = OP_READING_LIVE_TRAFFIC_COND_V; break; + + case OP_READING_TRACES_COND_V: z->op = OP_NOT_READING_TRACES_COND_V; break; + case OP_NOT_READING_TRACES_COND_V: z->op = OP_READING_TRACES_COND_V; break; + + case OP_TABLE_HAS_ELEMENTS_COND_VV: z->op = OP_NOT_TABLE_HAS_ELEMENTS_COND_VV; break; + case OP_NOT_TABLE_HAS_ELEMENTS_COND_VV: z->op = OP_TABLE_HAS_ELEMENTS_COND_VV; break; + + case OP_VECTOR_HAS_ELEMENTS_COND_VV: z->op = OP_NOT_VECTOR_HAS_ELEMENTS_COND_VV; break; + case OP_NOT_VECTOR_HAS_ELEMENTS_COND_VV: z->op = OP_VECTOR_HAS_ELEMENTS_COND_VV; break; + case OP_VAL_IS_IN_TABLE_COND_VVV: z->op = OP_VAL_IS_NOT_IN_TABLE_COND_VVV; break; case OP_VAL_IS_NOT_IN_TABLE_COND_VVV: z->op = OP_VAL_IS_IN_TABLE_COND_VVV; break; @@ -205,10 +236,6 @@ const ZAMStmt ZAMCompiler::GenCond(const Expr* e, int& branch_v) { auto op1 = e->GetOp1(); auto op2 = e->GetOp2(); - NameExpr* n1 = nullptr; - NameExpr* n2 = nullptr; - ConstExpr* c = nullptr; - if ( e->Tag() == EXPR_HAS_FIELD ) { auto hf = e->AsHasFieldExpr(); auto z = GenInst(OP_HAS_FIELD_COND_VVV, op1->AsNameExpr(), hf->Field()); @@ -218,7 +245,6 @@ const ZAMStmt ZAMCompiler::GenCond(const Expr* e, int& branch_v) { } if ( e->Tag() == EXPR_IN ) { - auto op1 = e->GetOp1(); auto op2 = e->GetOp2()->AsNameExpr(); // First, deal with the easy cases: it's a single index. @@ -293,6 +319,31 @@ const ZAMStmt ZAMCompiler::GenCond(const Expr* e, int& branch_v) { return AddInst(z); } + if ( e->Tag() == EXPR_CALL ) { + auto c = static_cast(e); + if ( IsZAM_BuiltInCond(this, c, branch_v) ) + return LastInst(); + } + + if ( e->Tag() == EXPR_SCRIPT_OPT_BUILTIN ) { + auto bi = static_cast(e); + ASSERT(bi->Tag() == ScriptOptBuiltinExpr::HAS_ELEMENTS); + auto aggr = bi->GetOp1()->AsNameExpr(); + + ZOp op; + if ( aggr->GetType()->Tag() == TYPE_TABLE ) + op = OP_TABLE_HAS_ELEMENTS_COND_VV; + else + op = OP_VECTOR_HAS_ELEMENTS_COND_VV; + + branch_v = 2; + return AddInst(GenInst(op, aggr, +0)); + } + + NameExpr* n1 = nullptr; + NameExpr* n2 = nullptr; + ConstExpr* c = nullptr; + if ( op1->Tag() == EXPR_NAME ) { n1 = op1->AsNameExpr(); diff --git a/src/script_opt/ZAM/ZBody.cc b/src/script_opt/ZAM/ZBody.cc index 3646829a5f..0eb283284a 100644 --- a/src/script_opt/ZAM/ZBody.cc +++ b/src/script_opt/ZAM/ZBody.cc @@ -12,6 +12,7 @@ #include "zeek/Trigger.h" #include "zeek/script_opt/ScriptOpt.h" #include "zeek/script_opt/ZAM/Compile.h" +#include "zeek/session/Manager.h" // Needed for managing the corresponding values. #include "zeek/File.h" @@ -20,10 +21,16 @@ // Just needed for BiFs. #include "zeek/analyzer/Manager.h" +#include "zeek/analyzer/protocol/conn-size/ConnSize.h" #include "zeek/broker/Manager.h" #include "zeek/file_analysis/Manager.h" +#include "zeek/file_analysis/file_analysis.bif.h" #include "zeek/logging/Manager.h" #include "zeek/packet_analysis/Manager.h" +#include "zeek/packet_analysis/protocol/gtpv1/GTPv1.h" +#include "zeek/packet_analysis/protocol/teredo/Teredo.h" + +#include "zeek.bif.func_h" // For reading_live and reading_traces #include "zeek/RunState.h"