All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
* [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle
@ 2026-10-14 17:12 Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 1/7] oracle/mm: take a file reference when preserving a file-backed VMA Konstantin Khorenko
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

Fixes in the existing Oracle MADV_DOEXEC code, found while working on
VSTOR-149847, and a refusal of preserved memory across setuid exec.
Applies on top of "mm: keep DMA-pinned MADV_DOEXEC memory working across
exec".

- 1: vma_dup() takes no file reference for a file-backed VMA, so a
  private /dev/zero mapping marked MADV_DOEXEC is put twice
  (use-after-free of struct file, reachable unprivileged).
- 2: a private VM_PFNMAP VMA with NULL vm_ops passed as anonymous and
  its PAT memtype was freed twice; refuse VM_SPECIAL.
- 3: MADV_DOEXEC never cleared VM_MAYEXEC and MADV_DONTEXEC granted it
  unconditionally, also on noexec mounts; the copies made by exec and
  fork get back what do_mmap() granted.
- 4: load_elf_binary() looked for the opt-in note only in the last
  PT_NOTE segment; a binary whose linker puts it elsewhere silently got
  no preserved memory.
- 5: a setuid/secureexec exec with preserved memory is refused with
  EPERM before the point of no return: long-term pins driven from
  another process would keep writing into the privileged image.
- 6, 7: selftests for 5 and for 1 and 3.

Tested with the whole stack on release and debug kernels (MADV_DOEXEC
selftests, gup_test, a Container start/exec/stop).

https://virtuozzo.atlassian.net/browse/VSTOR-150232

---
Konstantin Khorenko (7):
      oracle/mm: take a file reference when preserving a file-backed VMA
      oracle/mm: refuse MADV_DOEXEC on special mappings
      oracle/mm: keep a MADV_DOEXEC range from becoming executable
      oracle/exec, elf: look for the opt-in note in every PT_NOTE segment
      exec: refuse to preserve memory across a privilege-changing exec
      selftests/mm: check that a setuid exec refuses preserved memory
      selftests/mm: check MADV_DOEXEC on /dev/zero and exec permission

 fs/binfmt_elf.c                     |  87 +++++++++------
 fs/exec.c                           |   4 +
 include/linux/mm.h                  |   1 +
 kernel/fork.c                       |   4 +-
 mm/madvise.c                        |  12 +-
 mm/mmap.c                           |  17 ++-
 tools/testing/selftests/mm/doexec.c | 211 +++++++++++++++++++++++++++++++++++-
 7 files changed, 290 insertions(+), 46 deletions(-)
---
base-commit: 0f7fa776e32c8e6832e7604dc157d05d3af14513
change-id: 20261014-b4-prep-khorenko-vstor-149847-3-doexec-oracle-fixes-33341d1abc4d

Best regards,
--  
Konstantin Khorenko <khorenko@virtuozzo.com>

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 1/7] oracle/mm: take a file reference when preserving a file-backed VMA
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 2/7] oracle/mm: refuse MADV_DOEXEC on special mappings Konstantin Khorenko
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

A private mapping of /dev/zero is anonymous for vma_is_anonymous() but
keeps its vm_file, so MADV_DOEXEC accepts it. vma_dup() copies the
vm_file pointer without taking a reference, and both mms drop one at
teardown. Any user can do this with a binary that carries the
preserved-mem-ok note: map /dev/zero privately, close the fd, mark the
range and exec. The second fput() is a use-after-free of the struct
file.

Take the reference the way dup_mmap() does, and drop it if linking the
copy fails.

Fixes: 9e6d16a6c0a1 ("oracle/mm: introduce VM_EXEC_KEEP")
https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 mm/mmap.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mm/mmap.c b/mm/mmap.c
index bc508c6bd934b..52e971aee020f 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1781,6 +1781,8 @@ int vma_dup(struct vm_area_struct *old_vma, struct mm_struct *mm)
 	ret = anon_vma_fork(vma, old_vma);
 	if (ret)
 		goto fail_nomem_anon_vma_fork;
