summaryrefslogtreecommitdiffstats
path: root/lib/cache_maint.c
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-10-01 14:52:55 +0000
committerKumar Kartikeya Dwivedi <memxor@gmail.com>2026-10-01 18:39:27 +0200
commit33a154a96e71a34a1bcca9f40da343dbbf7b38b4 (patch)
tree543be8029ab3d8a3a63994ae23385a053ed30029 /lib/cache_maint.c
downloadlinux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.tar.gz
linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.zip
selftests/bpf: Test packet range of pointers sharing an idgrafted
Add tests where two packet pointers share an id and tightening one pointer's umax from its var_off would put it less than their constant distance from the other's umax: with an index & 0x38 capped at 50, the base pointer keeps umax 50, so the pointer 8 bytes further on must keep umax 58, even though its known bits allow at most 56. These refused a valid program or accepted an out-of-bounds access before the fix: - check the advanced copy, load through the base: valid, was refused; - check the base, load the byte at base + 1 through a copy advanced by 8: was accepted; - check base + 4, load 4 bytes at base + 2 through base + 8: reads two bytes past the checked range, was accepted; - the same as the second with data_meta pointers checked against data: was accepted. These pass with and without the fix and cover nearby paths: - subtract an unknown scalar from a checked pointer and load below it (the range is kept across a new id); - reach a load through two paths whose checks cover 8 and 7 bytes after the loaded pointer; the second path must not be pruned by the first; - spill a copy of a pointer, check the pointer, fill the copy and load one byte past the checked range: the load is refused, and the copy has the range of the check. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20261001145255.855630-2-alexei.starovoitov@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Diffstat (limited to 'lib/cache_maint.c')
-rw-r--r--lib/cache_maint.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/lib/cache_maint.c b/lib/cache_maint.c
new file mode 100644
index 000000000..9256a9ffc
--- /dev/null
+++ b/lib/cache_maint.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic support for Memory System Cache Maintenance operations.
+ *
+ * Coherency maintenance drivers register with this simple framework that will
+ * iterate over each registered instance to first kick off invalidation and
+ * then to wait until it is complete.
+ *
+ * If no implementations are registered yet cpu_cache_has_invalidate_memregion()
+ * will return false. If this runs concurrently with unregistration then a
+ * race exists but this is no worse than the case where the operations instance
+ * responsible for a given memory region has not yet registered.
+ */
+#include <linux/cache_coherency.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/export.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/memregion.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+
+static LIST_HEAD(cache_ops_instance_list);
+static DECLARE_RWSEM(cache_ops_instance_list_lock);
+
+static void __cache_coherency_ops_instance_free(struct kref *kref)
+{
+ struct cache_coherency_ops_inst *cci =
+ container_of(kref, struct cache_coherency_ops_inst, kref);
+ kfree(cci);
+}
+
+void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci)
+{
+ kref_put(&cci->kref, __cache_coherency_ops_instance_free);
+}
+EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put);
+
+static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data)
+{
+ if (!cci->ops)
+ return -EINVAL;
+
+ return cci->ops->wbinv(cci, data);
+}
+
+static int cache_inval_done_one(struct cache_coherency_ops_inst *cci)
+{
+ if (!cci->ops)
+ return -EINVAL;
+
+ if (!cci->ops->done)
+ return 0;
+
+ return cci->ops->done(cci);
+}
+
+static int cache_invalidate_memregion(phys_addr_t addr, size_t size)
+{
+ int ret;
+ struct cache_coherency_ops_inst *cci;
+ struct cc_inval_params params = {
+ .addr = addr,
+ .size = size,
+ };
+
+ guard(rwsem_read)(&cache_ops_instance_list_lock);
+ list_for_each_entry(cci, &cache_ops_instance_list, node) {
+ ret = cache_inval_one(cci, &params);
+ if (ret)
+ return ret;
+ }
+ list_for_each_entry(cci, &cache_ops_instance_list, node) {
+ ret = cache_inval_done_one(cci);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct cache_coherency_ops_inst *
+_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops,
+ size_t size)
+{
+ struct cache_coherency_ops_inst *cci;
+
+ if (!ops || !ops->wbinv)
+ return NULL;
+
+ cci = kzalloc(size, GFP_KERNEL);
+ if (!cci)
+ return NULL;
+
+ cci->ops = ops;
+ INIT_LIST_HEAD(&cci->node);
+ kref_init(&cci->kref);
+
+ return cci;
+}
+EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, "CACHE_COHERENCY");
+
+int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci)
+{
+ guard(rwsem_write)(&cache_ops_instance_list_lock);
+ list_add(&cci->node, &cache_ops_instance_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, "CACHE_COHERENCY");
+
+void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci)
+{
+ guard(rwsem_write)(&cache_ops_instance_list_lock);
+ list_del(&cci->node);
+}
+EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, "CACHE_COHERENCY");
+
+int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len)
+{
+ return cache_invalidate_memregion(start, len);
+}
+EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, "DEVMEM");
+
+/*
+ * Used for optimization / debug purposes only as removal can race
+ *
+ * Machines that do not support invalidation, e.g. VMs, will not have any
+ * operations instance to register and so this will always return false.
+ */
+bool cpu_cache_has_invalidate_memregion(void)
+{
+ guard(rwsem_read)(&cache_ops_instance_list_lock);
+ return !list_empty(&cache_ops_instance_list);
+}
+EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, "DEVMEM");