wireshark/epan/dfilter/dfunctions.c
João Valverde 6bdc85e37f dfilter: Reject constant expressions
Constant logical expressions are tautologies and almost certainly
user error. Reject them as invalid.

Most of them were already rejected with insufficient type information
but some corner cases were still valid.

Before:

    Filter: ${frame.number} == 3

    Syntax tree:
     0 TEST_ANY_EQ:
       1 REFERENCE(frame.number <FT_UINT32>)
       1 FVALUE(3 <FT_UINT32>)

    Instructions:
    00000 READ_REFERENCE	${frame.number <FT_UINT32>} -> reg#0
    00001 IF_FALSE_GOTO	3
    00002 ANY_EQ		reg#0 == 3 <FT_UINT32>
    00003 RETURN

After:

    Filter: ${frame.number} == 3
    dftest: Constant expression is invalid.
    	${frame.number} == 3
    	^~~~~~~~~~~~~~~~~~~~
2022-12-30 18:46:22 +00:00

513 lines
14 KiB
C

/*
* Wireshark - Network traffic analyzer
*
* Copyright 2006 Gilbert Ramirez <gram@alumni.rice.edu>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#define WS_LOG_DOMAIN LOG_DOMAIN_DFILTER
#include <glib.h>
#include "dfilter-int.h"
#include "dfunctions.h"
#include "sttype-field.h"
#include "semcheck.h"
#include <string.h>
#include <ftypes/ftypes.h>
#include <epan/exceptions.h>
#include <wsutil/ws_assert.h>
#define FAIL(dfw, node, ...) \
do { \
ws_noisy("Semantic check failed here."); \
dfilter_fail_throw(dfw, DF_ERROR_GENERIC, stnode_location(node), __VA_ARGS__); \
} while (0)
/* Convert an FT_STRING using a callback function */
static gboolean
string_walk(GSList *args, guint32 arg_count, GSList **retval, gchar(*conv_func)(gchar))
{
GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *new_ft_string;
const wmem_strbuf_t *src;
wmem_strbuf_t *dst;
ws_assert(arg_count == 1);
arg1 = args->data;
if (arg1 == NULL)
return FALSE;
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
/* XXX - it would be nice to handle FT_TVBUFF, too */
if (IS_FT_STRING(fvalue_type_ftenum(arg_fvalue))) {
src = fvalue_get_strbuf(arg_fvalue);
dst = wmem_strbuf_new_sized(NULL, src->len);
for (size_t i = 0; i < src->len; i++) {
wmem_strbuf_append_c(dst, conv_func(src->str[i]));
}
new_ft_string = fvalue_new(FT_STRING);
fvalue_set_strbuf(new_ft_string, dst);
*retval = g_slist_prepend(*retval, new_ft_string);
}
arg1 = arg1->next;
}
return TRUE;
}
/* dfilter function: lower() */
static gboolean
df_func_lower(GSList *args, guint32 arg_count, GSList **retval)
{
return string_walk(args, arg_count, retval, g_ascii_tolower);
}
/* dfilter function: upper() */
static gboolean
df_func_upper(GSList *args, guint32 arg_count, GSList **retval)
{
return string_walk(args, arg_count, retval, g_ascii_toupper);
}
/* dfilter function: count() */
static gboolean
df_func_count(GSList *args, guint32 arg_count, GSList **retval)
{
GSList *arg1;
fvalue_t *ft_ret;
guint32 num_items;
ws_assert(arg_count == 1);
arg1 = args->data;
if (arg1 == NULL)
return FALSE;
num_items = (guint32)g_slist_length(arg1);
ft_ret = fvalue_new(FT_UINT32);
fvalue_set_uinteger(ft_ret, num_items);
*retval = g_slist_prepend(*retval, ft_ret);
return TRUE;
}
/* dfilter function: string() */
static gboolean
df_func_string(GSList *args, guint32 arg_count, GSList **retval)
{
GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *new_ft_string;
char *s;
ws_assert(arg_count == 1);
arg1 = args->data;
if (arg1 == NULL)
return FALSE;
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
switch (fvalue_type_ftenum(arg_fvalue))
{
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT40:
case FT_UINT48:
case FT_UINT56:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT32:
case FT_INT40:
case FT_INT48:
case FT_INT56:
case FT_INT64:
case FT_IPv4:
case FT_IPv6:
case FT_FLOAT:
case FT_DOUBLE:
case FT_ETHER:
case FT_FRAMENUM:
case FT_AX25:
case FT_IPXNET:
case FT_GUID:
case FT_OID:
case FT_EUI64:
case FT_VINES:
case FT_REL_OID:
case FT_SYSTEM_ID:
case FT_FCWWN:
case FT_IEEE_11073_SFLOAT:
case FT_IEEE_11073_FLOAT:
s = fvalue_to_string_repr(NULL, arg_fvalue, FTREPR_DFILTER, BASE_NONE);
/* Ensure we have an allocated string here */
if (!s)
s = wmem_strdup(NULL, "");
break;
default:
return TRUE;
}
new_ft_string = fvalue_new(FT_STRING);
fvalue_set_string(new_ft_string, s);
wmem_free(NULL, s);
*retval = g_slist_prepend(*retval, new_ft_string);
arg1 = arg1->next;
}
return TRUE;
}
static gboolean
df_func_compare(GSList *args, guint32 arg_count, GSList **retval,
gboolean (*fv_cmp)(const fvalue_t *a, const fvalue_t *b))
{
fvalue_t *fv_ret = NULL;
GSList *l1, *l2;
guint32 i;
for (l1 = args, i = 0; i < arg_count; l1 = l1->next, i++) {
for (l2 = l1->data; l2 != NULL; l2 = l2->next) {
if (fv_ret == NULL || fv_cmp(l2->data, fv_ret)) {
fv_ret = l2->data;
}
}
}
if (fv_ret == NULL)
return FALSE;
*retval = g_slist_append(NULL, fvalue_dup(fv_ret));
return TRUE;
}
/* Find maximum value. */
static gboolean
df_func_max(GSList *args, guint32 arg_count, GSList **retval)
{
return df_func_compare(args, arg_count, retval, fvalue_gt);
}
/* Find minimum value. */
static gboolean
df_func_min(GSList *args, guint32 arg_count, GSList **retval)
{
return df_func_compare(args, arg_count, retval, fvalue_lt);
}
static gboolean
df_func_abs(GSList *args, guint32 arg_count, GSList **retval)
{
GSList *arg1;
fvalue_t *fv_arg, *new_fv;
char *err_msg = NULL;
GSList *result = NULL;
ws_assert(arg_count == 1);
arg1 = args->data;
if (arg1 == NULL)
return FALSE;
while (arg1) {
fv_arg = arg1->data;
if (fvalue_is_negative(fv_arg)) {
new_fv = fvalue_unary_minus(fv_arg, &err_msg);
if (new_fv == NULL) {
ws_debug("abs: %s", err_msg);
g_free(err_msg);
err_msg = NULL;
}
}
else {
new_fv = fvalue_dup(fv_arg);
}
result = g_slist_prepend(result, new_fv);
arg1 = arg1->next;
}
if (g_slist_length(result) == 0)
return FALSE;
*retval = result;
return TRUE;
}
/* For upper() and lower() checks that the parameter passed to
* it is an FT_STRING */
static ftenum_t
ul_semcheck_is_field_string(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype _U_,
GSList *param_list, df_loc_t func_loc _U_)
{
header_field_info *hfinfo;
ws_assert(g_slist_length(param_list) == 1);
stnode_t *st_node = param_list->data;
if (stnode_type_id(st_node) == STTYPE_FIELD) {
dfw->field_count++;
hfinfo = sttype_field_hfinfo(st_node);
if (IS_FT_STRING(hfinfo->type)) {
return FT_STRING;
}
}
FAIL(dfw, st_node, "Only string type fields can be used as parameter for %s()", func_name);
}
static ftenum_t
ul_semcheck_is_field(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype _U_,
GSList *param_list, df_loc_t func_loc _U_)
{
ws_assert(g_slist_length(param_list) == 1);
stnode_t *st_node = param_list->data;
if (stnode_type_id(st_node) == STTYPE_FIELD) {
dfw->field_count++;
return FT_UINT32;
}
FAIL(dfw, st_node, "Only fields can be used as parameter for %s()", func_name);
}
static ftenum_t
ul_semcheck_string_param(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype _U_,
GSList *param_list, df_loc_t func_loc _U_)
{
header_field_info *hfinfo;
ws_assert(g_slist_length(param_list) == 1);
stnode_t *st_node = param_list->data;
if (stnode_type_id(st_node) == STTYPE_FIELD) {
dfw->field_count++;
hfinfo = sttype_field_hfinfo(st_node);
switch (hfinfo->type) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT40:
case FT_UINT48:
case FT_UINT56:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT32:
case FT_INT40:
case FT_INT48:
case FT_INT56:
case FT_INT64:
case FT_IPv4:
case FT_IPv6:
case FT_FLOAT:
case FT_DOUBLE:
case FT_ETHER:
case FT_FRAMENUM:
case FT_AX25:
case FT_IPXNET:
case FT_GUID:
case FT_OID:
case FT_EUI64:
case FT_VINES:
case FT_REL_OID:
case FT_SYSTEM_ID:
case FT_FCWWN:
case FT_IEEE_11073_SFLOAT:
case FT_IEEE_11073_FLOAT:
return FT_STRING;
default:
break;
}
FAIL(dfw, st_node, "String conversion for field \"%s\" is not supported", hfinfo->abbrev);
}
FAIL(dfw, st_node, "Only fields can be used as parameter for %s()", func_name);
}
/* 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;
ftype = lhs_ftype;
for (l = param_list; l != NULL; l = l->next) {
arg = l->data;
type = stnode_type_id(arg);
if (type == STTYPE_ARITHMETIC) {
ft_arg = check_arithmetic(dfw, arg, ftype);
}
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 (type == STTYPE_FUNCTION) {
ft_arg = check_function(dfw, arg, ftype);
}
else if (type == STTYPE_FIELD) {
dfw->field_count++;
ft_arg = sttype_field_ftenum(arg);
}
else if (type == STTYPE_REFERENCE) {
ft_arg = sttype_field_ftenum(arg);
}
else {
FAIL(dfw, arg, "Argument '%s' is not valid for %s()",
stnode_todisplay(arg), func_name);
}
if (ftype == FT_NONE) {
ftype = ft_arg;
}
if (ft_arg != FT_NONE && ftype != FT_NONE && !compatible_ftypes(ft_arg, ftype)) {
FAIL(dfw, arg, "Arguments to '%s' must be type compatible (expected %s, got %s)",
func_name, ftype_name(ftype), ftype_name(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;
}
static ftenum_t
ul_semcheck_absolute_value(dfwork_t *dfw, const char *func_name, ftenum_t lhs_ftype,
GSList *param_list, df_loc_t func_loc _U_)
{
ws_assert(g_slist_length(param_list) == 1);
stnode_t *st_node;
ftenum_t ftype;
fvalue_t *fv;
st_node = param_list->data;
if (stnode_type_id(st_node) == STTYPE_ARITHMETIC) {
ftype = check_arithmetic(dfw, st_node, lhs_ftype);
}
else if (stnode_type_id(st_node) == STTYPE_LITERAL) {
if (lhs_ftype != FT_NONE) {
/* Convert RHS literal to the same ftype as LHS. */
fv = dfilter_fvalue_from_literal(dfw, lhs_ftype, st_node, FALSE, NULL);
stnode_replace(st_node, STTYPE_FVALUE, fv);
ftype = fvalue_type_ftenum(fv);
}
else {
FAIL(dfw, st_node, "Need a field or field-like value on the LHS.");
}
}
else if (stnode_type_id(st_node) == STTYPE_FUNCTION) {
ftype = check_function(dfw, st_node, lhs_ftype);
}
else if (stnode_type_id(st_node) == STTYPE_FIELD) {
dfw->field_count++;
ftype = sttype_field_ftenum(st_node);
}
else {
ftype = FT_NONE;
}
if (ftype == FT_NONE) {
FAIL(dfw, st_node, "Type %s is not valid for %s",
stnode_type_name(st_node), func_name);
}
if (!ftype_can_is_negative(ftype)) {
FAIL(dfw, st_node, "'%s' is not a valid argument to '%s'()",
stnode_todisplay(st_node), func_name);
}
return ftype;
}
/* The table of all display-filter functions */
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 },
/* 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 },
{ "min", df_func_min, 1, 0, ul_semcheck_compare },
{ "abs", df_func_abs, 1, 1, ul_semcheck_absolute_value },
{ NULL, NULL, 0, 0, NULL }
};
/* Lookup a display filter function record by name */
df_func_def_t*
df_func_lookup(const char *name)
{
df_func_def_t *func_def;
func_def = df_functions;
while (func_def->name != NULL) {
if (strcmp(func_def->name, name) == 0) {
return func_def;
}
func_def++;
}
return NULL;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/