+	if (vma->vm_file)
+		get_file(vma->vm_file);
 
 	/*
 	 * Clear functionality that should not carry over to the new
@@ -1814,6 +1816,8 @@ int vma_dup(struct vm_area_struct *old_vma, struct mm_struct *mm)
 	return ret;
 
 fail_nomem_vma_link:
+	if (vma->vm_file)
+		fput(vma->vm_file);
 	unlink_anon_vmas(vma);
 fail_nomem_anon_vma_fork:
 	mpol_put(vma_policy(vma));

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 2/7] oracle/mm: refuse MADV_DOEXEC on special mappings
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 1/7] oracle/mm: take a file reference when preserving a file-backed VMA Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 3/7] oracle/mm: keep a MADV_DOEXEC range from becoming executable Konstantin Khorenko
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

A DRM import of a dma-buf clears vm_ops before it calls the exporter's
mmap, and nothing on that path insists on MAP_SHARED. An exporter that
fills the VMA with remap_pfn_range() and sets no vm_ops of its own
leaves a VM_PFNMAP VMA that vma_is_anonymous() accepts. vma_dup() then
copies it with copy_page_range_mt(), which never calls
track_pfn_copy(), while vm_area_dup() hands the copy the VM_PAT of the
original: both mms free the same memtype reservation at teardown. For a
range outside RAM, the second free_pfn_range() either logs "freeing
invalid memtype" or removes the entry that another mapping of the same
range holds.

Preserving device or other special memory was never the intent of the
anonymous case. Refuse VM_SPECIAL there.

Fixes: d674aa9506ea ("oracle/mm: use padata for copying page ranges in vma_dup()")
https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 mm/madvise.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/mm/madvise.c b/mm/madvise.c
index b89d891e122c9..644057a2bbbb0 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -1310,10 +1310,11 @@ static int madvise_vma_behavior(struct vm_area_struct *vma,
 	case MADV_DOEXEC:
 		/*
 		 * MADV_DOEXEC is only supported on private, non-executable,
-		 * non-stack anonymous memory and if the VM_EXEC_KEEP flag
-		 * is available.
+		 * non-stack, non-special anonymous memory and if the
+		 * VM_EXEC_KEEP flag is available.
 		 */
-		if (!VM_EXEC_KEEP || !vma_is_anonymous(vma) || vma->vm_flags & (VM_EXEC|VM_SHARED|VM_STACK)) {
+		if (!VM_EXEC_KEEP || !vma_is_anonymous(vma) ||
+		    vma->vm_flags & (VM_EXEC | VM_SHARED | VM_STACK | VM_SPECIAL)) {
 			error = -EINVAL;
 			goto out;
 		}

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 3/7] oracle/mm: keep a MADV_DOEXEC range from becoming executable
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 1/7] oracle/mm: take a file reference when preserving a file-backed VMA Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 2/7] oracle/mm: refuse MADV_DOEXEC on special mappings Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 4/7] oracle/exec, elf: look for the opt-in note in every PT_NOTE segment Konstantin Khorenko
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

MADV_DOEXEC rejects executable memory and MADV_DONTEXEC gives
VM_MAYEXEC back, so the intent is that a preserved range cannot become
executable. Two bugs break it:

- The OR-assignment in the MADV_DOEXEC case never drops VM_MAYEXEC, so
  mprotect(PROT_EXEC) on the range succeeds and exec carries the
  executable mapping into the new image.
- MADV_DONTEXEC sets VM_MAYEXEC unconditionally. A private /dev/zero
  mapping from a noexec mount, which do_mmap() created without
  VM_MAYEXEC, becomes executable after MADV_DOEXEC and MADV_DONTEXEC.

Clear the bit on MADV_DOEXEC and give back what do_mmap() grants such a
mapping whenever the mark goes: on MADV_DONTEXEC, and when exec or fork
drops it from a copy, which could otherwise never be made executable
again.

Fixes: 056724255f5f ("oracle/mm: introduce MADV_DOEXEC")
https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 include/linux/mm.h |  1 +
 kernel/fork.c      |  4 +++-
 mm/madvise.c       |  5 +++--
 mm/mmap.c          | 13 ++++++++++++-
 4 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 9ebf3c6dcb978..91f305fd5c025 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2463,6 +2463,7 @@ struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
 void zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
 		  unsigned long size);
 void zap_vma_mt(struct vm_area_struct *vma);
+vm_flags_t vma_doexec_mayexec(struct vm_area_struct *vma);
 void zap_page_range_single(struct vm_area_struct *vma, unsigned long address,
 			   unsigned long size, struct zap_details *details);
 static inline void zap_vma_pages(struct vm_area_struct *vma)
diff --git a/kernel/fork.c b/kernel/fork.c
index 92a7e9aae9278..25e87cdbeb073 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -715,7 +715,9 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
 			tmp->anon_vma = NULL;
 		} else if (anon_vma_fork(tmp, mpnt))
 			goto fail_nomem_anon_vma_fork;
-		vm_flags_clear(tmp, VM_LOCKED_MASK | VM_EXEC_KEEP);
+		if (tmp->vm_flags & VM_EXEC_KEEP)
+			vm_flags_mod(tmp, vma_doexec_mayexec(tmp), VM_EXEC_KEEP);
+		vm_flags_clear(tmp, VM_LOCKED_MASK);
 		/*
 		 * Copy/update hugetlb private vma information.
 		 */
diff --git a/mm/madvise.c b/mm/madvise.c
index 644057a2bbbb0..e6972bcfc9749 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -1318,7 +1318,7 @@ static int madvise_vma_behavior(struct vm_area_struct *vma,
 			error = -EINVAL;
 			goto out;
 		}
