dfilter: Allow existence check for slices

Allow checking if a slice exists. The result is true if the
slice has length greater than zero.

The len() function is implemented as a DFVM instruction instead.
The semantics are the same.
This commit is contained in:
João Valverde 2022-07-03 23:07:36 +01:00 committed by A Wireshark GitLab Utility
parent 0fc81c21b2
commit a877f2d5f3
8 changed files with 117 additions and 38 deletions

View File

@ -3839,7 +3839,7 @@ set(_test_group_list
suite_dfilter.group_integer_1byte
suite_dfilter.group_ipv4
suite_dfilter.group_membership
suite_dfilter.group_range_method
suite_dfilter.group_slice
suite_dfilter.group_scanner
suite_dfilter.group_string_type
suite_dfilter.group_stringz

View File

@ -141,6 +141,7 @@ They previously shipped with Qt 5.12.2.
For example the double-quoted string "\0 is a null byte" is a legal literal value.
This may be useful to match byte patterns but note that in general protocol fields with a string type still cannot contain embedded null bytes.
** Booleans can be written as True/TRUE or False/FALSE. Previously they could only be written as 1 or 0.
** It is now possible to test for the existence of a slice.
* The `text2pcap` command and the “Import from Hex Dump” feature have been updated and enhanced:
** `text2pcap` supports writing the output file in all the capture file formats that wiretap library supports, using the same `-F` option as `editcap`, `mergecap`, and `tshark`.

View File

@ -73,30 +73,6 @@ df_func_upper(GSList *args, guint32 arg_count, GSList **retval)
return string_walk(args, arg_count, retval, g_ascii_toupper);
}
/* dfilter function: len() */
static gboolean
df_func_len(GSList *args, guint32 arg_count, GSList **retval)
{
GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *ft_len;
ws_assert(arg_count == 1);
arg1 = args->data;
if (arg1 == NULL)
return FALSE;
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
ft_len = fvalue_new(FT_UINT32);
fvalue_set_uinteger(ft_len, fvalue_length(arg_fvalue));
*retval = g_slist_prepend(*retval, ft_len);
arg1 = arg1->next;
}
return TRUE;
}
/* dfilter function: count() */
static gboolean
df_func_count(GSList *args, guint32 arg_count, GSList **retval)
@ -460,7 +436,8 @@ static df_func_def_t
df_functions[] = {
{ "lower", df_func_lower, 1, 1, ul_semcheck_is_field_string },
{ "upper", df_func_upper, 1, 1, ul_semcheck_is_field_string },
{ "len", df_func_len, 1, 1, ul_semcheck_is_field },
/* Length function is implemented as a DFVM instruction. */
{ "len", NULL, 1, 1, ul_semcheck_is_field },
{ "count", df_func_count, 1, 1, ul_semcheck_is_field },
{ "string", df_func_string, 1, 1, ul_semcheck_string_param },
{ "max", df_func_max, 1, 0, ul_semcheck_compare },
@ -476,7 +453,7 @@ df_func_lookup(const char *name)
df_func_def_t *func_def;
func_def = df_functions;
while (func_def->function != NULL) {
while (func_def->name != NULL) {
if (strcmp(func_def->name, name) == 0) {
return func_def;
}

View File

@ -53,6 +53,7 @@ dfvm_opcode_tostr(dfvm_opcode_t code)
case DFVM_ALL_IN_RANGE: return "ALL_IN_RANGE";
case DFVM_ANY_IN_RANGE: return "ANY_IN_RANGE";
case DFVM_SLICE: return "SLICE";
case DFVM_LENGTH: return "LENGTH";
case DFVM_BITWISE_AND: return "BITWISE_AND";
case DFVM_UNARY_MINUS: return "UNARY_MINUS";
case DFVM_ADD: return "ADD";
@ -350,6 +351,11 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references)
id, opcode_str, arg1_str, arg3_str, arg2_str);
break;
case DFVM_LENGTH:
wmem_strbuf_append_printf(buf, "%05d %s\t\t%s -> %s\n",
id, opcode_str, arg1_str, arg2_str);
break;
case DFVM_ALL_EQ:
case DFVM_ANY_EQ:
wmem_strbuf_append_printf(buf, "%05d %s\t\t%s === %s\n",
@ -998,6 +1004,28 @@ mk_slice(dfilter_t *df, dfvm_value_t *from_arg, dfvm_value_t *to_arg,
df->free_registers[to_arg->value.numeric] = (GDestroyNotify)fvalue_free;
}
static void
mk_length(dfilter_t *df, dfvm_value_t *from_arg, dfvm_value_t *to_arg)
{
GSList *from_list, *to_list;
fvalue_t *old_fv, *new_fv;
to_list = NULL;
from_list = df->registers[from_arg->value.numeric];
while (from_list) {
old_fv = from_list->data;
new_fv = fvalue_new(FT_UINT32);
fvalue_set_uinteger(new_fv, fvalue_length(old_fv));
to_list = g_slist_prepend(to_list, new_fv);
from_list = g_slist_next(from_list);
}
df->registers[to_arg->value.numeric] = to_list;
df->free_registers[to_arg->value.numeric] = (GDestroyNotify)fvalue_free;
}
static gboolean
call_function(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *arg2,
dfvm_value_t *arg3)
@ -1318,6 +1346,10 @@ dfvm_apply(dfilter_t *df, proto_tree *tree)
mk_slice(df, arg1, arg2, arg3);
break;
case DFVM_LENGTH:
mk_length(df, arg1, arg2);
break;
case DFVM_ALL_EQ:
accum = all_test(df, fvalue_eq, arg1, arg2);
break;

View File

@ -79,6 +79,7 @@ typedef enum {
DFVM_ALL_IN_RANGE,
DFVM_ANY_IN_RANGE,
DFVM_SLICE,
DFVM_LENGTH,
DFVM_BITWISE_AND,
DFVM_UNARY_MINUS,
DFVM_ADD,

View File

@ -70,6 +70,7 @@ select_opcode(dfvm_opcode_t op, stmatch_t how)
case DFVM_READ_REFERENCE_R:
case DFVM_PUT_FVALUE:
case DFVM_SLICE:
case DFVM_LENGTH:
case DFVM_BITWISE_AND:
case DFVM_UNARY_MINUS:
case DFVM_ADD:
@ -281,6 +282,30 @@ dfw_append_put_fvalue(dfwork_t *dfw, fvalue_t *fv)
return reg_val;
}
/* returns register number that the length's result will be in. */
static dfvm_value_t *
dfw_append_length(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
{
GSList *params;
dfvm_insn_t *insn;
dfvm_value_t *reg_val, *val_arg;
/* Create the new DFVM instruction */
insn = dfvm_insn_new(DFVM_LENGTH);
/* Create input argument */
params = sttype_function_params(node);
ws_assert(params);
ws_assert(g_slist_length(params) == 1);
val_arg = gen_entity(dfw, params->data, jumps_ptr);
insn->arg1 = dfvm_value_ref(val_arg);
/* Destination. */
reg_val = dfvm_value_new_register(dfw->next_register++);
insn->arg2 = dfvm_value_ref(reg_val);
dfw_append_insn(dfw, insn);
return reg_val;
}
/* returns register number that the functions's result will be in. */
static dfvm_value_t *
dfw_append_function(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
@ -292,6 +317,11 @@ dfw_append_function(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
dfvm_value_t *reg_val, *val1, *val3, *val_arg;
guint count;
if (strcmp(sttype_function_name(node), "len") == 0) {
/* Replace len() function call with DFVM_LENGTH instruction. */
return dfw_append_length(dfw, node, jumps_ptr);
}
/* Create the new DFVM instruction */
insn = dfvm_insn_new(DFVM_CALL_FUNCTION);
val1 = dfvm_value_new_funcdef(sttype_function_funcdef(node));
@ -600,6 +630,31 @@ gen_notzero(dfwork_t *dfw, stnode_t *st_node)
g_slist_free(jumps);
}
static void
gen_exists_slice(dfwork_t *dfw, stnode_t *st_node)
{
dfvm_insn_t *insn;
dfvm_value_t *val1, *reg_val;
GSList *jumps = NULL;
val1 = gen_entity(dfw, st_node, &jumps);
/* Compute length. */
insn = dfvm_insn_new(DFVM_LENGTH);
insn->arg1 = dfvm_value_ref(val1);
reg_val = dfvm_value_new_register(dfw->next_register++);
insn->arg2 = dfvm_value_ref(reg_val);
dfw_append_insn(dfw, insn);
/* Check length is not zero. */
insn = dfvm_insn_new(DFVM_ALL_ZERO);
insn->arg1 = dfvm_value_ref(reg_val);
dfw_append_insn(dfw, insn);
insn = dfvm_insn_new(DFVM_NOT);
dfw_append_insn(dfw, insn);
/* Fixup jumps. */
g_slist_foreach(jumps, fixup_jumps, dfw);
g_slist_free(jumps);
}
static void
gen_test(dfwork_t *dfw, stnode_t *st_node)
{
@ -717,6 +772,9 @@ gencode(dfwork_t *dfw, stnode_t *st_node)
case STTYPE_ARITHMETIC:
gen_notzero(dfw, st_node);
break;
case STTYPE_SLICE:
gen_exists_slice(dfw, st_node);
break;
default:
ws_assert_not_reached();
}

View File

@ -454,7 +454,6 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
switch (stnode_type_id(st_arg1)) {
case STTYPE_FIELD:
case STTYPE_ARITHMETIC:
/* This is OK */
break;
case STTYPE_REFERENCE:
@ -465,16 +464,6 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
stnode_todisplay(st_arg1));
break;
case STTYPE_SLICE:
/*
* XXX - why not? Shouldn't "eth[3:2]" mean
* "check whether the 'eth' field is present and
* has at least 2 bytes starting at an offset of
* 3"?
*/
FAIL(dfw, st_arg1, "You cannot test whether a slice is present.");
break;
case STTYPE_FUNCTION:
/* XXX - Maybe we should change functions so they can return fields,
* in which case the 'exist' should be fine. */
@ -487,6 +476,8 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
case STTYPE_TEST:
case STTYPE_FVALUE:
case STTYPE_PCRE:
case STTYPE_ARITHMETIC:
case STTYPE_SLICE:
ws_assert_not_reached();
}
}
@ -1255,6 +1246,9 @@ semcheck(dfwork_t *dfw, stnode_t *st_node)
case STTYPE_ARITHMETIC:
check_arithmetic_expr(dfw, st_node, FT_NONE);
break;
case STTYPE_SLICE:
check_slice_sanity(dfw, st_node, FT_NONE);
break;
default:
check_exists(dfw, st_node);
}

View File

@ -78,3 +78,19 @@ class case_range(unittest.TestCase):
def test_slice_range_5(self, checkDFilterSucceed):
dfilter = "frame[20:] contains :12345678"
checkDFilterSucceed(dfilter)
def test_slice_exists_1(self, checkDFilterCount):
dfilter = "frame[59]"
checkDFilterCount(dfilter, 1)
def test_slice_exists_2(self, checkDFilterCount):
dfilter = "frame[60]"
checkDFilterCount(dfilter, 0)
def test_slice_exists_3(self, checkDFilterCount):
dfilter = "frame[50-59]"
checkDFilterCount(dfilter, 1)
def test_slice_exists_4(self, checkDFilterCount):
dfilter = "frame[50-60]"
checkDFilterCount(dfilter, 0)