dfilter: Allow constants as the first or only argument to min/max

The strategy here is to delay resolving literals to values until
we have looked at the entire argument list.

Also we will try to commute the relation in a comparison if
we do not have a type for the return value of the function,
like any other constant.

Before:

    Filter: max(1,_ws.ftypes.int8) == 1
    dftest: Argument '1' is not valid for max()
    	max(1,_ws.ftypes.int8) == 1
    	    ^

After:

    Filter: max(1,_ws.ftypes.int8) == 1

    Syntax tree:
     0 TEST_ANY_EQ:
       1 FUNCTION(max#2):
         2 FVALUE(1 <FT_INT8>)
         2 FIELD(_ws.ftypes.int8 <FT_INT8>)
       1 FVALUE(1 <FT_INT8>)

    Instructions:
    00000 STACK_PUSH	1 <FT_INT8>
    00001 READ_TREE		_ws.ftypes.int8 <FT_INT8> -> reg#1
    00002 IF_FALSE_GOTO	3
    00003 STACK_PUSH	reg#1
    00004 CALL_FUNCTION	max(reg#1, 1 <FT_INT8>) -> reg#0
    00005 STACK_POP	2
    00006 IF_FALSE_GOTO	8
    00007 ANY_EQ		reg#0 == 1 <FT_INT8>
    00008 RETURN
This commit is contained in:
João Valverde 2022-12-26 03:42:07 +00:00
parent 6399f724d9
commit b19bed43d1
5 changed files with 70 additions and 40 deletions

View File

@ -56,6 +56,10 @@ typedef struct {
GHashTable *raw_references; /* hfinfo -> pointer to array of references */
char *expanded_text;
gboolean apply_optimization;
wmem_allocator_t *dfw_scope; /* Because we use exceptions for error handling sometimes
cleaning up memory allocations is inconvenient. Memory
allocated from this pool will be freed when the dfwork_t
context is destroyed. */
} dfwork_t;
/*

View File

@ -234,6 +234,8 @@ dfwork_new(void)
g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)free_refs_array);
dfw->dfw_scope = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
return dfw;
}
@ -273,6 +275,8 @@ dfwork_free(dfwork_t *dfw)
g_free(dfw->expanded_text);
wmem_destroy_allocator(dfw->dfw_scope);
/*
* We don't free the error message string; our caller will return
* it to its caller.

View File

@ -330,51 +330,52 @@ ul_semcheck_string_param(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftyp
}
/* Check arguments are all the same type and they can be compared. */
/*
Every STTYPE_LITERAL needs to be resolved to a STTYPE_FVALUE. If we don't
have type information (lhs_ftype is FT_NONE) and we have not seen an argument
with a definite type we defer resolving literals to values until we have examined
the entire list of function arguments. If we still cannot resolve to a definite
type after that (all arguments must have the same type) then we give up and
return FT_NONE.
*/
static ftenum_t
ul_semcheck_compare(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype,
GSList *param_list, df_loc_t func_loc _U_)
{
stnode_t *arg;
sttype_id_t type;
ftenum_t ftype, ft_arg;
GSList *l;
fvalue_t *fv;
wmem_list_t *literals = NULL;
arg = param_list->data;
ftype = lhs_ftype;
if (stnode_type_id(arg) == STTYPE_ARITHMETIC) {
ftype = check_arithmetic(dfw, arg, lhs_ftype);
}
else if (stnode_type_id(arg) == STTYPE_LITERAL && lhs_ftype != FT_NONE) {
fv = dfilter_fvalue_from_literal(dfw, lhs_ftype, arg, FALSE, NULL);
stnode_replace(arg, STTYPE_FVALUE, fv);
ftype = fvalue_type_ftenum(fv);
}
else if (stnode_type_id(arg) == STTYPE_FUNCTION) {
ftype = check_function(dfw, arg, lhs_ftype);
}
else if (stnode_type_id(arg) == STTYPE_FIELD || stnode_type_id(arg) == STTYPE_REFERENCE) {
ftype = sttype_field_ftenum(arg);
}
else {
FAIL(dfw, arg, "Argument '%s' is not valid for %s()",
stnode_todisplay(arg), func_name);
}
for (l = param_list->next; l != NULL; l = l->next) {
for (l = param_list; l != NULL; l = l->next) {
arg = l->data;
type = stnode_type_id(arg);
if (stnode_type_id(arg) == STTYPE_ARITHMETIC) {
if (type == STTYPE_ARITHMETIC) {
ft_arg = check_arithmetic(dfw, arg, ftype);
}
else if (stnode_type_id(arg) == STTYPE_LITERAL && ftype != FT_NONE) {
fv = dfilter_fvalue_from_literal(dfw, ftype, arg, FALSE, NULL);
stnode_replace(arg, STTYPE_FVALUE, fv);
ft_arg = fvalue_type_ftenum(fv);
else if (type == STTYPE_LITERAL) {
if (ftype != FT_NONE) {
fv = dfilter_fvalue_from_literal(dfw, ftype, arg, FALSE, NULL);
stnode_replace(arg, STTYPE_FVALUE, fv);
ft_arg = fvalue_type_ftenum(fv);
}
else {
if (literals == NULL) {
literals = wmem_list_new(dfw->dfw_scope);
}
wmem_list_append(literals, arg);
ft_arg = FT_NONE;
}
}
else if (stnode_type_id(arg) == STTYPE_FUNCTION) {
else if (type == STTYPE_FUNCTION) {
ft_arg = check_function(dfw, arg, ftype);
}
else if (stnode_type_id(arg) == STTYPE_FIELD || stnode_type_id(arg) == STTYPE_REFERENCE) {
else if (type == STTYPE_FIELD || type == STTYPE_REFERENCE) {
ft_arg = sttype_field_ftenum(arg);
}
else {
@ -385,15 +386,29 @@ ul_semcheck_compare(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype,
if (ftype == FT_NONE) {
ftype = ft_arg;
}
if (ft_arg != ftype) {
if (ft_arg != FT_NONE && ftype != FT_NONE && ft_arg != ftype) {
FAIL(dfw, arg, "Arguments to '%s' must have the same type (expected %s, got %s)",
func_name, ftype_name(ftype), ftype_name(ft_arg));
}
if (!ftype_can_cmp(ft_arg)) {
if (ft_arg != FT_NONE && !ftype_can_cmp(ft_arg)) {
FAIL(dfw, arg, "Argument '%s' to '%s' cannot be ordered",
stnode_todisplay(arg), func_name);
}
}
if (literals != NULL) {
if (ftype != FT_NONE) {
wmem_list_frame_t *fp;
stnode_t *st;
for (fp = wmem_list_head(literals); fp != NULL; fp = wmem_list_frame_next(fp)) {
st = wmem_list_frame_data(fp);
fv = dfilter_fvalue_from_literal(dfw, ftype, st, FALSE, NULL);
stnode_replace(st, STTYPE_FVALUE, fv);
}
}
wmem_destroy_list(literals);
}
return ftype;
}

View File

@ -787,8 +787,8 @@ check_relation_LHS_SLICE(dfwork_t *dfw, stnode_op_t st_op,
static void
check_relation_LHS_FUNCTION(dfwork_t *dfw, stnode_op_t st_op,
FtypeCanFunc can_func, gboolean allow_partial_value,
stnode_t *st_node,
stnode_t *st_arg1, stnode_t *st_arg2)
stnode_t *st_node, stnode_t *st_arg1, stnode_t *st_arg2,
int commute)
{
sttype_id_t type2;
ftenum_t ftype1, ftype2;
@ -797,7 +797,11 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, stnode_op_t st_op,
LOG_NODE(st_node);
ftype1 = check_function(dfw, st_arg1, FT_NONE);
if (ftype1 == FT_NONE) {
check_relation(dfw, st_op, can_func, allow_partial_value,
st_node, st_arg2, st_arg1, commute - 1);
return;
}
if (!can_func(ftype1)) {
FAIL(dfw, st_arg1, "Function %s (type=%s) cannot participate in %s comparison.",
sttype_function_name(st_arg1), ftype_pretty_name(ftype1),
@ -1006,7 +1010,7 @@ check_relation(dfwork_t *dfw, stnode_op_t st_op,
break;
case STTYPE_FUNCTION:
check_relation_LHS_FUNCTION(dfw, st_op, can_func,
allow_partial_value, st_node, st_arg1, st_arg2);
allow_partial_value, st_node, st_arg1, st_arg2, commute);
break;
case STTYPE_ARITHMETIC:
check_relation_LHS_ARITHMETIC(dfw, st_op, can_func,
@ -1042,7 +1046,7 @@ check_relation_contains(dfwork_t *dfw, stnode_t *st_node,
break;
case STTYPE_FUNCTION:
check_relation_LHS_FUNCTION(dfw, STNODE_OP_CONTAINS, ftype_can_contains,
TRUE, st_node, st_arg1, st_arg2);
TRUE, st_node, st_arg1, st_arg2, 0);
break;
case STTYPE_SLICE:
check_relation_LHS_SLICE(dfw, STNODE_OP_CONTAINS, ftype_can_contains,
@ -1089,7 +1093,7 @@ check_relation_matches(dfwork_t *dfw, stnode_t *st_node,
break;
case STTYPE_FUNCTION:
check_relation_LHS_FUNCTION(dfw, STNODE_OP_MATCHES, ftype_can_matches,
TRUE, st_node, st_arg1, st_arg2);
TRUE, st_node, st_arg1, st_arg2, 0);
break;
case STTYPE_SLICE:
check_relation_LHS_SLICE(dfw, STNODE_OP_MATCHES, ftype_can_matches,

View File

@ -66,10 +66,13 @@ class case_dfunction_maxmin(unittest.TestCase):
dfilter = 'max(udp.srcport, udp.dstport) < 5060'
checkDFilterCount(dfilter, 1)
def test_max_4(self, checkDFilterFail):
error = 'Argument \'1\' is not valid for max()'
dfilter = 'max(1,_ws.ftypes.int8) == 1'
checkDFilterFail(dfilter, error)
def test_max_4(self, checkDFilterCount):
dfilter = 'max(5060, udp.dstport) == udp.srcport'
checkDFilterCount(dfilter, 2)
def test_max_5(self, checkDFilterCount):
dfilter = 'max(5060, 5070) == udp.srcport'
checkDFilterCount(dfilter, 1)
@fixtures.uses_fixtures
class case_dfunction_abs(unittest.TestCase):