-		new_flags |= (new_flags & ~VM_MAYEXEC) | VM_EXEC_KEEP;
+		new_flags = (new_flags & ~VM_MAYEXEC) | VM_EXEC_KEEP;
 		break;
 	case MADV_DONTEXEC:
 		if (!VM_EXEC_KEEP) {
@@ -1326,7 +1326,8 @@ static int madvise_vma_behavior(struct vm_area_struct *vma,
 			goto out;
 		}
 		if (new_flags & VM_EXEC_KEEP)
-			new_flags = (new_flags & ~VM_EXEC_KEEP) | VM_MAYEXEC;
+			new_flags = (new_flags & ~VM_EXEC_KEEP) |
+				    vma_doexec_mayexec(vma);
 		break;
 	case MADV_DONTDUMP:
 		new_flags |= VM_DONTDUMP;
diff --git a/mm/mmap.c b/mm/mmap.c
index 52e971aee020f..69dd1514d750e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1750,6 +1750,17 @@ int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma)
 	return 0;
 }
 
+/*
+ * The VM_MAYEXEC that MADV_DOEXEC took from @vma, to give back when the mark
+ * goes: what do_mmap() grants such a mapping.
+ */
+vm_flags_t vma_doexec_mayexec(struct vm_area_struct *vma)
+{
+	if (vma->vm_file && path_noexec(&vma->vm_file->f_path))
+		return 0;
+	return VM_MAYEXEC;
+}
+
 /*
  * Duplicate an anonymous VMA into a new mm as part of preserving
  * it across exec.
@@ -1811,7 +1822,7 @@ int vma_dup(struct vm_area_struct *old_vma, struct mm_struct *mm)
 #else
 	ret = copy_page_range(vma, old_vma);
 #endif
-	vm_flags_clear(vma, VM_EXEC_KEEP);
+	vm_flags_mod(vma, vma_doexec_mayexec(vma), VM_EXEC_KEEP);
 
 	return ret;
 

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 4/7] oracle/exec, elf: look for the opt-in note in every PT_NOTE segment
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
                   ` (2 preceding siblings ...)
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 3/7] oracle/mm: keep a MADV_DOEXEC range from becoming executable Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 5/7] exec: refuse to preserve memory across a privilege-changing exec Konstantin Khorenko
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

load_elf_binary() keeps only the last PT_NOTE segment it sees and looks
for the preserved-mem-ok note there. Linkers group notes into segments
by alignment: .note.gnu.property, aligned to 8, gets a PT_NOTE of its
own, and the 4-aligned notes share another. ld.bfd happens to put the
4-aligned one last, but another linker or a linker script can put the
segment with the opt-in elsewhere. The binary then silently gets no
preserved memory, and a live update loses the guest RAM it meant to
keep.

Check the notes of every PT_NOTE segment. The last segment, the only one
read before, keeps its check: its first note of the opt-in type fails
the exec with ENOEXEC unless it is the opt-in, that is when its name
size is over 17, its name is another or it is cut off by the end of the
segment. In the other segments only the opt-in counts and anything else
is skipped, so the notes refuse nothing the old check accepted. The
opt-in is recorded in bprm only after all segments passed, so that an
ENOEXEC does not leave it behind for the next binfmt handler. The walk
keeps the 4-byte padding it always used, so in an 8-aligned segment it
stays in step only with notes whose name size rounds up to an odd
multiple of 4 and whose descriptor size rounds up to a multiple of 8, as
the GNU property notes have. It stops at a note whose sizes run past the
segment, which on a 32-bit kernel could wrap the offset and loop
forever.

The notes are read for every ELF binary. A segment too short for a
note, larger than MAX_FILE_NOTE_SIZE or past the end of the file holds
nothing a linker emits and is skipped; the exec of a binary whose last
PT_NOTE was like that used to fail with ENOEXEC, EIO or EINVAL. An
error reading a segment, or allocating the buffer for it, fails the
exec, since skipping it could drop the opt-in and the memory with it.

Fixes: d07a690048ce ("oracle/exec, elf: require opt-in for accepting preserved mem")
https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 fs/binfmt_elf.c | 87 ++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 52 insertions(+), 35 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a5689a2844d43..9cc84cdbb60fb 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -837,16 +837,19 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
 
 static int get_elf_notes(struct linux_binprm *bprm, struct elf_phdr *phdr, char **notes, size_t *notes_sz)
 {
+	u64 isize = i_size_read(file_inode(bprm->file));
 	char *data;
 	size_t datasz;
 	int ret;
 
-	if (!phdr)
-		return 0;
-
+	/*
+	 * Skip a segment too short for a note, too large to read or past the
+	 * end of the file; an error reading it is fatal.
+	 */
 	datasz = phdr->p_filesz;
