return count;
}
-static void bpf_stmt(struct sock_filter *ins, unsigned *i,
- unsigned short code, unsigned data) {
+static int bpf_stmt_impl(struct sock_filter *ins, size_t *i, size_t n_ins,
+ unsigned short code, unsigned data) {
+ assert(ins);
assert(i);
+ if (*i >= n_ins)
+ return -E2BIG;
+
ins[(*i)++] = (struct sock_filter) {
.code = code,
.k = data,
};
+ return 0;
}
-static void bpf_jmp(struct sock_filter *ins, unsigned *i,
- unsigned short code, unsigned data,
- unsigned short jt, unsigned short jf) {
+#define bpf_stmt(ins, i, code, data) \
+ bpf_stmt_impl((ins), (i), ELEMENTSOF(ins), (code), (data))
+
+static int bpf_jmp_impl(struct sock_filter *ins, size_t *i, size_t n_ins,
+ unsigned short code, unsigned data,
+ unsigned jt, unsigned jf) {
+ assert(ins);
assert(i);
+ if (*i >= n_ins)
+ return -E2BIG;
+
+ /* The jump offsets are stored in single bytes (struct sock_filter.jt/.jf are __u8). A larger
+ * offset would be silently truncated and make the filter branch to the wrong instruction, i.e.
+ * drop events that should match. */
+ if (jt > UINT8_MAX || jf > UINT8_MAX)
+ return -E2BIG;
+
ins[(*i)++] = (struct sock_filter) {
.code = code,
.jt = jt,
.jf = jf,
.k = data,
};
+ return 0;
}
+#define bpf_jmp(ins, i, code, data, jt, jf) \
+ bpf_jmp_impl((ins), (i), ELEMENTSOF(ins), (code), (data), (jt), (jf))
+
_public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
struct sock_filter ins[512] = {};
struct sock_fprog filter;
const char *subsystem, *devtype, *tag;
- unsigned i = 0;
+ size_t i = 0;
+ int r;
assert_return(m, -EINVAL);
}
/* load magic in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
+ r = bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
/* jump if magic matches */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0));
/* wrong magic, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
if (!set_isempty(m->tag_filter)) {
int tag_matches = set_size(m->tag_filter);
uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
/* load device bloom bits in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_hi));
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_hi)));
/* clear bits (tag bits & bloom bits) */
- bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi));
/* jump to next tag if it does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3));
/* load device bloom bits in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_lo));
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_lo)));
/* clear bits (tag bits & bloom bits) */
- bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo));
/* jump behind end of tag match block if tag matches */
tag_matches--;
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0));
}
/* nothing matched, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0));
}
/* add all subsystem matches */
uint32_t hash = string_hash32(subsystem);
/* load device subsystem value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash));
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash)));
if (!devtype) {
/* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1));
} else {
/* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3));
/* load device devtype value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_devtype_hash));
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_devtype_hash)));
/* jump if value does not match */
hash = string_hash32(devtype);
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1));
}
/* matched, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
-
- if (i+1 >= ELEMENTSOF(ins))
- return -E2BIG;
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
}
/* nothing matched, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0));
}
/* matched, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+ RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
+
+ if (r < 0)
+ return r;
/* install filter */
filter = (struct sock_fprog) {