@@ -430,6 +430,8 @@ extern unsigned int kobjsize(const void *objp);
#ifdef CONFIG_64BIT
/* VM is sealed, in vm_flags */
#define VM_SEALED _BITUL(63)
+/* VM was writable */
+#define VM_WASWRITE _BITUL(62)
#endif
/* Bits set in the VMA until the stack is in its final location */
@@ -821,6 +821,9 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
break;
}
+ if ((vma->vm_flags & VM_WRITE) && !(newflags & VM_WRITE))
+ newflags |= VM_WASWRITE;
+
error = security_file_mprotect(vma, reqprot, prot);
if (error)
break;
@@ -36,12 +36,8 @@ static bool is_madv_discard(int behavior)
return false;
}
-static bool is_ro_anon(struct vm_area_struct *vma)
+static bool anon_is_ro(struct vm_area_struct *vma)
{
- /* check anonymous mapping. */
- if (vma->vm_file || vma->vm_flags & VM_SHARED)
- return false;
-
/*
* check for non-writable:
* PROT=RO or PKRU is not writeable.
@@ -53,6 +49,22 @@ static bool is_ro_anon(struct vm_area_struct *vma)
return false;
}
+static bool vma_is_prot_none(struct vm_area_struct *vma)
+{
+ if ((vma->vm_flags & VM_ACCESS_FLAGS) == VM_NONE)
+ return true;
+
+ return false;
+}
+
+static bool vma_was_writable_turn_readonly(struct vm_area_struct *vma)
+{
+ if (!(vma->vm_flags & VM_WRITE) && vma->vm_flags & VM_WASWRITE)
+ return true;
+
+ return false;
+}
+
/*
* Check if a vma is allowed to be modified by madvise.
*/
@@ -61,7 +73,25 @@ bool can_modify_vma_madv(struct vm_area_struct *vma, int behavior)
if (!is_madv_discard(behavior))
return true;
- if (unlikely(!can_modify_vma(vma) && is_ro_anon(vma)))
+ /* not sealed */
+ if (likely(can_modify_vma(vma)))
+ return true;
+
+ /* PROT_NONE mapping */
+ if (vma_is_prot_none(vma))
+ return true;
+
+ /* file-backed private mapping */
+ if (vma->vm_file) {
+ /* read-only but was writeable */
+ if (vma_was_writable_turn_readonly(vma))
+ return false;
+
+ return true;
+ }
+
+ /* anonymous mapping is read-only */
+ if (anon_is_ro(vma))
return false;
/* Allow by default. */