-	if ((datasz > MAX_FILE_NOTE_SIZE) || (datasz < sizeof(struct elf_note)))
-		return -ENOEXEC;
+	if (datasz > MAX_FILE_NOTE_SIZE || datasz < sizeof(struct elf_note) ||
+	    phdr->p_offset > isize || datasz > isize - phdr->p_offset)
+		return 0;
 
 	data = kvmalloc(datasz, GFP_KERNEL);
 	if (!data)
@@ -866,10 +869,17 @@ static int get_elf_notes(struct linux_binprm *bprm, struct elf_phdr *phdr, char
 #define PRESERVED_MEM_OK_STRING		"preserved-mem-ok"
 #define SZ_PRESERVED_MEM_OK_STRING	sizeof(PRESERVED_MEM_OK_STRING)
 
-static int check_preserved_mem_ok(struct linux_binprm *bprm, const char *data, const size_t datasz)
+/*
+ * 1 if the notes hold the opt-in. In the last PT_NOTE segment the first note
+ * of the opt-in type fails the exec with -ENOEXEC unless it is the opt-in; in
+ * the other segments only the opt-in counts.
+ */
+static int check_preserved_mem_ok(const char *data, const size_t datasz,
+				  bool last)
 {
 	size_t off = 0;
 	size_t remain;
+	u64 next;
 
 	if (!data)
 		return 0;
@@ -887,21 +897,21 @@ static int check_preserved_mem_ok(struct linux_binprm *bprm, const char *data, c
 		off += sizeof(*nhdr);
 		remain -= sizeof(*nhdr);
 
-		if (nhdr->n_type != 0x07c1feed) {
-			off += roundup(nhdr->n_namesz, 4) + roundup(nhdr->n_descsz, 4);
-			continue;
+		if (nhdr->n_type == 0x07c1feed) {
+			name = data + off;
+			if (nhdr->n_namesz <= SZ_PRESERVED_MEM_OK_STRING &&
+			    remain >= SZ_PRESERVED_MEM_OK_STRING &&
+			    !strncmp(name, PRESERVED_MEM_OK_STRING,
+				     SZ_PRESERVED_MEM_OK_STRING))
+				return 1;
+			if (last)
+				return -ENOEXEC;
 		}
-
-		if (nhdr->n_namesz > SZ_PRESERVED_MEM_OK_STRING)
-			return -ENOEXEC;
-
-		name =  data + off;
-		if (remain < SZ_PRESERVED_MEM_OK_STRING ||
-		    strncmp(name, PRESERVED_MEM_OK_STRING, SZ_PRESERVED_MEM_OK_STRING))
-			return -ENOEXEC;
-
-		bprm->accepts_preserved_mem = 1;
-		break;
+		next = round_up((u64)nhdr->n_namesz, 4) +
+		       round_up((u64)nhdr->n_descsz, 4);
+		if (next > remain)
+			break;
+		off += next;
 	}
 
 	return 0;
@@ -915,9 +925,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	unsigned long error;
 	struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
 	struct elf_phdr *elf_property_phdata = NULL;
-	struct elf_phdr *elf_notes_phdata = NULL;
-	char *elf_notes = NULL;
-	size_t elf_notes_sz = 0;
+	int accepts_preserved_mem = 0, last_note = -1;
 	unsigned long elf_brk;
 	bool brk_moved = false;
 	int retval, i;
@@ -1028,10 +1036,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
 				executable_stack = EXSTACK_DISABLE_X;
 			break;
 
-		case PT_NOTE:
-			elf_notes_phdata = elf_ppnt;
-			break;
-
 		case PT_LOPROC ... PT_HIPROC:
 			retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
 						  bprm->file, false,
@@ -1097,13 +1101,28 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	if (retval)
 		goto out_free_dentry;
 
-	retval = get_elf_notes(bprm, elf_notes_phdata, &elf_notes, &elf_notes_sz);
-	if (retval)
-		goto out_free_dentry;
+	/* The linker spreads notes over several segments by their alignment. */
+	for (i = 0, elf_ppnt = elf_phdata; i < elf_ex->e_phnum; i++, elf_ppnt++)
+		if (elf_ppnt->p_type == PT_NOTE)
+			last_note = i;
+	for (i = 0, elf_ppnt = elf_phdata; i < elf_ex->e_phnum; i++, elf_ppnt++) {
+		size_t elf_notes_sz = 0;
+		char *elf_notes = NULL;
 
-	retval = check_preserved_mem_ok(bprm, elf_notes, elf_notes_sz);
-	if (retval)
-		goto out_free_dentry;
+		if (elf_ppnt->p_type != PT_NOTE)
+			continue;
+		retval = get_elf_notes(bprm, elf_ppnt, &elf_notes, &elf_notes_sz);
+		if (retval)
+			goto out_free_dentry;
+		retval = check_preserved_mem_ok(elf_notes, elf_notes_sz,
+						i == last_note);
+		kvfree(elf_notes);
+		if (retval < 0)
+			goto out_free_dentry;
+		accepts_preserved_mem |= retval;
+	}
+	/* Only now: an ENOEXEC above lets another binfmt handler try the file. */
+	bprm->accepts_preserved_mem = accepts_preserved_mem;
 
 	/* Flush all traces of the currently running executable */
 	retval = begin_new_exec(bprm);
@@ -1381,7 +1400,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		}
 	}
 
-	kvfree(elf_notes);
 	kfree(elf_phdata);
 
 	set_binfmt(&elf_format);
@@ -1486,7 +1504,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	if (interpreter)
 		fput(interpreter);
 out_free_ph:
-	kvfree(elf_notes);
 	kfree(elf_phdata);
 	goto out;
 }

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 5/7] exec: refuse to preserve memory across a privilege-changing exec
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
                   ` (3 preceding siblings ...)
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 4/7] oracle/exec, elf: look for the opt-in note in every PT_NOTE segment Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 6/7] selftests/mm: check that a setuid exec refuses preserved memory Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 7/7] selftests/mm: check MADV_DOEXEC on /dev/zero and exec permission Konstantin Khorenko
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

Since commit 9e6d16a6c0a1 ("oracle/mm: introduce VM_EXEC_KEEP") a pinned
page in a preserved range is shared with the new image instead of
copied, and since commit 37d9f55655ea ("oracle/mm: do not write protect
COW mappings when preserving across exec") it is not copied on write
either: long-term pins taken before the exec, such as a VFIO DMA mapping
or io_uring fixed buffers, keep pointing at it. That is what the feature
is for, DMA into the pages has to continue across exec.

When the exec is a secure one, for a set-id or file-capability binary or
an LSM transition that asks for AT_SECURE, this hands a write path into
the privileged image to whoever drives those pins: the user who set them
up keeps the device or the io_uring fd in another process and has the
device or io_uring write into the memory of the new image. The pins
cannot be revoked, and zapping the ranges in the old mm does not touch
them.

Refuse the exec with EPERM from reserve_preserved_mem() when
bprm->secureexec is set. bprm_creds_from_file() has already run at that
point and the check is before the point of no return, so the caller gets
the error from execve(), can drop MADV_DOEXEC and retry. secureexec is
computed before the ptrace downgrade, so a ptraced setuid exec is
refused as well. A root task that regains dropped capabilities through
exec does not set it and is not refused.

Fixes: 9e6d16a6c0a1 ("oracle/mm: introduce VM_EXEC_KEEP")
https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 fs/exec.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index 5bf856e829af3..49a0a9693eb8a 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1021,6 +1021,10 @@ static int reserve_preserved_mem(struct linux_binprm *bprm)
 	if (!preserve)
 		return 0;
 
+	/* Pins taken under the old credentials outlive the exec. */
+	if (bprm->secureexec)
+		return -EPERM;
+
 	/* A clone is either counted here or already visible in the task list. */
 	spin_lock(&mm->page_table_lock);
 	if (mm->exec_keep_active || mm->exec_keep_clones)

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 6/7] selftests/mm: check that a setuid exec refuses preserved memory
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
                   ` (4 preceding siblings ...)
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 5/7] exec: refuse to preserve memory across a privilege-changing exec Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 7/7] selftests/mm: check MADV_DOEXEC on /dev/zero and exec permission Konstantin Khorenko
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

