mem: don't PROT_WRITE on reservation mmaps

An application might call mmap with PROT_NONE to reserve a large amount
of the virtual address space. Something like:

void *addr = mmap(NULL, SIZE, PROT_NONE,
	      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

During restore, when we have to premap private vmas (ie pieok is false),
CRIU tries to mmap this large PROT_NONE and fails.

If a vma has never been written to, it does not have "ac" flag set. Use
this fact to safely premap vmas that were done for address reservation.

Suggested-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Bhavik Sachdev <b.sachdev1904@gmail.com>
This commit is contained in:
Bhavik Sachdev 2026-04-23 18:38:03 +05:30 committed by Andrei Vagin
parent 2c9ee0abd2
commit 7daaa11aa0
5 changed files with 33 additions and 11 deletions

View file

@ -56,6 +56,7 @@ status = {
"VMA_AREA_AIORING": 1 << 13,
"VMA_AREA_MEMFD": 1 << 14,
"VMA_AREA_UPROBES": 1 << 17,
"VMA_AREA_NOT_ACCOUNTABLE": 1 << 18,
"VMA_AREA_UNSUPP": 1 << 31
}

View file

@ -101,6 +101,7 @@
#define VMA_AREA_SHSTK (1 << 15)
#define VMA_AREA_GUARD (1 << 16)
#define VMA_AREA_UPROBES (1 << 17)
#define VMA_AREA_NOT_ACCOUNTABLE (1 << 18)
#define VMA_EXT_PLUGIN (1 << 27)
#define VMA_CLOSE (1 << 28)

View file

@ -951,15 +951,25 @@ static int premap_private_vma(struct pstree_item *t, struct vma_area *vma, void
}
/*
* All mappings here get PROT_WRITE regardless of whether we
* put any data into it or not, because this area will get
* mremap()-ed (branch below) so we MIGHT need to have WRITE
* bits there. Ideally we'd check for the whole COW-chain
* having any data in.
* For VMAs that have PROT_NONE and are not accountable
* (did not have the "ac" flag in /proc/pid/smaps), we
* can safely mmap them with PROT_NONE because we know
* we will never need to write any bits to them.
*/
addr = mmap(*tgt_addr, size, vma->e->prot | PROT_WRITE, vma->e->flags | MAP_FIXED | flag, vma->e->fd,
vma->e->pgoff);
if (vma->e->prot == PROT_NONE && vma_area_is(vma, VMA_AREA_NOT_ACCOUNTABLE)) {
addr = mmap(*tgt_addr, size, PROT_NONE, vma->e->flags | MAP_FIXED | flag, vma->e->fd,
vma->e->pgoff);
} else {
/*
* All mappings here get PROT_WRITE regardless of whether we
* put any data into it or not, because this area will get
* mremap()-ed (branch below) so we MIGHT need to have WRITE
* bits there. Ideally we'd check for the whole COW-chain
* having any data in.
*/
addr = mmap(*tgt_addr, size, vma->e->prot | PROT_WRITE, vma->e->flags | MAP_FIXED | flag, vma->e->fd,
vma->e->pgoff);
}
if (addr == MAP_FAILED) {
pr_perror("Unable to map ANON_VMA");
return -1;

View file

@ -123,10 +123,11 @@ bool handle_vma_plugin(int *fd, struct stat *stat)
}
static void __parse_vmflags(char *buf, u32 *flags, u64 *madv, int *io_pf,
int *shstk)
int *shstk, bool *no_ac)
{
char *tok;
*no_ac = true;
if (!buf[0])
return;
@ -174,6 +175,9 @@ static void __parse_vmflags(char *buf, u32 *flags, u64 *madv, int *io_pf,
if (_vmflag_match(tok, "ss"))
*shstk = 1;
if (_vmflag_match(tok, "ac"))
*no_ac = false;
/*
* Anything else is just ignored.
*/
@ -185,21 +189,26 @@ static void __parse_vmflags(char *buf, u32 *flags, u64 *madv, int *io_pf,
void parse_vmflags(char *buf, u32 *flags, u64 *madv, int *io_pf)
{
int shstk = 0;
bool no_ac = false;
__parse_vmflags(buf, flags, madv, io_pf, &shstk);
__parse_vmflags(buf, flags, madv, io_pf, &shstk, &no_ac);
}
static void parse_vma_vmflags(char *buf, struct vma_area *vma_area)
{
int io_pf = 0;
int shstk = 0;
bool no_ac = false;
__parse_vmflags(buf, &vma_area->e->flags, &vma_area->e->madv, &io_pf,
&shstk);
&shstk, &no_ac);
if (shstk)
vma_area->e->status |= VMA_AREA_SHSTK;
if (no_ac)
vma_area->e->status |= VMA_AREA_NOT_ACCOUNTABLE;
/*
* vmsplice doesn't work for VM_IO and VM_PFNMAP mappings, the
* only exception is VVAR area that mapped by the kernel as

View file

@ -113,6 +113,7 @@ mmap_status_map = [
('VMA_AREA_MEMFD', 1 << 14),
('VMA_AREA_SHSTK', 1 << 15),
('VMA_AREA_UPROBES', 1 << 17),
('VMA_AREA_NOT_ACCOUNTABLE', 1 << 18),
('VMA_UNSUPP', 1 << 31),
]