mirror of https://github.com/OpenIPC/firmware.git
177 lines
4.0 KiB
Diff
177 lines
4.0 KiB
Diff
diff -drupN a/kernel/sys.c b/kernel/sys.c
|
|
--- a/kernel/sys.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/kernel/sys.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -41,6 +41,8 @@
|
|
#include <linux/syscore_ops.h>
|
|
#include <linux/version.h>
|
|
#include <linux/ctype.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/mempolicy.h>
|
|
|
|
#include <linux/compat.h>
|
|
#include <linux/syscalls.h>
|
|
@@ -2075,6 +2077,153 @@ static int prctl_get_tid_address(struct
|
|
}
|
|
#endif
|
|
|
|
+#ifdef CONFIG_MMU
|
|
+static int prctl_update_vma_anon_name(struct vm_area_struct *vma,
|
|
+ struct vm_area_struct **prev,
|
|
+ unsigned long start, unsigned long end,
|
|
+ const char __user *name_addr)
|
|
+{
|
|
+ struct mm_struct *mm = vma->vm_mm;
|
|
+ int error = 0;
|
|
+ pgoff_t pgoff;
|
|
+
|
|
+ if (name_addr == vma_get_anon_name(vma)) {
|
|
+ *prev = vma;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
|
|
+ *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma,
|
|
+ vma->vm_file, pgoff, vma_policy(vma),
|
|
+ vma->vm_userfaultfd_ctx, name_addr);
|
|
+ if (*prev) {
|
|
+ vma = *prev;
|
|
+ goto success;
|
|
+ }
|
|
+
|
|
+ *prev = vma;
|
|
+
|
|
+ if (start != vma->vm_start) {
|
|
+ error = split_vma(mm, vma, start, 1);
|
|
+ if (error)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (end != vma->vm_end) {
|
|
+ error = split_vma(mm, vma, end, 0);
|
|
+ if (error)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+success:
|
|
+ if (!vma->vm_file)
|
|
+ vma->anon_name = name_addr;
|
|
+
|
|
+out:
|
|
+ if (error == -ENOMEM)
|
|
+ error = -EAGAIN;
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int prctl_set_vma_anon_name(unsigned long start, unsigned long end,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ unsigned long tmp;
|
|
+ struct vm_area_struct *vma, *prev;
|
|
+ int unmapped_error = 0;
|
|
+ int error = -EINVAL;
|
|
+
|
|
+ /*
|
|
+ * If the interval [start,end) covers some unmapped address
|
|
+ * ranges, just ignore them, but return -ENOMEM at the end.
|
|
+ * - this matches the handling in madvise.
|
|
+ */
|
|
+ vma = find_vma_prev(current->mm, start, &prev);
|
|
+ if (vma && start > vma->vm_start)
|
|
+ prev = vma;
|
|
+
|
|
+ for (;;) {
|
|
+ /* Still start < end. */
|
|
+ error = -ENOMEM;
|
|
+ if (!vma)
|
|
+ return error;
|
|
+
|
|
+ /* Here start < (end|vma->vm_end). */
|
|
+ if (start < vma->vm_start) {
|
|
+ unmapped_error = -ENOMEM;
|
|
+ start = vma->vm_start;
|
|
+ if (start >= end)
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
|
|
+ tmp = vma->vm_end;
|
|
+ if (end < tmp)
|
|
+ tmp = end;
|
|
+
|
|
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
|
|
+ error = prctl_update_vma_anon_name(vma, &prev, start, tmp,
|
|
+ (const char __user *)arg);
|
|
+ if (error)
|
|
+ return error;
|
|
+ start = tmp;
|
|
+ if (prev && start < prev->vm_end)
|
|
+ start = prev->vm_end;
|
|
+ error = unmapped_error;
|
|
+ if (start >= end)
|
|
+ return error;
|
|
+ if (prev)
|
|
+ vma = prev->vm_next;
|
|
+ else /* madvise_remove dropped mmap_sem */
|
|
+ vma = find_vma(current->mm, start);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int prctl_set_vma(unsigned long opt, unsigned long start,
|
|
+ unsigned long len_in, unsigned long arg)
|
|
+{
|
|
+ struct mm_struct *mm = current->mm;
|
|
+ int error;
|
|
+ unsigned long len;
|
|
+ unsigned long end;
|
|
+
|
|
+ if (start & ~PAGE_MASK)
|
|
+ return -EINVAL;
|
|
+ len = (len_in + ~PAGE_MASK) & PAGE_MASK;
|
|
+
|
|
+ /* Check to see whether len was rounded up from small -ve to zero */
|
|
+ if (len_in && !len)
|
|
+ return -EINVAL;
|
|
+
|
|
+ end = start + len;
|
|
+ if (end < start)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (end == start)
|
|
+ return 0;
|
|
+
|
|
+ down_write(&mm->mmap_sem);
|
|
+
|
|
+ switch (opt) {
|
|
+ case PR_SET_VMA_ANON_NAME:
|
|
+ error = prctl_set_vma_anon_name(start, end, arg);
|
|
+ break;
|
|
+ default:
|
|
+ error = -EINVAL;
|
|
+ }
|
|
+
|
|
+ up_write(&mm->mmap_sem);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+#else /* CONFIG_MMU */
|
|
+static int prctl_set_vma(unsigned long opt, unsigned long start,
|
|
+ unsigned long len_in, unsigned long arg)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif
|
|
+
|
|
int __weak arch_prctl_spec_ctrl_get(struct task_struct *t, unsigned long which)
|
|
{
|
|
return -EINVAL;
|
|
@@ -2294,6 +2443,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsi
|
|
return -EINVAL;
|
|
error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
|
|
break;
|
|
+ case PR_SET_VMA:
|
|
+ error = prctl_set_vma(arg2, arg3, arg4, arg5);
|
|
+ break;
|
|
default:
|
|
error = -EINVAL;
|
|
break;
|