Copy the test binary, make it setuid root and exec it from an
unprivileged child with a MADV_DOEXEC range: exec must fail with EPERM
and leave the mapping intact, and must succeed once the range is
unmarked with MADV_DONTEXEC. The case needs root and a directory that
is neither private nor mounted nosuid; the current directory, /var/tmp
and /tmp are tried in that order and the case is skipped if none fits.
The copy goes into a new directory that others can enter but not list,
and run setuid it does nothing but report its euid.

https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 tools/testing/selftests/mm/doexec.c | 121 ++++++++++++++++++++++++++++++++++--
 1 file changed, 117 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
index 33529aa41367b..dfa3da2c43934 100644
--- a/tools/testing/selftests/mm/doexec.c
+++ b/tools/testing/selftests/mm/doexec.c
@@ -3,6 +3,7 @@
 #define _GNU_SOURCE
 #include <errno.h>
 #include <fcntl.h>
+#include <grp.h>
 #include <linux/vhost.h>
 #include <poll.h>
 #include <pthread.h>
@@ -17,6 +18,8 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/resource.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -27,6 +30,9 @@
 #ifndef MADV_DOEXEC
 #define MADV_DOEXEC 201
 #endif
+#ifndef MADV_DONTEXEC
+#define MADV_DONTEXEC 202
+#endif
 #ifndef MADV_COLLAPSE
 #define MADV_COLLAPSE 25
 #endif
@@ -283,7 +289,8 @@ static int before_exec(int kind)
 	return KSFT_FAIL;
 }
 
