[PATCH RFC 9/9] filter_seccomp: optimize binary match

Paul Chaignon paul.chaignon at gmail.com
Fri Aug 23 09:44:47 UTC 2019


This commit optimizes the seccomp-filter generation strategy introduced in
the previous commit, to generate shorter programs.  Sequences of direct
jumps to RET_ALLOW and RET_TRACE (in case of all-0 or all-1 bit arrays)
are squashed into a couple of inequalities (conditional jge jumps).

This optimization tends to generate shorter programs only when there are
few or many traced syscalls.

filter_seccomp.c (bpf_syscalls_match): Optimize sequences of all-0 and
all-1 bit arrays.
(binary_match_filter_generator): Use new bpf_syscalls_match argument.

Signed-off-by: Paul Chaignon <paul.chaignon at gmail.com>
---
 filter_seccomp.c | 101 ++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 83 insertions(+), 18 deletions(-)

diff --git a/filter_seccomp.c b/filter_seccomp.c
index e5bc2003..f4296408 100644
--- a/filter_seccomp.c
+++ b/filter_seccomp.c
@@ -354,30 +354,89 @@ reverse_linear_filter_generator(struct sock_filter *filter, bool *overflow) {
 
 static unsigned short
 bpf_syscalls_match(struct sock_filter *filter, unsigned int bitarray,
-		   unsigned int bitarray_idx)
+		   unsigned int bitarray_idx, unsigned int *lower_idx,
+		   unsigned char *previous_ret, bool end)
 {
-	if (!bitarray) {
-		/* return RET_ALLOW; */
-		SET_BPF_JUMP(filter, BPF_JMP | BPF_JEQ | BPF_K, bitarray_idx,
-			     JMP_PLACEHOLDER_ALLOW, 0);
-		return 1;
-	}
-	if (bitarray == UINT_MAX) {
-		/* return RET_TRACE; */
-		SET_BPF_JUMP(filter, BPF_JMP | BPF_JEQ | BPF_K, bitarray_idx,
-			     JMP_PLACEHOLDER_TRACE, 0);
-		return 1;
+	unsigned short nb_insns = 0;
+	unsigned char ret = 0;
+	if (!bitarray || bitarray == UINT_MAX)
+		ret = bitarray ? JMP_PLACEHOLDER_TRACE : JMP_PLACEHOLDER_ALLOW;
+
+	/*
+	 * As a first optimization, we can replace bitwise AND on all-1 and
+	 * all-0 bitarrays with direct jumps such that:
+	 *   if (A == bitarray_idx) return ret;
+	 *
+	 * We can do better though and replace continuous sequences of such
+	 * direct jumps with inequalities, such that:
+	 *   if (A >= *lower_idx && A < bitarray_idx) return *previous_ret;
+	 * where *lower_idx is the first arraybit_idx compared to in the
+	 * sequence and *previous_ret is the destination of the jumps from that
+	 * sequence.
+	 */
+
+	if (!end && (!bitarray || bitarray == UINT_MAX) && *previous_ret == ret)
+		/* We're in the middle of a sequence. */
+		return 0;
+
+	/* Check if previous syscalls_match was the last of a sequence. */
+	if (end || *previous_ret != ret) {
+		/*
+		 * If the last direct jump in the sequence is also the last
+		 * bitarray, we want to include it in the inequality.
+		 */
+		if (end && *previous_ret == ret)
+			bitarray_idx++;
+
+		if (*previous_ret && *lower_idx + 1 == bitarray_idx) {
+			/* if (A == *lower_idx) return *previous_ret; */
+			SET_BPF_JUMP(filter, BPF_JEQ | BPF_K, *lower_idx,
+				     *previous_ret, 0);
+			nb_insns++;
+		} else if (*previous_ret) {
+			/*
+			 * if (A >= *lower_idx && A < bitarray_idx)
+			 *   return *previous_ret;
+			 */
+			SET_BPF_JUMP(filter, BPF_JGE | BPF_K, *lower_idx, 0, 1);
+			SET_BPF_JUMP(filter + 1, BPF_JGE | BPF_K, bitarray_idx,
+				     0, *previous_ret);
+			nb_insns += 2;
+		}
+
+		if (nb_insns)
+			filter = &filter[nb_insns];
+
+		if (!bitarray || bitarray == UINT_MAX) {
+			/* We are starting a new sequence. */
+			*previous_ret = ret;
+			*lower_idx = bitarray_idx;
+			/*
+			 * If last bitarray is starting this sequence, we want
+			 * to generate the instruction now.
+			 */
+			if (end && *previous_ret == ret) {
+				/* if (A == bitarray_idx) return ret; */
+				SET_BPF_JUMP(filter, BPF_JEQ | BPF_K,
+					     bitarray_idx, ret, 0);
+				nb_insns++;
+			}
+			return nb_insns;
+		}
 	}
+
+	*previous_ret = 0;
+
 	/*
-	 * if (A == nr / 32)
+	 * if (A == bitarray_idx)
 	 *   return (X & bitarray) ? RET_TRACE : RET_ALLOW;
 	 */
-	SET_BPF_JUMP(filter, BPF_JMP | BPF_JEQ | BPF_K, bitarray_idx,
+	SET_BPF_JUMP(filter, BPF_JEQ | BPF_K, bitarray_idx,
 		     0, 2);
 	SET_BPF_STMT(filter + 1, BPF_MISC | BPF_TXA, 0);
-	SET_BPF_JUMP(filter + 2, BPF_JMP | BPF_JSET | BPF_K, bitarray,
+	SET_BPF_JUMP(filter + 2, BPF_JSET | BPF_K, bitarray,
 		     JMP_PLACEHOLDER_TRACE, JMP_PLACEHOLDER_ALLOW);
-	return 3;
+	return nb_insns + 3;
 }
 
 static unsigned short
@@ -400,7 +459,9 @@ binary_match_filter_generator(struct sock_filter *filter, bool *overflow)
 		 p >= 0 && pos <= BPF_MAXINSNS;
 		 --p) {
 		unsigned short start = pos, end;
+		unsigned char last_jump = 0;
 		unsigned int bitarray = 0;
+		unsigned int lower_idx = 0;
 		unsigned int i;
 
 #if SUPPORTED_PERSONALITIES > 1
@@ -446,14 +507,18 @@ binary_match_filter_generator(struct sock_filter *filter, bool *overflow)
 			if (traced_by_seccomp(i, p))
 				bitarray |= (1 << i % 32);
 			if (i % 32 == 31) {
+				bool end = i + 1 == nsyscall_vec[p];
 				pos += bpf_syscalls_match(filter + pos,
-							  bitarray, i / 32);
+							  bitarray, i / 32,
+							  &lower_idx,
+							  &last_jump, end);
 				bitarray = 0;
 			}
 		}
 		if (i % 32 != 0)
 			pos += bpf_syscalls_match(filter + pos, bitarray,
-						  i / 32);
+						  i / 32, &lower_idx,
+						  &last_jump, true);
 
 		end = pos;
 
-- 
2.17.1



More information about the Strace-devel mailing list