From 1122b45dfdef5da534cb18da7cb9c688f9a2b528 Mon Sep 17 00:00:00 2001
From: Alice Frosi <afrosi@redhat.com>
Date: Wed, 5 Apr 2023 16:35:08 +0200
Subject: Add support for 64 bits arguments

---
 cooker/filter.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++----------
 cooker/filter.h | 36 ++++++++++++++++++++++++---
 2 files changed, 94 insertions(+), 17 deletions(-)

(limited to 'cooker')

diff --git a/cooker/filter.c b/cooker/filter.c
index 350d6bc..29c2d64 100644
--- a/cooker/filter.c
+++ b/cooker/filter.c
@@ -9,6 +9,7 @@
 
 #include "numbers.h"
 #include "filter.h"
+#include "util.h"
 
 #define N_SYSCALL sizeof(numbers) / sizeof(numbers[0])
 
@@ -157,7 +158,7 @@ static unsigned get_n_args_syscall_entry(const struct bpf_call *entry)
 	unsigned i, n = 0;
 
 	for (i = 0; i < 6; i++)
-		if (entry->check_arg[i])
+		if (entry->args[i].type != NO_CHECK)
 			n++;
 	return n;
 }
@@ -181,22 +182,35 @@ static unsigned int get_n_args_syscall_instr(const struct syscall_entry *table)
 	for (unsigned int i = 0; i < table->count; i++) {
 		entry = table->entry + i;
 		n = 0;
-
-		/* For every argument there are 2 instructions, one to
-		* load the value and the second to evaluate the
-		* argument
-		*/
 		for (unsigned int k = 0; k < 6; k++) {
-			if (entry->check_arg[k]) {
+			switch (entry->args[k].type) {
+			case U32:
+				/* For 32 bit arguments: 2 instructions,
+				 * 1 for loading the value and
+				 * 1 for evaluating the  argument */
 				n += 2;
+				break;
+			case U64:
+				/* For 64 bit arguments: 4 instructions, for
+				 * loading and evaluating the high and low 32
+				 * bits chuncks.
+				*/
+				n += 4;
+				break;
+			case NO_CHECK:
+				break;
 			}
 		}
 		total_instr += n;
+		/* If there at least an argument, then there is the jump to the
+		 * notification */
 		if (n > 0) {
 			has_arg = true;
 			total_instr++;
 		}
 	}
+	/* If there at least an argument for that syscall, then there is the jump to the
+	* accept */
 	if (has_arg)
 		total_instr++;
 
@@ -215,9 +229,12 @@ static unsigned int get_total_args_instr(const struct syscall_entry table[],
 }
 
 static bool check_args_syscall_entry(const struct bpf_call *entry){
-	return entry->check_arg[0] || entry->check_arg[1] ||
-	       entry->check_arg[2] || entry->check_arg[3] ||
-	       entry->check_arg[4] || entry->check_arg[5];
+	return entry->args[0].type != NO_CHECK ||
+	       entry->args[1].type != NO_CHECK ||
+	       entry->args[2].type != NO_CHECK ||
+	       entry->args[3].type != NO_CHECK ||
+	       entry->args[4].type != NO_CHECK ||
+	       entry->args[5].type != NO_CHECK;
 }
 
 static bool check_args_syscall(const struct syscall_entry *table)
@@ -243,6 +260,21 @@ unsigned int create_bpf_program_log(struct sock_filter filter[])
 	return 4;
 }
 
+static unsigned int eq_u64_filter(struct sock_filter filter[], int idx,
+				  uint64_t v, unsigned int jtrue,
+				  unsigned int jfalse)
+{
+	uint32_t hi = get_hi(v);
+	uint32_t lo = get_lo(v);
+
+	filter[0] = (struct sock_filter)LOAD(LO_ARG(idx));
+	filter[1] = (struct sock_filter)EQ(lo, 0, jfalse);
+	filter[2] = (struct sock_filter)LOAD(HI_ARG(idx));
+	filter[3] = (struct sock_filter)EQ(hi, jtrue, jfalse);
+
+	return 4;
+}
+
 unsigned int create_bfp_program(struct syscall_entry table[],
 				struct sock_filter filter[],
 				unsigned int n_syscall)
@@ -320,16 +352,33 @@ unsigned int create_bfp_program(struct syscall_entry table[],
 			entry = table[i].entry + j;
 			next_args_off = get_n_args_syscall_entry(entry);
 			for (k = 0; k < 6; k++) {
-				if (entry->check_arg[k]) {
+				offset = next_args_off - n_checks;
+				switch (entry->args[k].type) {
+				case NO_CHECK:
+					break;
+				case U64:
+					size += eq_u64_filter(
+						&filter[size], k,
+						entry->args[k].value.v64, 0,
+						offset);
+					n_checks++;
+					has_arg = true;
+					break;
+				case U32:
 					filter[size++] = (struct sock_filter)
 						LOAD((offsetof(
 							struct seccomp_data,
 							args[k])));
 					filter[size++] = (struct sock_filter)EQ(
-						(table[i].entry + j)->args[k],
-						0, next_args_off - n_checks);
+						entry->args[k].value.v32, 0,
+						offset);
 					n_checks++;
 					has_arg = true;
+					break;
+				default:
+					fprintf(stderr,
+						"value for args not recognized\n");
+					return -1;
 				}
 			}
 			if (check_args_syscall_entry(table[i].entry))
diff --git a/cooker/filter.h b/cooker/filter.h
index c8e74be..7705414 100644
--- a/cooker/filter.h
+++ b/cooker/filter.h
@@ -1,25 +1,53 @@
 #ifndef FILTER_H_
 #define FILTER_H_
 
+#include <stdint.h>
+
 #include <linux/filter.h>
 #include <linux/audit.h>
 #include <linux/seccomp.h>
 
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define ENDIAN(_lo, _hi) _lo, _hi
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#define get_hi(x) ((uint32_t)((x) >> 32))
+#define get_lo(x) ((uint32_t)((x)&0xffffffff))
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define ENDIAN(_lo, _hi) _hi, _lo
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#define get_lo(x) ((uint32_t)((x) >> 32))
+#define get_hi(x) ((uint32_t)((x)&0xffffffff))
+#else
+#error "Unknown endianness"
+#endif
+
 #define JGE(nr, right, left) \
 	BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (nr), (right), (left))
 #define JUMPA(jump) BPF_JUMP(BPF_JMP | BPF_JA, (jump), 0, 0)
-#define EQ(nr, a1, a2) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (nr), (a1), (a2))
+#define EQ(x, a1, a2) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (x), (a1), (a2))
 #define LOAD(x) BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (x))
-
 #define MAX_FILTER 1024
 
 #define MAX_JUMPS 128
 #define EMPTY -1
 
+enum arg_type { NO_CHECK, U32, U64 };
+
+union arg_value {
+	uint32_t v32;
+	uint64_t v64;
+};
+
+struct arg {
+	enum arg_type type;
+	union arg_value value;
+};
+
 struct bpf_call {
 	char *name;
-	int args[6];
-	bool check_arg[6];
+	struct arg args[6];
 };
 
 struct syscall_entry {
-- 
cgit v1.2.3