-static int check_exec(char *ram, size_t size, size_t stride, bool reject)
+static int exec_path(const char *exe, char *ram, size_t size, size_t stride,
+		     bool reject)
 {
 	char addr_arg[32], size_arg[32], stride_arg[32];
 
@@ -291,11 +298,16 @@ static int check_exec(char *ram, size_t size, size_t stride, bool reject)
 		 (unsigned long long)(uintptr_t)ram);
 	snprintf(size_arg, sizeof(size_arg), "%zu", size);
 	snprintf(stride_arg, sizeof(stride_arg), "%zu", stride);
-	execl("/proc/self/exe", "doexec", reject ? "--reject" : "--check", addr_arg, size_arg,
+	execl(exe, "doexec", reject ? "--reject" : "--check", addr_arg, size_arg,
 	      stride_arg, NULL);
 	return errno;
 }
 
+static int check_exec(char *ram, size_t size, size_t stride, bool reject)
+{
+	return exec_path("/proc/self/exe", ram, size, stride, reject);
+}
+
 static char *preserved_ram(size_t size)
 {
 	char *ram = mmap(NULL, size, PROT_READ | PROT_WRITE,
@@ -397,6 +409,98 @@ static int vhost_owner_exec(void)
 	return KSFT_FAIL;
 }
 
+static void remove_setuid_copy(char *path)
+{
+	unlink(path);
+	*strrchr(path, '/') = '\0';
+	rmdir(path);
+}
+
+static int make_setuid_copy(const char *dir, char *path, size_t len)
+{
+	ssize_t copied = -1;
+	int in, out;
+	size_t n;
+
+	snprintf(path, len, "%s/doexec-setuid.XXXXXX", dir);
+	/* Others may run the copy but cannot find it. */
+	if (!mkdtemp(path))
+		return -1;
+	if (chmod(path, 0711)) {
+		rmdir(path);
+		return -1;
+	}
+	n = strlen(path);
+	snprintf(path + n, len - n, "/doexec.XXXXXX");
+	out = mkstemp(path);
+	in = open("/proc/self/exe", O_RDONLY);
+	if (in >= 0 && out >= 0)
+		while ((copied = sendfile(out, in, NULL, 1 << 20)) > 0)
+			;
+	if (in >= 0)
+		close(in);
+	if (out >= 0 && !copied && !fchmod(out, S_ISUID | 0755)) {
+		close(out);
+		return 0;
+	}
+	if (out >= 0)
+		close(out);
+	remove_setuid_copy(path);
+	return -1;
+}
+
+static int unprivileged_exec(const char *path, bool preserve)
+{
+	size_t size = sysconf(_SC_PAGESIZE);
+	pid_t pid = fork();
+	char *ram;
+	int err;
+
+	if (pid < 0)
+		return KSFT_FAIL;
+	if (pid)
+		return wait_child(pid);
+	if (setgroups(0, NULL) || setresgid(65534, 65534, 65534) ||
+	    setresuid(65534, 65534, 65534))
+		_exit(KSFT_SKIP);
+	if (!preserve) {
+		execl(path, "doexec", "--euid", NULL);
+		_exit(KSFT_SKIP);
+	}
+	ram = preserved_ram(size);
+	if (!ram)
+		_exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL);
+	err = exec_path(path, ram, size, 1, true);
+	/* Exec must return EPERM with the old mapping intact. */
+	if (err != EPERM || check_bytes(ram, size, 0x31))
+		_exit(KSFT_FAIL);
+	if (madvise(ram, size, MADV_DONTEXEC))
+		_exit(KSFT_FAIL);
+	execl(path, "doexec", "--euid", NULL);
+	_exit(KSFT_FAIL);
+}
+
+/* The ELF opt-in must not pull memory into a more privileged image. */
+static int setuid_exec(void)
+{
+	static const char * const dirs[] = { ".", "/var/tmp", "/tmp" };
+	char path[96];
+	int i, ret = KSFT_SKIP;
+
+	if (geteuid())
+		return KSFT_SKIP;
+	for (i = 0; i < 3 && ret == KSFT_SKIP; i++) {
+		if (make_setuid_copy(dirs[i], path, sizeof(path)))
+			continue;
+		/* A nosuid mount or a private directory makes the copy useless. */
+		ret = unprivileged_exec(path, false);
+		if (ret == KSFT_PASS)
+			ret = unprivileged_exec(path, true);
+		remove_setuid_copy(path);
+	}
+	return ret;
+}
+
 static void *exec_thread(void *arg)
 {
 	check_exec(arg, sysconf(_SC_PAGESIZE), 1, false);
@@ -723,6 +827,10 @@ int main(int argc, char **argv)
 	pid_t pid;
 	int kind, ret;
 
+	/* The setuid copy only reports that it runs as root. */
+	if (geteuid() != getuid())
+		return argc == 2 && !strcmp(argv[1], "--euid") && !geteuid() ?
+		       KSFT_PASS : KSFT_FAIL;
 	if (argc == 7 && !strcmp(argv[1], "--exec"))
 		return after_exec(atoi(argv[2]),
 			(char *)(uintptr_t)strtoull(argv[3], NULL, 10),
@@ -732,6 +840,8 @@ int main(int argc, char **argv)
 			strtoull(argv[3], NULL, 10), atoi(argv[4]), atoi(argv[5]));
 	if (argc == 5 && !strcmp(argv[1], "--reject"))
 		return KSFT_FAIL;
+	if (argc == 2 && !strcmp(argv[1], "--euid"))
+		return geteuid() ? KSFT_SKIP : KSFT_PASS;
 	if (argc == 5 && !strcmp(argv[1], "--proc"))
 		return proc_exec((char *)(uintptr_t)strtoull(argv[2], NULL, 10),
 				 strtoull(argv[3], NULL, 10), atoi(argv[4]));
@@ -751,7 +861,7 @@ int main(int argc, char **argv)
 		return KSFT_PASS;
 	}
 	ksft_print_header();
