forked from osmocom/wireshark
![Peter Wu](/assets/img/avatar_default.png)
The WRETH dissector showed up some garbage in the column display. Upon further inspection, it turns out that the format string had a trailing percent sign which caused (unsigned)-1 to be returned by g_printf_string_upper_bound (in emem_strdup_vprintf). Then ep_alloc is called with (unsigned)-1 + 1 = 0 memory, no wonder that garbage shows up. ASAN could not even catch this error because EP is in charge of this. So, start adding G_GNUC_PRINTF annotations in each header that uses the "fmt" or "format" paramters (grepped + awk). This revealed some other errors. The NCP2222 dissector was missing a format string (not a security vuln though). Many dissectors used val_to_str with a constant (but empty) string, these have been replaced by val_to_str_const. ASN.1 dissectors were regenerated for this. Minor: the mate plugin used "%X" instead of "%p" for a pointer type. The ncp2222 dissector and wimax plugin gained modelines. Change-Id: I7f3f6a3136116f9b251719830a39a7b21646f622 Reviewed-on: https://code.wireshark.org/review/2881 Reviewed-by: Evan Huus <eapache@gmail.com>
319 lines
6.9 KiB
Text
319 lines
6.9 KiB
Text
|
|
%include {
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "dfilter-int.h"
|
|
#include "syntax-tree.h"
|
|
#include "sttype-range.h"
|
|
#include "sttype-test.h"
|
|
#include "sttype-function.h"
|
|
#include "drange.h"
|
|
|
|
#include "grammar.h"
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(disable:4671)
|
|
#endif
|
|
|
|
/* End of C code */
|
|
}
|
|
|
|
/* Parser Information */
|
|
%name Dfilter
|
|
%token_prefix TOKEN_
|
|
%extra_argument {dfwork_t *dfw}
|
|
|
|
/* Terminal and Non-Terminal types and destructors */
|
|
%token_type {stnode_t*}
|
|
%token_destructor {
|
|
(void) dfw; /* Mark unused, similar to Q_UNUSED */
|
|
stnode_free($$);
|
|
}
|
|
|
|
%type sentence {stnode_t*}
|
|
|
|
|
|
%type expr {stnode_t*}
|
|
%destructor expr {stnode_free($$);}
|
|
|
|
%type entity {stnode_t*}
|
|
%destructor entity {stnode_free($$);}
|
|
|
|
%type relation_test {stnode_t*}
|
|
%destructor relation_test {stnode_free($$);}
|
|
|
|
%type logical_test {stnode_t*}
|
|
%destructor logical_test {stnode_free($$);}
|
|
|
|
%type rel_op2 {test_op_t}
|
|
|
|
%type range {stnode_t*}
|
|
%destructor range {stnode_free($$);}
|
|
|
|
%type drnode {drange_node*}
|
|
%destructor drnode {drange_node_free($$);}
|
|
|
|
%type drnode_list {GSList*}
|
|
%destructor drnode_list {drange_node_free_list($$);}
|
|
|
|
%type funcparams {GSList*}
|
|
%destructor funcparams {st_funcparams_free($$);}
|
|
|
|
/* This is called as soon as a syntax error happens. After that,
|
|
any "error" symbols are shifted, if possible. */
|
|
%syntax_error {
|
|
|
|
header_field_info *hfinfo;
|
|
|
|
if (!TOKEN) {
|
|
dfilter_fail("Unexpected end of filter string.");
|
|
return;
|
|
}
|
|
|
|
switch(stnode_type_id(TOKEN)) {
|
|
case STTYPE_UNINITIALIZED:
|
|
dfilter_fail("Syntax error.");
|
|
break;
|
|
case STTYPE_TEST:
|
|
dfilter_fail("Syntax error, TEST.");
|
|
break;
|
|
case STTYPE_STRING:
|
|
dfilter_fail("The string \"%s\" was unexpected in this context.",
|
|
(char *)stnode_data(TOKEN));
|
|
break;
|
|
case STTYPE_UNPARSED:
|
|
dfilter_fail("\"%s\" was unexpected in this context.",
|
|
(char *)stnode_data(TOKEN));
|
|
break;
|
|
case STTYPE_INTEGER:
|
|
dfilter_fail("The integer %d was unexpected in this context.",
|
|
stnode_value(TOKEN));
|
|
break;
|
|
case STTYPE_FIELD:
|
|
hfinfo = (header_field_info *)stnode_data(TOKEN);
|
|
dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev);
|
|
break;
|
|
case STTYPE_FUNCTION:
|
|
dfilter_fail("The function s was unexpected in this context.");
|
|
break;
|
|
|
|
/* These aren't handed to use as terminal tokens from
|
|
the scanner, so was can assert that we'll never
|
|
see them here. */
|
|
case STTYPE_NUM_TYPES:
|
|
case STTYPE_RANGE:
|
|
case STTYPE_FVALUE:
|
|
g_assert_not_reached();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* When a parse fails, mark an error. This occurs after
|
|
the above syntax_error code and after the parser fails to
|
|
use error recovery, shifting an "error" symbol and successfully
|
|
shifting 3 more symbols. */
|
|
%parse_failure {
|
|
dfw->syntax_error = TRUE;
|
|
}
|
|
|
|
/* ----------------- The grammar -------------- */
|
|
|
|
/* Associativity */
|
|
%left TEST_AND.
|
|
%left TEST_OR.
|
|
%nonassoc TEST_EQ TEST_NE TEST_LT TEST_LE TEST_GT TEST_GE TEST_CONTAINS TEST_MATCHES TEST_BITWISE_AND.
|
|
%right TEST_NOT.
|
|
|
|
/* Top-level targets */
|
|
sentence ::= expr(X). { dfw->st_root = X; }
|
|
sentence ::= . { dfw->st_root = NULL; }
|
|
|
|
expr(X) ::= relation_test(R). { X = R; }
|
|
expr(X) ::= logical_test(L). { X = L; }
|
|
|
|
|
|
/* Logical tests */
|
|
logical_test(T) ::= expr(E) TEST_AND expr(F).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(T, TEST_OP_AND, E, F);
|
|
}
|
|
|
|
logical_test(T) ::= expr(E) TEST_OR expr(F).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(T, TEST_OP_OR, E, F);
|
|
}
|
|
|
|
logical_test(T) ::= TEST_NOT expr(E).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set1(T, TEST_OP_NOT, E);
|
|
}
|
|
|
|
logical_test(T) ::= entity(E).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set1(T, TEST_OP_EXISTS, E);
|
|
}
|
|
|
|
|
|
|
|
/* Entities, or things that can be compared/tested/checked */
|
|
entity(E) ::= FIELD(F). { E = F; }
|
|
entity(E) ::= STRING(S). { E = S; }
|
|
entity(E) ::= UNPARSED(U). { E = U; }
|
|
entity(E) ::= range(R). { E = R; }
|
|
|
|
|
|
/* Ranges */
|
|
range(R) ::= entity(E) LBRACKET drnode_list(L) RBRACKET.
|
|
{
|
|
R = stnode_new(STTYPE_RANGE, NULL);
|
|
sttype_range_set(R, E, L);
|
|
|
|
/* Delete the list, but not the drange_nodes that
|
|
* the list contains. */
|
|
g_slist_free(L);
|
|
}
|
|
|
|
drnode_list(L) ::= drnode(D).
|
|
{
|
|
L = g_slist_append(NULL, D);
|
|
}
|
|
|
|
drnode_list(L) ::= drnode_list(P) COMMA drnode(D).
|
|
{
|
|
L = g_slist_append(P, D);
|
|
}
|
|
|
|
/* x:y is offset:length */
|
|
drnode(D) ::= INTEGER(X) COLON INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_length(D, stnode_value(Y));
|
|
|
|
stnode_free(X);
|
|
stnode_free(Y);
|
|
}
|
|
|
|
/* x-y == offset:offset */
|
|
drnode(D) ::= INTEGER(X) HYPHEN INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_end_offset(D, stnode_value(Y));
|
|
|
|
stnode_free(X);
|
|
stnode_free(Y);
|
|
}
|
|
|
|
|
|
/* :y == from start to offset */
|
|
drnode(D) ::= COLON INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, 0);
|
|
drange_node_set_length(D, stnode_value(Y));
|
|
|
|
stnode_free(Y);
|
|
}
|
|
|
|
/* x: from offset to end */
|
|
drnode(D) ::= INTEGER(X) COLON.
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_to_the_end(D);
|
|
|
|
stnode_free(X);
|
|
}
|
|
|
|
/* x == x:1 */
|
|
drnode(D) ::= INTEGER(X).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_length(D, 1);
|
|
|
|
stnode_free(X);
|
|
}
|
|
|
|
|
|
|
|
/* Relational tests */
|
|
relation_test(T) ::= entity(E) rel_op2(O) entity(F).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(T, O, E, F);
|
|
}
|
|
|
|
/* 'a == b == c' or 'a < b <= c <= d < e' */
|
|
relation_test(T) ::= entity(E) rel_op2(O) relation_test(R).
|
|
{
|
|
stnode_t *L, *F;
|
|
/* for now generate it like E O F TEST_OP_AND F P G, later it could be optimized
|
|
or semantically checked (to make a <= b >= c or a == b != c invalid)?
|
|
*/
|
|
|
|
F = R;
|
|
do {
|
|
g_assert(F != NULL && stnode_type_id(F) == STTYPE_TEST);
|
|
sttype_test_get(F, NULL, &F, NULL);
|
|
} while (stnode_type_id(F) == STTYPE_TEST);
|
|
|
|
L = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(L, O, E, stnode_dup(F));
|
|
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(T, TEST_OP_AND, L, R);
|
|
}
|
|
|
|
rel_op2(O) ::= TEST_EQ. { O = TEST_OP_EQ; }
|
|
rel_op2(O) ::= TEST_NE. { O = TEST_OP_NE; }
|
|
rel_op2(O) ::= TEST_GT. { O = TEST_OP_GT; }
|
|
rel_op2(O) ::= TEST_GE. { O = TEST_OP_GE; }
|
|
rel_op2(O) ::= TEST_LT. { O = TEST_OP_LT; }
|
|
rel_op2(O) ::= TEST_LE. { O = TEST_OP_LE; }
|
|
rel_op2(O) ::= TEST_BITWISE_AND. { O = TEST_OP_BITWISE_AND; }
|
|
rel_op2(O) ::= TEST_CONTAINS. { O = TEST_OP_CONTAINS; }
|
|
rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; }
|
|
|
|
|
|
/* Functions */
|
|
|
|
/* A function can have one or more parameters */
|
|
entity(E) ::= FUNCTION(F) LPAREN funcparams(P) RPAREN.
|
|
{
|
|
E = F;
|
|
sttype_function_set_params(E, P);
|
|
}
|
|
|
|
/* A function can have zero parameters. */
|
|
entity(E) ::= FUNCTION(F) LPAREN RPAREN.
|
|
{
|
|
E = F;
|
|
}
|
|
|
|
funcparams(P) ::= entity(E).
|
|
{
|
|
P = g_slist_append(NULL, E);
|
|
}
|
|
|
|
funcparams(P) ::= funcparams(L) COMMA entity(E).
|
|
{
|
|
P = g_slist_append(L, E);
|
|
}
|
|
|
|
|
|
/* Any expression inside parens is simply that expression */
|
|
expr(X) ::= LPAREN expr(Y) RPAREN.
|
|
{
|
|
X = Y;
|
|
stnode_set_bracket(X, TRUE);
|
|
}
|
|
|