diff options
| author | Kumar Kartikeya Dwivedi <memxor@gmail.com> | 2026-09-04 10:43:19 +0200 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-09-04 07:58:36 -0700 |
| commit | 6668ed271eaefaa63e686bdfbedaeb7b8e492722 (patch) | |
| tree | 58e8288fead484f6755ad0815fc74c3742663761 /sound/synth/util_mem.c | |
| download | linux-stable-6668ed271eaefaa63e686bdfbedaeb7b8e492722.tar.gz linux-stable-6668ed271eaefaa63e686bdfbedaeb7b8e492722.zip | |
selftests/bpf: Reject graph kptr use after RCU unlockgrafted
Add a sleepable verifier test that loads a graph-node local kptr in an
explicit RCU read-side critical section, then passes its node to
bpf_rbtree_remove() after the section ends.
Before the verifier fix, the stale NON_OWN_REF flag makes the node look like
a live borrowed reference and the program is accepted. After the fix, the
pointer is demoted without NON_OWN_REF and the graph kfunc argument is
rejected.
Also exercise a graph kptr loaded while a spin lock provides implicit RCU
protection. The pointer must be invalidated when the lock is released, which
guards the required ordering between non-owning-reference invalidation and
RCU demotion.
Update the existing fault-protected load test state description. The
post-unlock pointer no longer carries NON_OWN_REF, but remains readable
because the load is rewritten to use BPF_PROBE_MEM.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20260904084325.52250-7-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'sound/synth/util_mem.c')
| -rw-r--r-- | sound/synth/util_mem.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c new file mode 100644 index 000000000..21893e561 --- /dev/null +++ b/sound/synth/util_mem.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> + * + * Generic memory management routines for soundcard memory allocation + */ + +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/util_mem.h> + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); +MODULE_LICENSE("GPL"); + +#define get_memblk(p) list_entry(p, struct snd_util_memblk, list) + +/* + * create a new memory manager + */ +struct snd_util_memhdr * +snd_util_memhdr_new(int memsize) +{ + struct snd_util_memhdr *hdr; + + hdr = kzalloc_obj(*hdr); + if (hdr == NULL) + return NULL; + hdr->size = memsize; + mutex_init(&hdr->block_mutex); + INIT_LIST_HEAD(&hdr->block); + + return hdr; +} + +/* + * free a memory manager + */ +void snd_util_memhdr_free(struct snd_util_memhdr *hdr) +{ + struct list_head *p; + + if (!hdr) + return; + /* release all blocks */ + while ((p = hdr->block.next) != &hdr->block) { + list_del(p); + kfree(get_memblk(p)); + } + kfree(hdr); +} + +/* + * allocate a memory block (without mutex) + */ +struct snd_util_memblk * +__snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) +{ + struct snd_util_memblk *blk; + unsigned int units, prev_offset; + struct list_head *p; + + if (snd_BUG_ON(!hdr || size <= 0)) + return NULL; + + /* word alignment */ + units = size; + if (units & 1) + units++; + if (units > hdr->size) + return NULL; + + /* look for empty block */ + prev_offset = 0; + list_for_each(p, &hdr->block) { + blk = get_memblk(p); + if (blk->offset - prev_offset >= units) + goto __found; + prev_offset = blk->offset + blk->size; + } + if (hdr->size - prev_offset < units) + return NULL; + +__found: + return __snd_util_memblk_new(hdr, units, p->prev); +} + + +/* + * create a new memory block with the given size + * the block is linked next to prev + */ +struct snd_util_memblk * +__snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units, + struct list_head *prev) +{ + struct snd_util_memblk *blk; + + blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size, + GFP_KERNEL); + if (blk == NULL) + return NULL; + + if (prev == &hdr->block) + blk->offset = 0; + else { + struct snd_util_memblk *p = get_memblk(prev); + blk->offset = p->offset + p->size; + } + blk->size = units; + list_add(&blk->list, prev); + hdr->nblocks++; + hdr->used += units; + return blk; +} + + +/* + * allocate a memory block (with mutex) + */ +struct snd_util_memblk * +snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) +{ + guard(mutex)(&hdr->block_mutex); + return __snd_util_mem_alloc(hdr, size); +} + + +/* + * remove the block from linked-list and free resource + * (without mutex) + */ +void +__snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) +{ + list_del(&blk->list); + hdr->nblocks--; + hdr->used -= blk->size; + kfree(blk); +} + +/* + * free a memory block (with mutex) + */ +int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) +{ + if (snd_BUG_ON(!hdr || !blk)) + return -EINVAL; + + guard(mutex)(&hdr->block_mutex); + __snd_util_mem_free(hdr, blk); + return 0; +} + +/* + * return available memory size + */ +int snd_util_mem_avail(struct snd_util_memhdr *hdr) +{ + guard(mutex)(&hdr->block_mutex); + return hdr->size - hdr->used; +} + + +EXPORT_SYMBOL(snd_util_memhdr_new); +EXPORT_SYMBOL(snd_util_memhdr_free); +EXPORT_SYMBOL(snd_util_mem_alloc); +EXPORT_SYMBOL(snd_util_mem_free); +EXPORT_SYMBOL(snd_util_mem_avail); +EXPORT_SYMBOL(__snd_util_mem_alloc); +EXPORT_SYMBOL(__snd_util_mem_free); +EXPORT_SYMBOL(__snd_util_memblk_new); |