-	ksft_set_plan(NR_TESTS + 8);
+	ksft_set_plan(NR_TESTS + 9);
 	for (kind = 0; kind < NR_TESTS; kind++) {
 		fflush(stdout);
 		pid = fork();
@@ -774,7 +884,7 @@ int main(int argc, char **argv)
 		ksft_test_result_skip("pre-existing COW: DOEXEC unavailable\n");
 	else
 		ksft_test_result(ret == KSFT_PASS, "pre-existing COW preserved\n");
-	for (kind = 0; kind < 7; kind++) {
+	for (kind = 0; kind < 8; kind++) {
 		static const char * const extra_names[] = {
 			"CLONE_VM: recoverable EBUSY and retry",
 			"own threads: preserving exec allowed",
@@ -783,6 +893,7 @@ int main(int argc, char **argv)
 			"non-leader thread: preserving exec allowed",
 			"proc readers: repeated preserving exec allowed",
 			"concurrent CLONE_VM: shared memory is not stolen",
+			"setuid exec: preserving exec refused",
 		};
 
 		fflush(stdout);
@@ -802,6 +913,8 @@ int main(int argc, char **argv)
 				_exit(proc_readers_exec());
 			case 6:
 				_exit(clone_exec_race());
+			case 7:
+				_exit(setuid_exec());
 			}
 		}
 		ret = pid < 0 ? KSFT_FAIL : wait_child(pid);

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Devel] [PATCH hci-8.1 7/7] selftests/mm: check MADV_DOEXEC on /dev/zero and exec permission
  2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
                   ` (5 preceding siblings ...)
  2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 6/7] selftests/mm: check that a setuid exec refuses preserved memory Konstantin Khorenko
@ 2026-10-14 17:12 ` Konstantin Khorenko
  6 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-10-14 17:12 UTC (permalink / raw)
  To: Pavel Tikhomirov, Andrey Drobyshev, Vasileios Almpanis; +Cc: devel

Add two cases to doexec:

- A private /dev/zero mapping is marked and preserved across exec. The
  mapping is anonymous but holds a reference to the file; a copy that
  does not take its own shows up as an fput() underflow at teardown.
- A marked range cannot be made executable until MADV_DONTEXEC, and
  MADV_DONTEXEC does not make a /dev/zero mapping from a noexec tmpfs
  executable. The noexec part needs a private mount namespace, a tmpfs
  mount and a /dev/zero node on it; the case is reported as skipped
  when it cannot get them.

https://virtuozzo.atlassian.net/browse/VSTOR-150232

Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 tools/testing/selftests/mm/doexec.c | 94 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 92 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
index dfa3da2c43934..97e045ba678e8 100644
--- a/tools/testing/selftests/mm/doexec.c
+++ b/tools/testing/selftests/mm/doexec.c
@@ -17,10 +17,12 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <sys/mount.h>
 #include <sys/resource.h>
 #include <sys/sendfile.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
+#include <sys/sysmacros.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
@@ -501,6 +503,88 @@ static int setuid_exec(void)
 	return ret;
 }
 
+/* A private /dev/zero mapping is anonymous but holds a file reference. */
+static int devzero_exec(void)
+{
+	size_t size = 16 * sysconf(_SC_PAGESIZE);
+	int fd = open("/dev/zero", O_RDWR);
+	char *ram;
+
+	if (fd < 0)
+		return KSFT_SKIP;
+	ram = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+	close(fd);
+	if (ram == MAP_FAILED)
+		return KSFT_FAIL;
+	memset(ram, 0x31, size);
+	if (madvise(ram, size, MADV_DOEXEC))
+		return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+	check_exec(ram, size, 1, false);
+	return KSFT_FAIL;
+}
+
+static bool exec_refused(char *ram, size_t size)
+{
+	return mprotect(ram, size, PROT_READ | PROT_EXEC) && errno == EACCES;
+}
+
+/* MADV_DONTEXEC must not grant more than mmap() did on a noexec mount. */
+static int noexec_dontexec(void)
+{
+	size_t size = sysconf(_SC_PAGESIZE);
+	char dir[] = "/tmp/doexec-noexec.XXXXXX", node[64];
+	int fd, ret = KSFT_FAIL;
+	char *ram;
+
+	if (unshare(CLONE_NEWNS) || mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) ||
+	    !mkdtemp(dir))
+		return KSFT_SKIP;
+	if (mount("tmpfs", dir, "tmpfs", MS_NOEXEC, NULL)) {
+		rmdir(dir);
+		return KSFT_SKIP;
+	}
+	snprintf(node, sizeof(node), "%s/zero", dir);
+	fd = mknod(node, S_IFCHR | 0600, makedev(1, 5)) ? -1 : open(node, O_RDWR);
+	if (fd < 0) {
+		ret = KSFT_SKIP;
+		goto out;
+	}
+	ram = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+	close(fd);
+	if (ram == MAP_FAILED || !exec_refused(ram, size)) {
+		ret = KSFT_SKIP;
+		goto out;
+	}
+	if (madvise(ram, size, MADV_DOEXEC)) {
+		ret = errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+		goto out;
+	}
+	if (!madvise(ram, size, MADV_DONTEXEC) && exec_refused(ram, size))
+		ret = KSFT_PASS;
+out:
+	umount2(dir, MNT_DETACH);
+	rmdir(dir);
+	return ret;
+}
+
+/* A marked range cannot become executable until it is unmarked. */
+static int doexec_noexec(void)
+{
+	size_t size = sysconf(_SC_PAGESIZE);
+	char *ram = preserved_ram(size);
+	int ret;
+
+	if (!ram)
+		return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+	if (!exec_refused(ram, size) || madvise(ram, size, MADV_DONTEXEC) ||
+	    mprotect(ram, size, PROT_READ | PROT_EXEC))
+		return KSFT_FAIL;
+	ret = noexec_dontexec();
+	if (ret == KSFT_SKIP)
+		ksft_print_msg("noexec mount part skipped\n");
+	return ret;
+}
+
 static void *exec_thread(void *arg)
 {
 	check_exec(arg, sysconf(_SC_PAGESIZE), 1, false);
@@ -861,7 +945,7 @@ int main(int argc, char **argv)
 		return KSFT_PASS;
 	}
 	ksft_print_header();
-	ksft_set_plan(NR_TESTS + 9);
+	ksft_set_plan(NR_TESTS + 11);
 	for (kind = 0; kind < NR_TESTS; kind++) {
 		fflush(stdout);
 		pid = fork();
@@ -884,7 +968,7 @@ int main(int argc, char **argv)
 		ksft_test_result_skip("pre-existing COW: DOEXEC unavailable\n");
 	else
 		ksft_test_result(ret == KSFT_PASS, "pre-existing COW preserved\n");
-	for (kind = 0; kind < 8; kind++) {
+	for (kind = 0; kind < 10; kind++) {
 		static const char * const extra_names[] = {
 			"CLONE_VM: recoverable EBUSY and retry",
 			"own threads: preserving exec allowed",
@@ -894,6 +978,8 @@ int main(int argc, char **argv)
 			"proc readers: repeated preserving exec allowed",
 			"concurrent CLONE_VM: shared memory is not stolen",
 			"setuid exec: preserving exec refused",
+			"private /dev/zero mapping: preserved across exec",
+			"marked range: not executable until unmarked",
 		};
 
 		fflush(stdout);
@@ -915,6 +1001,10 @@ int main(int argc, char **argv)
 				_exit(clone_exec_race());
 			case 7:
 				_exit(setuid_exec());
+			case 8:
+				_exit(devzero_exec());
+			case 9:
+				_exit(doexec_noexec());
 			}
 		}
 		ret = pid < 0 ? KSFT_FAIL : wait_child(pid);

-- 
2.43.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-14 17:14 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-14 17:12 [Devel] [PATCH hci-8.1 0/7] mm, exec: fix the MADV_DOEXEC code we carry from Oracle Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 1/7] oracle/mm: take a file reference when preserving a file-backed VMA Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 2/7] oracle/mm: refuse MADV_DOEXEC on special mappings Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 3/7] oracle/mm: keep a MADV_DOEXEC range from becoming executable Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 4/7] oracle/exec, elf: look for the opt-in note in every PT_NOTE segment Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 5/7] exec: refuse to preserve memory across a privilege-changing exec Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 6/7] selftests/mm: check that a setuid exec refuses preserved memory Konstantin Khorenko
2026-10-14 17:12 ` [Devel] [PATCH hci-8.1 7/7] selftests/mm: check MADV_DOEXEC on /dev/zero and exec permission Konstantin Khorenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.