Commit Graph

98 Commits

Author SHA1 Message Date
John Thacker 49934c1d14 dfilter: Add a flag to return field values for the tree root
In many grammatical contexts fields are only tested for existence
instead of loading the values into a register, because that's all
that is needed to determine if a filter passes or not. Add a
dfilter option to load the field values from the tree and return
them when a field (including field at a certain protocol layer) is
the root of the filter syntax tree.

This is useful for columns, especially for parsing columns defined
with the layer operator, but it can't completely replace the current
custom column handling because we don't yet return exactly which
hfinfo was present, if more than one has the same abbreviation, and
it's possible for fields with the same abbreviation to have different
strings, and hence different "resolved" values.

$ ./run/dftest -s "@ip.proto#1"
Filter:
 @ip.proto#1

Syntax tree:
 0 FIELD(@ip.proto#[1:1] <FT_BYTES>)

Instructions:
 0000 CHECK_EXISTS_R   ip.proto#[1:1]
 0001 RETURN

$ ./run/dftest -s "@ip.proto#1" --return-vals
Filter:
 @ip.proto#1

Syntax tree:
 0 FIELD(@ip.proto#[1:1] <FT_BYTES>)

Instructions:
 0000 READ_TREE_R      @ip.proto#[1:1]  -> R0
 0001 NO_OP
 0002 RETURN           R0

Related to #18588
2024-02-12 20:39:12 -05:00
John Thacker 214a744bb7 dfilter: Return the register containing fvalues
When generating DVFM code, tell the return function what
register has the final set of fvalues for filters that are
functions, arithmetic, or slices (that is, that compare one
or more fvalues to see if they are all zero.) Make sure
that these functions return an empty ptr array, unlike
tests that return a null ptr array.

For fields, we could return the fvalues, but currently we
don't bother loading the fvalues into registers since display
filters that just have a field test existence, so the generated
code would have to change. It's also a little more complicated
because there can be multiple fields that have different types
(sometimes not commensurable, which is an error noted by some of
the checks.) The logic in custom columns handles the field cases
currently.
2024-02-10 19:44:04 -05:00
João Valverde 491857ae7e dfilter: Elide branch jumps to next instruction
If a branch instruction does not branch, i.e it jumps
to the next instruction, replace it with a no-op for
a slight performance optimization and decluttering
of the bytecode.
2023-11-02 09:42:50 +00:00
João Valverde 5a4d883f85 dfilter: Fix crash with nested function calls
When the jumps_ptr is NULL a nested function call
results in a NULL pointer dereference. We could add
a NULL check but removing the jump in commit e85f8d4cf1
was a mitake, because the jump is not always a no-op,
so add it back.

Fixes e85f8d4cf1.
2023-11-02 09:42:50 +00:00
João Valverde 47746f0316 dfilter: Minor cleanup 2023-10-24 22:28:30 +01:00
João Valverde 5609c77144 dfilter: Allow testing for nonzero function result
Allow functions to be tested for "existence". This is in fact
not an existence test but a truthiness test for the function
return value.
2023-10-24 20:51:24 +01:00
João Valverde 1af4b495f2 dfilter: Use better assertions for invalid enums
Remove name static storage and use a switch to map enums to
names. This allows mapping names all names, even those that
are not instantiated as objects.

Rewrite some assertions using ws_error() for detailed error
messages.
2023-10-22 21:50:46 +00:00
João Valverde b86a14be4a dfilter: Fix a compiler warning [-Wmaybe-uninitialized]
/builds/wireshark/wireshark/epan/dfilter/gencode.c: In function 'gen_arithmetic':
/builds/wireshark/wireshark/epan/dfilter/gencode.c:538:17: error: 'op' may be used uninitialized [-Werror=maybe-uninitialized]
  538 |                 gen_relation_insn(dfw, op, val1, reg_val, NULL);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/builds/wireshark/wireshark/epan/dfilter/gencode.c:501:25: note: 'op' was declared here
  501 |         dfvm_opcode_t   op;
      |                         ^~
cc1: all warnings being treated as errors

/builds/wireshark/wireshark/epan/dfilter/semcheck.c: In function 'dfilter_fvalue_from_number':
/builds/wireshark/wireshark/epan/dfilter/semcheck.c:340:17: error: 'fv' may be used uninitialized [-Werror=maybe-uninitialized]
  340 |                 stnode_replace(st, STTYPE_FVALUE, fv);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/builds/wireshark/wireshark/epan/dfilter/semcheck.c:297:19: note: 'fv' was declared here
  297 |         fvalue_t *fv;
      |                   ^~
cc1: all warnings being treated as errors
2023-10-22 11:08:37 +01:00
João Valverde 8710ec9cf1 dfilter: Rewrite if-else block using a switch 2023-10-22 10:54:26 +01:00
João Valverde d7bc20d3ea dfilter: Refactor DFVM_VALUE_STRING argument
Instead of having a specific value type for a value_string
just pass the header_field_info as an argument to the
DFVM_VALUE_STRING instruction.
2023-09-28 10:15:56 +00:00
João Valverde dcce728bb7 dfilter: Add support for BASE_CUSTOM value strings
BASE_CUSTOM is not a value string so it doesn not have a catalog
of values but for implementation purposes we can treat it as a
value string that cannot be uniquely mapped to an integer.

This way fields using BASE_CUSTOM can also be matched using the
custom string representation.

This is probably less useful thani normal value strings but still
it is a use case we can support with the current compiler.
2023-09-28 10:15:56 +00:00
João Valverde eb3d347c02 dfilter: Compute value string at runtime
Not all value strings or other maps that can be treated like
a value string can be inverted to a single number. Sometimes
they might represent a range, such as with range_strings,
or more than one number maps to the same string, so the
function is not a bijection.

To be able to handle those cases we add an instruction to
transform the field value to the string at runtime and use
string comparisons to match the value string.

This way all string maps can be handled by the display filter
engine.

TFS() strings are always bijections so that case is unchanged;
we map the string to a boolean at compile time.

Fixes #19351.
2023-09-28 10:15:56 +00:00
João Valverde 43fd777dcd Fixup "void *" pointer declaration style 2023-09-24 15:04:24 +01:00
Gerald Combs 54e729fe6c Convert epan/dfilter to C99 types
Ping #19116
2023-09-24 06:26:10 +00:00
João Valverde ca8976020f dfilter: Change "not in" behaviour to match inequality
"A not in S" is now implemented as "A and A not_in S"
instead of "not (A in S)".

"not A in S" is implemented as "not A or A not_in S".

This is to be consistent with the way inequality has historically
worked, where "A != B" is not the same as "not A == B".

Maybe we should change both propositions to have inequality
be the same as not equality instead.

Fixes #19187.
2023-07-25 12:17:25 +00:00
João Valverde d138e594b5 dfilter: Fix `all .. in` operator semantics
Fix the "all X in S" expression to be implemented as

    (x1 in S) AND (x2 in S) AND ... AND (xn in S)

Previously it was implemented as

    (X all_eq s1) OR (X all_eq s2) OR ... OR (X all_eq sn)

which does not implement set membership semantics correctly.

The implementation uses a list to build the set and the
set membership test is done with a SET_*_IN instruction
that tests if a register belongs to the set (list contents).

Example:

    Filter:
     all tcp.port in {10..15,20,30}

    Instructions:
     0000 READ_TREE        tcp.port         -> R0
     0001 IF_FALSE_GOTO    7
     0002 SET_ADD_RANGE    10 .. 15
     0003 SET_ADD          20
     0004 SET_ADD          30
     0005 SET_ALL_IN       R0
     0006 SET_CLEAR
     0007 RETURN

Fixes  #19188.
2023-07-24 22:25:33 +00:00
João Valverde 9e98b13524 dfilter: Move flags to dfwork_t
Replace booleans with a flags field.
2023-04-21 17:02:29 +00:00
João Valverde 39f3587b98 dfilter: Add some missing headers 2023-04-17 01:08:15 +00:00
João Valverde e85f8d4cf1 dfilter: Do not jump when generating function arguments
Instead of "jumping" with length zero to the next sequential
instruction skip generating the no-op jump instruction entirely.
2022-12-27 21:09:04 +00:00
João Valverde 540b71d738 dfilter: Fix crash with a constant arithmetic expression 2022-12-26 23:55:27 +00:00
João Valverde 84e75be5c6 dfilter: Add optimization flag
When we are just testing code to see if it compiles performing
optimizations is wasteful. Add an option to disable them.
2022-11-30 17:36:17 +00:00
João Valverde b83658d8a4 dfilter: Add suport for raw addressing with references
Extends raw adressing syntax to wok with references. The syntax
is
    @field1 == ${@field2}

This requires replicating the logic to load field references, but
using raw values instead. We use separate hash tables for that,
namely "references" vs "raw_references".
2022-10-31 21:02:39 +00:00
João Valverde 0853ddd1cb dfilter: Add support for raw (bytes) addressing mode
This adds new syntax to read a field from the tree as bytes, instead
of the actual type. This is a useful extension for example to match
matformed strings that contain unicode replacement characters. In
this case it is not possible to match the raw value of the malformed
string field. This extension fills this need and is generic enough
that it should be useful in many other situations.

The syntax used is to prefix the field name with "@". The following
artificial example tests if the HTTP user agent contains a particular
invalid UTF-8 sequence:

    @http.user_agent == "Mozill\xAA"

Where simply using "http.user_agent" won't work because the invalid byte
sequence will have been replaced with U+FFFD.

Considering the following programs:

    $ dftest '_ws.ftypes.string == "ABC"'
    Filter: _ws.ftypes.string == "ABC"

    Syntax tree:
     0 TEST_ANY_EQ:
       1 FIELD(_ws.ftypes.string <FT_STRING>)
       1 FVALUE("ABC" <FT_STRING>)

    Instructions:
    00000 READ_TREE		_ws.ftypes.string <FT_STRING> -> reg#0
    00001 IF_FALSE_GOTO	3
    00002 ANY_EQ		reg#0 == "ABC" <FT_STRING>
    00003 RETURN

    $ dftest '@_ws.ftypes.string == "ABC"'
    Filter: @_ws.ftypes.string == "ABC"

    Syntax tree:
     0 TEST_ANY_EQ:
       1 FIELD(_ws.ftypes.string <RAW>)
       1 FVALUE(41:42:43 <FT_BYTES>)

    Instructions:
    00000 READ_TREE		@_ws.ftypes.string <FT_BYTES> -> reg#0
    00001 IF_FALSE_GOTO	3
    00002 ANY_EQ		reg#0 == 41:42:43 <FT_BYTES>
    00003 RETURN

In the second case the field has a "raw" type, that equates directly to
FT_BYTES, and the field value is read from the protocol raw data.
2022-10-31 21:02:39 +00:00
João Valverde 5e3a7e9ab8 dfilter: Small optimization for "not all zero" code
Remove extra NOT instruction. Also remove unused ANY_ZERO opcode.
2022-07-05 09:58:43 +01:00
João Valverde a877f2d5f3 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.
2022-07-04 22:45:14 +00:00
João Valverde eb8acd088e dfilter: Rename dfvm opcodes with a namespace prefix 2022-07-02 11:46:45 +01:00
João Valverde fc5c81328e dfilter: Rename test syntax tree node
Test node also includes arithmetic operations so rename it
to a generic "operator" node.
2022-07-02 11:39:17 +01:00
João Valverde aaff0d21ae dfilter: Add layer support for references
This adds support for using the layers filter
with field references.

Before:
    $ dftest 'ip.src != ${ip.src#2}'
    dftest: invalid character in macro name

After:
    $ dftest 'ip.src != ${ip.src#2}'
    Filter: ip.src != ${ip.src#2}

    Syntax tree:
     0 TEST_ALL_NE:
       1 FIELD(ip.src <FT_IPv4>)
       1 REFERENCE(ip.src#[2:1] <FT_IPv4>)

    Instructions:
    00000 READ_TREE		ip.src <FT_IPv4> -> reg#0
    00001 IF_FALSE_GOTO	5
    00002 READ_REFERENCE_R	${ip.src <FT_IPv4>} #[2:1] -> reg#1
    00003 IF_FALSE_GOTO	5
    00004 ALL_NE		reg#0 != reg#1
    00005 RETURN

This requires adding another level of complexity to references.
When loading references we need to copy the 'proto_layer_num'
and add the logic to filter on that.

The "layer" sttype is removed and replace by a new
field sttype with support for a range. This is a nice
cleanup for the semantic check and general simplification.
The grammar is better too with this design.

Range sttype is renamed to slice for clarity.
2022-06-25 14:57:40 +01:00
João Valverde bebf7afa37 dfilter: Remove unused DFVM 4th instruction argument 2022-05-13 14:13:18 +01:00
João Valverde b602911b31 dfilter: Add support for universal quantifiers
Adds the keywords "any" and "all" to implement the quantification
to any existing relational operator.

Filter: all tcp.port in {100, 2000..3000}

Syntax tree:
 0 ALL TEST_IN:
   1 FIELD(tcp.port)
   1 SET(#2):
     2 FVALUE(100 <FT_UINT16>)
     2 FVALUE(2000 <FT_UINT16>) .. FVALUE(3000 <FT_UINT16>)

Instructions:
00000 READ_TREE		tcp.port -> reg#0
00001 IF_FALSE_GOTO	5
00002 ALL_EQ		reg#0 === 100 <FT_UINT16>
00003 IF_TRUE_GOTO	5
00004 ALL_IN_RANGE	reg#0 in { 2000 <FT_UINT16> .. 3000 <FT_UINT16> }
00005 RETURN
2022-05-12 14:26:54 +01:00
João Valverde 4f3f507eee dfilter: Add syntax to match specific layers in the protocol stack
Add support to display filters for matching a specific layer within a frame.
Layers are counted sequentially up the protocol stack. Each protocol
(dissector) that appears in the stack is one layer.

LINK-LAYER#1 <-> IP#1 <-> TCP#1 <-> IP#2 <-> TCP#2 <-> etc.

The syntax allows for negative indexes and ranges with the usual semantics
for slices (but note that counting starts at one):

    tcp.port#[2-4] == 1024

Matches layers 2 to 4 inclusive.

Fixes #3791.
2022-04-26 16:50:59 +00:00
João Valverde c0170dad42 dfilter: Rename "range" to "slice"
The word range is used for different things with different
meanings and that is confusing. Avoid using "range" in code to
mean "slice".

A range is one or more intervals with a lower and upper bound.

A slice is a range applied to a bytes field.

Replace range with slice wherever appropriate. This usage of
"slice" instead of range is generally correct and consistent in
the documentation.
2022-04-26 16:50:59 +00:00
João Valverde fab32ea0cb dfilter: Allow arithmetic expressions as function arguments
This allows writing moderately complex expressions, for example
a float epsilon test (#16483):

Filter: {abs(_ws.ftypes.double - 1) / max(abs(_ws.ftypes.double), abs(1))} < 0.01

Syntax tree:
 0 TEST_LT:
   1 OP_DIVIDE:
     2 FUNCTION(abs#1):
       3 OP_SUBTRACT:
         4 FIELD(_ws.ftypes.double)
         4 FVALUE(1 <FT_DOUBLE>)
     2 FUNCTION(max#2):
       3 FUNCTION(abs#1):
         4 FIELD(_ws.ftypes.double)
       3 FUNCTION(abs#1):
         4 FVALUE(1 <FT_DOUBLE>)
   1 FVALUE(0.01 <FT_DOUBLE>)

Instructions:
00000 READ_TREE		_ws.ftypes.double -> reg#1
00001 IF_FALSE_GOTO	3
00002 SUBRACT		reg#1 - 1 <FT_DOUBLE> -> reg#2
00003 STACK_PUSH	reg#2
00004 CALL_FUNCTION	abs(reg#2) -> reg#0
00005 STACK_POP	1
00006 IF_FALSE_GOTO	24
00007 READ_TREE		_ws.ftypes.double -> reg#1
00008 IF_FALSE_GOTO	9
00009 STACK_PUSH	reg#1
00010 CALL_FUNCTION	abs(reg#1) -> reg#4
00011 STACK_POP	1
00012 IF_FALSE_GOTO	13
00013 STACK_PUSH	reg#4
00014 STACK_PUSH	1 <FT_DOUBLE>
00015 CALL_FUNCTION	abs(1 <FT_DOUBLE>) -> reg#5
00016 STACK_POP	1
00017 IF_FALSE_GOTO	18
00018 STACK_PUSH	reg#5
00019 CALL_FUNCTION	max(reg#5, reg#4) -> reg#3
00020 STACK_POP	2
00021 IF_FALSE_GOTO	24
00022 DIVIDE		reg#0 / reg#3 -> reg#6
00023 ANY_LT		reg#6 < 0.01 <FT_DOUBLE>
00024 RETURN

We now use a stack to pass arguments to the function. The
stack is implemented as a list of lists (list of registers).
Arguments may still be non-existent to functions (this is
a feature). Functions must check for nil arguments (NULL lists)
and handle that case.

It's somewhat complicated to allow literal values and test compatibility
for different types, both because of lack of type information with
unparsed/literal and also because it is an underdeveloped area in the
code. In my limited testing it was good enough and useful, further
enhancements are left for future work.
2022-04-18 17:10:31 +01:00
João Valverde 827d143e6e dfilter: Allow function arguments to be non-existent.
Instead of not calling the function if an argument is non-existent
(read tree fails), call the function and let the function handle
the condition.
2022-04-14 13:07:41 +00:00
João Valverde cb2f085f14 dfilter: Add max() and min() functions
Changes the function calling convention to pass the first register
number plus the number of registers after that sequentially. This
allows function with any number of arguments. Functions can still
only return one value.

Adds max() and min() function to select the maximum/minimum value
from any number of arguments, all of the same type. The functions
accept literals too. The return type is the same as the first argument
(cannot be a literal).
2022-04-14 13:07:41 +00:00
João Valverde 20afbd46ec dfilter: Remove existence test syntax tree nodes
After some experimentation I don't think these two existence tests
belong in the grammar, it's an implementation detail and removing it
might avoid some artificial constraints.
2022-04-05 12:04:37 +01:00
João Valverde fb08c4b4a8 dfilter: Replace bitwise sttype with arithmetic
Most of the bitwise codepaths are just duplicating code for
the arithmetic type. Parse bitwise expressions as arithmetic
instead.
2022-04-05 12:04:37 +01:00
João Valverde 8bc214b5bb dfilter: Add remaining arithmetic integer ops 2022-03-31 16:49:42 +01:00
João Valverde 2a9cb588aa dfilter: Add binary arithmetic (add/subtract)
Add support for display filter binary addition and subtraction.

The grammar is intentionally kept simple for now. The use case
is to add a constant to a protocol field, or (maybe) add two
fields in an expression.

We use signed arithmetic with unsigned numbers, checking for
overflow and casting where necessary to do the conversion.
We could legitimately opt to use traditional modular arithmetic
instead (like C) and if it turns out that that is more useful for
some reason we may want to in the future.

Fixes #15504.
2022-03-31 11:27:34 +01:00
João Valverde 260942e170 dfilter: Refactor macro tree references
This replaces the current macro reference system with
a completely different implementation. Instead of a macro a reference
is a syntax element. A reference is a constant that can be filled
in the dfilter code after compilation from an existing protocol tree.
It is best understood as a field value that can be read from a fixed
tree that is not the frame being filtered. Usually this fixed tree
is the currently selected frame when the filter is applied. This
allows comparing fields in the filtered frame with fields in the
selected frame.

Because the field reference syntax uses the same sigil notation
as a macro we have to use a heuristic to distinguish them:
if the name has a dot it is a field reference, otherwise
it is a macro name.

The reference is synctatically validated at compile time.

There are two main advantages to this implementation (and a couple of
minor ones):

The protocol tree for each selected frame is only walked if we have a
display filter and if the display filter uses references. Also only the
actual reference values are copied, intead of loading the entire tree
into a hash table (in textual form even).

The other advantage is that the reference is tested like a protocol
field against all the values in the selected frame (if there is more
than one).

Currently the reference fields are not "primed" during dissection, so
the entire tree is walked to find a particular reference (this is
similar to the previous implementation).

If the display filter contains a valid reference and the reference is
not loaded at the time the filter is run the result is the same as a
non existing field for a regular READ_TREE instruction.

Fixes #17599.
2022-03-29 12:36:31 +00:00
João Valverde ac0a69636b dfilter: Add support for unary arithmetic
This change implements a unary minus operator.

Filter: tcp.window_size_scalefactor == -tcp.dstport

Instructions:
00000 READ_TREE		tcp.window_size_scalefactor -> reg#0
00001 IF_FALSE_GOTO	6
00002 READ_TREE		tcp.dstport -> reg#1
00003 IF_FALSE_GOTO	6
00004 MK_MINUS		-reg#1 -> reg#2
00005 ANY_EQ		reg#0 == reg#2
00006 RETURN

It is supported for integer types, floats and relative time values.
The unsigned integer types are promoted to a 32 bit signed integer.

Unary plus is implemented as a no-op. The plus sign is simply ignored.

Constant arithmetic expressions are computed during compilation.

Overflow with constants is a compile time error. Overflow with
variables is a run time error and silently ignored. Only a debug
message will be printed to the console.

Related to #15504.
2022-03-28 11:20:41 +00:00
João Valverde 0335ebdc3a dfilter: ftype_is_true -> ftype_is_zero 2022-03-23 11:04:41 +00:00
João Valverde 16729be2c1 dfilter: Add bitwise masking of bits
Add support for masking of bits. Before the bitwise operator
could only test bits, it did not support clearing bits.

This allows testing if any combination of bits are set/unset
more naturally with a single test. Previously this was only
possible by combining several bitwise predicates.

Bitwise is implemented as a test node, even though it is not.
Maybe the test node should be renamed to something else.

Fixes #17246.
2022-03-22 12:58:04 +00:00
João Valverde 54d8627c9a dfilter: Add more comments to optimization pass 2022-03-21 17:36:41 +00:00
João Valverde d60f2580ba dfilter: Pass around constants in instructions
The DFVM instructions arguments are generic boxed types but instead
of using FVALUE and PCRE types the code passes aroung REGISTER types
instead. Change that to pass constants in the instruction.
2022-03-21 17:09:56 +00:00
João Valverde 94d909103e dfilter: Remove DFVM constant initialization 2022-03-21 17:09:43 +00:00
João Valverde ae17e733ac dfilter: Use more DFVM values in gencode 2022-03-21 17:09:29 +00:00
João Valverde 769f1f10de dfilter: Add DFVM value constructor 2022-03-21 17:09:19 +00:00
João Valverde 50f04cb9da dfilter: Remove dead code 2022-03-19 20:10:43 +00:00
João Valverde 588d22a82b dfilter: Allow variable number of jumps during codegen
Use a list to allow a variable number of jumps, instead of a fixed
count. The flexibility in the number of jumps a given syntax tree
node might need to handle is useful to add new kinds of
operations.
2022-03-16 20:12:22 +00:00