mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
mem: track fd invalidation of imported memblocks
When a memblock closes its fd, have it emit signal to other memblocks possibly sharing the fd, so they know it is now invalid. When a memblock has an invalidated fd, it'll not allow mapping more memory, nor match against fd searches. This avoids bugs where kernel fd reuse causes already invalid memblocks to be used. Generally, higher level code should make sure memblocks get freed in right order so that this situation does not occur, but try to keep mempool in consistent state regardless.
This commit is contained in:
parent
6ab86209f2
commit
f1d2d40748
1 changed files with 87 additions and 1 deletions
|
|
@ -78,6 +78,9 @@ static inline int memfd_create(const char *name, unsigned int flags)
|
|||
#define pw_mempool_emit_added(p,b) pw_mempool_emit(p, added, 0, b)
|
||||
#define pw_mempool_emit_removed(p,b) pw_mempool_emit(p, removed, 0, b)
|
||||
|
||||
#define memblock_emit(b,m,v,...) spa_hook_list_call(&b->listener_list, struct memblock_events, m, v, ##__VA_ARGS__)
|
||||
#define memblock_emit_invalidated(b) memblock_emit(b, invalidated, 0)
|
||||
|
||||
struct mempool {
|
||||
struct pw_mempool this;
|
||||
|
||||
|
|
@ -93,6 +96,15 @@ struct memblock {
|
|||
struct spa_list link; /* link in mempool */
|
||||
struct spa_list mappings; /* list of struct mapping */
|
||||
struct spa_list memmaps; /* list of struct memmap */
|
||||
struct memblock *owner; /* owner of fd, if another memblock */
|
||||
struct spa_hook owner_listener; /* listen for fd owner memblock events */
|
||||
struct spa_hook_list listener_list;
|
||||
};
|
||||
|
||||
struct memblock_events {
|
||||
#define VERSION_MEMBLOCK_EVENTS 0
|
||||
uint32_t version;
|
||||
void (*invalidated) (void *data);
|
||||
};
|
||||
|
||||
/* a mapped region of a block */
|
||||
|
|
@ -285,6 +297,11 @@ static struct mapping * memblock_map(struct memblock *b,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (b->this.fd == -1) {
|
||||
pw_log_error("%p: block:%p cannot map memory with stale fd", p, b);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = mmap(NULL, size, prot, fl, b->this.fd, offset);
|
||||
if (ptr == MAP_FAILED) {
|
||||
|
|
@ -347,6 +364,12 @@ struct pw_memmap * pw_memblock_map(struct pw_memblock *block,
|
|||
struct pw_map_range range;
|
||||
struct stat sb;
|
||||
|
||||
if (b->this.fd == -1) {
|
||||
pw_log_error("%p: block:%p cannot map memory with stale fd", p, block);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat(b->this.fd, &sb) != 0)
|
||||
return NULL;
|
||||
|
||||
|
|
@ -482,6 +505,7 @@ struct pw_memblock * pw_mempool_alloc(struct pw_mempool *pool, enum pw_memblock_
|
|||
b->this.size = size;
|
||||
spa_list_init(&b->mappings);
|
||||
spa_list_init(&b->memmaps);
|
||||
spa_hook_list_init(&b->listener_list);
|
||||
|
||||
#ifdef HAVE_MEMFD_CREATE
|
||||
char name[128];
|
||||
|
|
@ -567,6 +591,9 @@ static struct memblock * mempool_find_fd(struct pw_mempool *pool, int fd)
|
|||
struct memblock *b;
|
||||
|
||||
spa_list_for_each(b, &impl->blocks, link) {
|
||||
if (b->this.fd == -1)
|
||||
continue;
|
||||
|
||||
if (fd == b->this.fd) {
|
||||
pw_log_debug("%p: found %p id:%u fd:%d ref:%d",
|
||||
pool, &b->this, b->this.id, fd, b->this.ref);
|
||||
|
|
@ -583,6 +610,12 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool,
|
|||
struct mempool *impl = SPA_CONTAINER_OF(pool, struct mempool, this);
|
||||
struct memblock *b;
|
||||
|
||||
if (fd < 0) {
|
||||
pw_log_error("%p: cannot import invalid fd", pool);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b = mempool_find_fd(pool, fd);
|
||||
if (b != NULL) {
|
||||
b->this.ref++;
|
||||
|
|
@ -595,6 +628,7 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool,
|
|||
|
||||
spa_list_init(&b->memmaps);
|
||||
spa_list_init(&b->mappings);
|
||||
spa_hook_list_init(&b->listener_list);
|
||||
|
||||
b->this.ref = 1;
|
||||
b->this.pool = pool;
|
||||
|
|
@ -613,15 +647,57 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool,
|
|||
return &b->this;
|
||||
}
|
||||
|
||||
static void memblock_invalidated(void *data)
|
||||
{
|
||||
struct memblock *b = data;
|
||||
|
||||
if (!b->owner)
|
||||
return;
|
||||
|
||||
pw_log_debug("%p: invalidated block:%p id:%u fd:%d ref:%d owner:%p",
|
||||
b->this.pool, b, b->this.id, b->this.fd, b->this.ref, b->owner);
|
||||
|
||||
spa_hook_remove(&b->owner_listener);
|
||||
b->owner = NULL;
|
||||
|
||||
b->this.fd = -1;
|
||||
}
|
||||
|
||||
static const struct memblock_events memblock_events = {
|
||||
VERSION_MEMBLOCK_EVENTS,
|
||||
.invalidated = memblock_invalidated,
|
||||
};
|
||||
|
||||
SPA_EXPORT
|
||||
struct pw_memblock * pw_mempool_import_block(struct pw_mempool *pool,
|
||||
struct pw_memblock *mem)
|
||||
{
|
||||
struct pw_memblock *block;
|
||||
struct memblock *b;
|
||||
|
||||
pw_log_debug("%p: import block:%p type:%d fd:%d", pool,
|
||||
mem, mem->type, mem->fd);
|
||||
return pw_mempool_import(pool,
|
||||
|
||||
block = pw_mempool_import(pool,
|
||||
mem->flags | PW_MEMBLOCK_FLAG_DONT_CLOSE,
|
||||
mem->type, mem->fd);
|
||||
if (!block)
|
||||
return NULL;
|
||||
|
||||
b = SPA_CONTAINER_OF(block, struct memblock, this);
|
||||
if (!b->owner) {
|
||||
struct memblock *bmem = SPA_CONTAINER_OF(mem, struct memblock, this);
|
||||
|
||||
while (bmem->owner)
|
||||
bmem = bmem->owner;
|
||||
|
||||
if (!(bmem->this.flags & PW_MEMBLOCK_FLAG_DONT_CLOSE)) {
|
||||
b->owner = bmem;
|
||||
spa_hook_list_append(&bmem->listener_list, &b->owner_listener, &memblock_events, b);
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
SPA_EXPORT
|
||||
|
|
@ -725,6 +801,13 @@ void pw_memblock_free(struct pw_memblock *block)
|
|||
if (!SPA_FLAG_IS_SET(block->flags, PW_MEMBLOCK_FLAG_DONT_NOTIFY))
|
||||
pw_mempool_emit_removed(impl, block);
|
||||
|
||||
if (b->owner) {
|
||||
spa_hook_remove(&b->owner_listener);
|
||||
b->owner = NULL;
|
||||
}
|
||||
|
||||
memblock_emit_invalidated(b);
|
||||
|
||||
spa_list_consume(mm, &b->memmaps, link)
|
||||
pw_memmap_free(&mm->this);
|
||||
|
||||
|
|
@ -737,6 +820,9 @@ void pw_memblock_free(struct pw_memblock *block)
|
|||
pw_log_debug("%p: close fd:%d", pool, block->fd);
|
||||
close(block->fd);
|
||||
}
|
||||
|
||||
spa_hook_list_clean(&b->listener_list);
|
||||
|
||||
free(b);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue