mirror of
https://github.com/zeek/zeek.git
synced 2025-10-06 08:38:20 +00:00
Avoiding infinite loops when an error message handlers triggers errors
itself. If an error is triggered inside one of the reporter_* handlers, the message about that will now fall back to stderr.
This commit is contained in:
parent
66e2c3b623
commit
fb6a8cec19
16 changed files with 94 additions and 11 deletions
|
@ -19,7 +19,7 @@ const char* attr_name(attr_tag t)
|
|||
"&persistent", "&synchronized", "&postprocessor",
|
||||
"&encrypt", "&match", "&disable_print_hook",
|
||||
"&raw_output", "&mergeable", "&priority",
|
||||
"&group", "&log", "(&tracked)",
|
||||
"&group", "&log", "&error_handler", "(&tracked)",
|
||||
};
|
||||
|
||||
return attr_names[int(t)];
|
||||
|
@ -408,6 +408,12 @@ void Attributes::CheckAttr(Attr* a)
|
|||
Error("&group only applicable to events");
|
||||
break;
|
||||
|
||||
case ATTR_ERROR_HANDLER:
|
||||
if ( type->Tag() != TYPE_FUNC ||
|
||||
! type->AsFuncType()->IsEvent() )
|
||||
Error("&group only applicable to events");
|
||||
break;
|
||||
|
||||
case ATTR_LOG:
|
||||
if ( ! LogVal::IsCompatibleType(type) )
|
||||
Error("&log applied to a type that cannot be logged");
|
||||
|
|
|
@ -36,6 +36,7 @@ typedef enum {
|
|||
ATTR_PRIORITY,
|
||||
ATTR_GROUP,
|
||||
ATTR_LOG,
|
||||
ATTR_ERROR_HANDLER,
|
||||
ATTR_TRACKED, // hidden attribute, tracked by NotifierRegistry
|
||||
#define NUM_ATTRS (int(ATTR_TRACKED) + 1)
|
||||
} attr_tag;
|
||||
|
|
|
@ -40,10 +40,16 @@ protected:
|
|||
event_serializer->Serialize(&info, handler->Name(), args);
|
||||
}
|
||||
|
||||
if ( handler->ErrorHandler() )
|
||||
reporter->BeginErrorHandler();
|
||||
|
||||
handler->Call(args, no_remote);
|
||||
if ( obj )
|
||||
// obj->EventDone();
|
||||
Unref(obj);
|
||||
|
||||
if ( handler->ErrorHandler() )
|
||||
reporter->EndErrorHandler();
|
||||
}
|
||||
|
||||
EventHandlerPtr handler;
|
||||
|
|
|
@ -13,6 +13,7 @@ EventHandler::EventHandler(const char* arg_name)
|
|||
local = 0;
|
||||
type = 0;
|
||||
group = 0;
|
||||
error_handler = false;
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,11 @@ public:
|
|||
void SetUsed() { used = true; }
|
||||
bool Used() { return used; }
|
||||
|
||||
// Handlers marked as error handlers will not be called recursively to
|
||||
// avoid infinite loops if they trigger a similar error themselves.
|
||||
void SetErrorHandler() { error_handler = true; }
|
||||
bool ErrorHandler() { return error_handler; }
|
||||
|
||||
const char* Group() { return group; }
|
||||
void SetGroup(const char* arg_group)
|
||||
{ group = copy_string(arg_group); }
|
||||
|
@ -57,6 +62,7 @@ private:
|
|||
FuncType* type;
|
||||
bool used; // this handler is indeed used somewhere
|
||||
bool enabled;
|
||||
bool error_handler; // this handler reports error messages.
|
||||
|
||||
declare(List, SourceID);
|
||||
typedef List(SourceID) receiver_list;
|
||||
|
|
|
@ -96,6 +96,15 @@ void EventRegistry::SetGroup(const char* name, const char* group)
|
|||
eh->SetGroup(group);
|
||||
}
|
||||
|
||||
void EventRegistry::SetErrorHandler(const char* name)
|
||||
{
|
||||
EventHandler* eh = Lookup(name);
|
||||
if ( ! eh )
|
||||
reporter->InternalError("unknown event handler in SetErrorHandler()");
|
||||
|
||||
eh->SetErrorHandler();
|
||||
}
|
||||
|
||||
void EventRegistry::EnableGroup(const char* group, bool enable)
|
||||
{
|
||||
IterCookie* c = handlers.InitForIteration();
|
||||
|
|
|
@ -31,6 +31,11 @@ public:
|
|||
// Associates a group with the given event.
|
||||
void SetGroup(const char* name, const char* group);
|
||||
|
||||
// Marks a handler as handling errors. Error handler will not be called
|
||||
// recursively to avoid infinite loops in case they trigger an error
|
||||
// themselves.
|
||||
void SetErrorHandler(const char* name);
|
||||
|
||||
// Enable/disable all members of the group.
|
||||
void EnableGroup(const char* group, bool enable);
|
||||
|
||||
|
|
|
@ -223,6 +223,7 @@ void ID::UpdateValAttrs()
|
|||
if ( Type()->Tag() == TYPE_FUNC )
|
||||
{
|
||||
Attr* attr = attrs->FindAttr(ATTR_GROUP);
|
||||
|
||||
if ( attr )
|
||||
{
|
||||
Val* group = attr->AttrExpr()->ExprVal();
|
||||
|
@ -234,6 +235,11 @@ void ID::UpdateValAttrs()
|
|||
Error("&group attribute takes string");
|
||||
}
|
||||
}
|
||||
|
||||
attr = attrs->FindAttr(ATTR_ERROR_HANDLER);
|
||||
|
||||
if ( attr )
|
||||
event_registry->SetErrorHandler(Name());
|
||||
}
|
||||
|
||||
if ( Type()->Tag() == TYPE_RECORD )
|
||||
|
|
|
@ -25,6 +25,7 @@ Reporter::Reporter()
|
|||
{
|
||||
errors = 0;
|
||||
via_events = false;
|
||||
in_error_handler = 0;
|
||||
|
||||
openlog("bro", 0, LOG_LOCAL5);
|
||||
}
|
||||
|
@ -246,7 +247,7 @@ void Reporter::DoLog(const char* prefix, EventHandlerPtr event, FILE* out, Conne
|
|||
FatalError("out of memory in Reporter");
|
||||
}
|
||||
|
||||
if ( event && via_events )
|
||||
if ( event && via_events && ! in_error_handler )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
|
||||
|
@ -304,8 +305,6 @@ void Reporter::DoLog(const char* prefix, EventHandlerPtr event, FILE* out, Conne
|
|||
fprintf(out, s.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( alloced )
|
||||
free(alloced);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,12 @@ public:
|
|||
void PopLocation()
|
||||
{ locations.pop_back(); }
|
||||
|
||||
// Signals that we're entering processing an error handler event.
|
||||
void BeginErrorHandler() { ++in_error_handler; }
|
||||
|
||||
// Signals that we're done processing an error handler event.
|
||||
void EndErrorHandler() { --in_error_handler; }
|
||||
|
||||
private:
|
||||
void DoLog(const char* prefix, EventHandlerPtr event, FILE* out, Connection* conn, val_list* addl, bool location, bool time, const char* fmt, va_list ap);
|
||||
|
||||
|
@ -87,6 +93,7 @@ private:
|
|||
|
||||
int errors;
|
||||
bool via_events;
|
||||
int in_error_handler;
|
||||
|
||||
std::list<std::pair<const Location*, const Location*> > locations;
|
||||
};
|
||||
|
|
|
@ -460,7 +460,8 @@ const_def: TOK_CONST opt_ws TOK_ID opt_ws ':' opt_ws TOK_ID opt_ws ';'
|
|||
|
||||
|
||||
/* Currently support only boolean and string values */
|
||||
opt_attr_init: '=' opt_ws TOK_BOOL opt_ws
|
||||
opt_attr_init: /* nothing */
|
||||
| '=' opt_ws TOK_BOOL opt_ws
|
||||
{
|
||||
fprintf(fp_bro_init, "=%s%c%s", $2, ($3) ? 'T' : 'F', $4);
|
||||
}
|
||||
|
|
|
@ -479,7 +479,8 @@ event rotate_size%(f: file%);
|
|||
event netflow_v5_header%(h: nf_v5_header%);
|
||||
event netflow_v5_record%(r: nf_v5_record%);
|
||||
|
||||
# Different types of reporter messages.
|
||||
event reporter_message%(t: time, msg: string, location: string%);
|
||||
event reporter_warning%(t: time, msg: string, location: string%);
|
||||
event reporter_error%(t: time, msg: string, location: string%);
|
||||
# Different types of reporter messages. These won't be called
|
||||
# recursively.
|
||||
event reporter_message%(t: time, msg: string, location: string%) &error_handler;
|
||||
event reporter_warning%(t: time, msg: string, location: string%) &error_handler;
|
||||
event reporter_error%(t: time, msg: string, location: string%) &error_handler;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
%}
|
||||
|
||||
%expect 85
|
||||
%expect 88
|
||||
|
||||
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY
|
||||
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF
|
||||
|
@ -24,7 +24,7 @@
|
|||
%token TOK_ATTR_EXPIRE_CREATE TOK_ATTR_EXPIRE_READ TOK_ATTR_EXPIRE_WRITE
|
||||
%token TOK_ATTR_PERSISTENT TOK_ATTR_SYNCHRONIZED
|
||||
%token TOK_ATTR_DISABLE_PRINT_HOOK TOK_ATTR_RAW_OUTPUT TOK_ATTR_MERGEABLE
|
||||
%token TOK_ATTR_PRIORITY TOK_ATTR_GROUP TOK_ATTR_LOG
|
||||
%token TOK_ATTR_PRIORITY TOK_ATTR_GROUP TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER
|
||||
|
||||
%token TOK_DEBUG
|
||||
|
||||
|
@ -1320,6 +1320,8 @@ attr:
|
|||
{ $$ = new Attr(ATTR_GROUP, $3); }
|
||||
| TOK_ATTR_LOG
|
||||
{ $$ = new Attr(ATTR_LOG); }
|
||||
| TOK_ATTR_ERROR_HANDLER
|
||||
{ $$ = new Attr(ATTR_ERROR_HANDLER); }
|
||||
;
|
||||
|
||||
stmt:
|
||||
|
|
|
@ -290,6 +290,7 @@ when return TOK_WHEN;
|
|||
&disable_print_hook return TOK_ATTR_DISABLE_PRINT_HOOK;
|
||||
&raw_output return TOK_ATTR_RAW_OUTPUT;
|
||||
&encrypt return TOK_ATTR_ENCRYPT;
|
||||
&error_handler return TOK_ATTR_ERROR_HANDLER;
|
||||
&expire_func return TOK_ATTR_EXPIRE_FUNC;
|
||||
&group return TOK_ATTR_GROUP;
|
||||
&log return TOK_ATTR_LOG;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[script] reporter_error: no such index (a[1]) [/da/home/robin/bro/topic/testing/btest/.tmp/core.reporter-error-in-handler/reporter-error-in-handler.bro, line 28] [0.000000]
|
||||
1st error printed on script level
|
||||
error in /da/home/robin/bro/topic/testing/btest/.tmp/core.reporter-error-in-handler/reporter-error-in-handler.bro, line 22: no such index (a[2])
|
29
testing/btest/core/reporter-error-in-handler.bro
Normal file
29
testing/btest/core/reporter-error-in-handler.bro
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# This test procudes a recursive error: the error handler is itself broken. Rather
|
||||
# than looping indefinitly, the error inside the handler should reported to stderr.
|
||||
#
|
||||
# @TEST-EXEC: bro %INPUT >output 2>&1
|
||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff output
|
||||
|
||||
global a: table[count] of count;
|
||||
|
||||
global c = 0;
|
||||
|
||||
event reporter_error(t: time, msg: string, location: string)
|
||||
{
|
||||
c += 1;
|
||||
|
||||
if ( c > 1 )
|
||||
print "FAILED: 2nd error reported to script as well.";
|
||||
|
||||
else
|
||||
{
|
||||
print "1st error printed on script level";
|
||||
print a[2];
|
||||
}
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
print a[1];
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue