pipewire: mem: pw_memblock_map(): fix pointer when reusing mapping

Previously the pointer was determined as follows:

  mm->this.ptr = SPA_PTROFF(m->ptr, range.start, void);

however, when `pw_map_range` is calculated, `pw_map_range::start` is the offset
from the beginning of the first page, starting at `pw_map_range::offset`.

This works correctly if `memblock_map()` runs because that will map the file
with expected offset, so using `range.start` is correct.

However, when a mapping is reused (i.e. `memblock_find_mapping()`) finds something,
then `range.start` is not necessarily correct. Consider the following example:

  * page size is 10
  * one memblock with size 20 (2 pages)
  * the applications wants to mappings:
    * (offset=5,size=10)
    * (offset=15,size=5)

After the first request from the application, a `mapping` object is created
that covers the first two pages of the memblock: offset=0 and size=20. During
the second request, the calculated `pw_map_range` is as follows:

  { start = 5, offset = 10, size = 10 }

and the only previously created mapping is reused since (0 <= 5) and (10 <= 20). When
the pointer of the mapping is adjusted afterwards it will be incorrect since `m->ptr`
points to byte 0 on page 0 (instead of byte 0 on page 1 -- that is assumed). Thereforce
the two will unexpectedly overlap.

Fix that by using `offset - m->offset` when adjusting the mapping's pointer. Also move
the `range` variable into a smaller scope because it only makes sense there. And add
a test that check the above previously incorrect case.

Fixes: 2caf81c97c ("mem: improve memory handling")
Fixes #4884
This commit is contained in:
Barnabás Pőcze 2025-09-08 12:47:10 +02:00 committed by Wim Taymans
parent 93774b1d14
commit 0b08468035
3 changed files with 58 additions and 7 deletions

View file

@ -393,7 +393,6 @@ struct pw_memmap * pw_memblock_map(struct pw_memblock *block,
struct mempool *p = SPA_CONTAINER_OF(block->pool, struct mempool, this);
struct mapping *m;
struct memmap *mm;
struct pw_map_range range;
struct stat sb;
if (b->this.fd == -1) {
@ -418,13 +417,15 @@ struct pw_memmap * pw_memblock_map(struct pw_memblock *block,
return NULL;
}
pw_map_range_init(&range, offset, size, p->pagesize);
m = memblock_find_mapping(b, flags, offset, size);
if (m == NULL)
if (m == NULL) {
struct pw_map_range range;
pw_map_range_init(&range, offset, size, p->pagesize);
m = memblock_map(b, flags, range.offset, range.size);
if (m == NULL)
return NULL;
if (m == NULL)
return NULL;
}
mm = calloc(1, sizeof(struct memmap));
if (mm == NULL) {
@ -439,7 +440,7 @@ struct pw_memmap * pw_memblock_map(struct pw_memblock *block,
mm->this.flags = flags;
mm->this.offset = offset;
mm->this.size = size;
mm->this.ptr = SPA_PTROFF(m->ptr, range.start, void);
mm->this.ptr = SPA_PTROFF(m->ptr, offset - m->offset, void);
pw_log_debug("%p: map:%p block:%p fd:%d flags:%08x ptr:%p (%u %u) mapping:%p ref:%d", p,
&mm->this, b, b->this.fd, b->this.flags, mm->this.ptr, offset, size, m, m->ref);