diff mbox series

[v2,tracing,1/6] tracing: support > 8 byte array filter predicates

Message ID 1682696089-27937-2-git-send-email-alan.maguire@oracle.com (mailing list archive)
State New
Headers show
Series tracing: additional filter predicates | expand

Commit Message

Alan Maguire April 28, 2023, 3:34 p.m. UTC
For > 8 byte values, allow simple binary '==', '!='
predicates where the user passes in a hex ASCII
representation of the desired value.  The representation
must match the field size exactly, and a simple memory
comparison between predicate and actual value is carried out.
For example:

 cd /sys/kernel/debug/tracing/events/tcp/tcp_receive_reset
 echo "saddr_v6 == 0x00000000000000000000000000000001" > filter

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 kernel/trace/trace_events_filter.c | 54 +++++++++++++++++++++++++++++-
 1 file changed, 53 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 1dad64267878..64f1dfb72cb5 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -67,6 +67,7 @@  enum filter_pred_fn {
 	FILTER_PRED_FN_FUNCTION,
 	FILTER_PRED_FN_,
 	FILTER_PRED_TEST_VISITED,
+	FILTER_PRED_FN_MEMCMP,
 };
 
 struct filter_pred {
@@ -622,8 +623,11 @@  predicate_parse(const char *str, int nr_parens, int nr_preds,
 	kfree(op_stack);
 	kfree(inverts);
 	if (prog_stack) {
-		for (i = 0; prog_stack[i].pred; i++)
+		for (i = 0; prog_stack[i].pred; i++) {
+			if (prog_stack[i].pred->fn_num == FILTER_PRED_FN_MEMCMP)
+				kfree((u8 *)(uintptr_t)(prog_stack[i].pred->val));
 			kfree(prog_stack[i].pred);
+		}
 		kfree(prog_stack);
 	}
 	return ERR_PTR(ret);
@@ -890,6 +894,14 @@  static int filter_pred_function(struct filter_pred *pred, void *event)
 	return pred->op == OP_EQ ? ret : !ret;
 }
 
+static int filter_pred_memcmp(struct filter_pred *pred, void *event)
+{
+	u8 *mem = (u8 *)(event + pred->offset);
+	u8 *cmp = (u8 *)(uintptr_t)(pred->val);
+
+	return (memcmp(mem, cmp, pred->field->size) == 0) ^ pred->not;
+}
+
 /*
  * regex_match_foo - Basic regex callbacks
  *
@@ -1353,6 +1365,8 @@  static int filter_pred_fn_call(struct filter_pred *pred, void *event)
 		return filter_pred_function(pred, event);
 	case FILTER_PRED_TEST_VISITED:
 		return test_pred_visited_fn(pred, event);
+	case FILTER_PRED_FN_MEMCMP:
+		return filter_pred_memcmp(pred, event);
 	default:
 		return 0;
 	}
@@ -1370,6 +1384,7 @@  static int parse_pred(const char *str, void *data,
 	unsigned long size;
 	unsigned long ip;
 	char num_buf[24];	/* Big enough to hold an address */
+	u8 *pred_val;
 	char *field_name;
 	char *name;
 	bool function = false;
@@ -1631,6 +1646,43 @@  static int parse_pred(const char *str, void *data,
 		/* go past the last quote */
 		i++;
 
+	} else if (str[i] == '0' && tolower(str[i + 1]) == 'x' &&
+		   field->size > 8) {
+		/* For sizes > 8 bytes, we store hex bytes for comparison;
+		 * only '==' and '!=' are supported.
+		 * To keep things simple, the predicate value must specify
+		 * a value that matches the field size exactly, with leading
+		 * 0s if necessary.
+		 */
+		if (pred->op != OP_EQ && pred->op != OP_NE) {
+			parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + i);
+			goto err_free;
+		}
+
+		/* skip required 0x */
+		s += 2;
+		i += 2;
+
+		while (isalnum(str[i]))
+			i++;
+
+		len = i - s;
+		if (len != (field->size * 2)) {
+			parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + s);
+			goto err_free;
+		}
+
+		pred_val = kzalloc(field->size, GFP_KERNEL);
+		if (hex2bin(pred_val, str + s, field->size)) {
+			parse_error(pe, FILT_ERR_ILLEGAL_INTVAL, pos + s);
+			kfree(pred_val);
+			goto err_free;
+		}
+		pred->val = (u64)pred_val;
+		pred->fn_num = FILTER_PRED_FN_MEMCMP;
+		if (pred->op == OP_NE)
+			pred->not = 1;
+
 	} else if (isdigit(str[i]) || str[i] == '-') {
 
 		/* Make sure the field is not a string */