[PATCH v2 4/5] mm: Add a NO_INHERIT flag to the PR_SET_MDWE prctl

Florent Revest
2023-05-17T15:06:59Z

This extends the current PR_SET_MDWE prctl arg with a bit to indicate
that the process doesn't want MDWE protection to propagate to children.

To implement this no-inherit mode, the tag in current->mm->flags must be
absent from MMF_INIT_MASK. This means that the encoding for "MDWE but
without inherit" is different in the prctl than in the mm flags. This
leads to a bit of bit-mangling in the prctl implementation.

Signed-off-by: Florent Revest <revest@chromium.org>
---
include/linux/sched/coredump.h | 10 ++++++++++
include/uapi/linux/prctl.h | 1 +
kernel/fork.c | 2 +-
kernel/sys.c | 24 +++++++++++++++++++++---
tools/include/uapi/linux/prctl.h | 1 +
5 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index 0ee96ea7a0e9..11f5e3dacb4e 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -91,4 +91,14 @@ static inline int get_dumpable(struct mm_struct *mm)
MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK)

#define MMF_VM_MERGE_ANY 29
+#define MMF_HAS_MDWE_NO_INHERIT 30
+
+#define MMF_INIT_FLAGS(flags) ({ \
+ unsigned long new_flags = flags; \
+ if (new_flags & (1UL << MMF_HAS_MDWE_NO_INHERIT)) \
+ new_flags &= ~((1UL << MMF_HAS_MDWE) | \
+ (1UL << MMF_HAS_MDWE_NO_INHERIT)); \
+ new_flags & MMF_INIT_MASK; \
+})
+
#endif /* _LINUX_SCHED_COREDUMP_H */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 6e9af6cbc950..dacbe824e7c3 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -284,6 +284,7 @@ struct prctl_mm_map {
/* Memory deny write / execute */
#define PR_SET_MDWE 65
# define PR_MDWE_REFUSE_EXEC_GAIN (1UL << 0)
+# define PR_MDWE_NO_INHERIT (1UL << 1)

#define PR_GET_MDWE 66

diff --git a/kernel/fork.c b/kernel/fork.c
index d17995934eb4..62d52ad99937 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1284,7 +1284,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
hugetlb_count_init(mm);

if (current->mm) {
- mm->flags = current->mm->flags & MMF_INIT_MASK;
+ mm->flags = MMF_INIT_FLAGS(current->mm->flags);
mm->def_flags = current->mm->def_flags & VM_INIT_DEF_MASK;
} else {
mm->flags = default_dump_filter;
diff --git a/kernel/sys.c b/kernel/sys.c
index 339fee3eff6a..320eae3b12ab 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2368,9 +2368,25 @@ static inline int prctl_set_mdwe(unsigned long bits, unsigned long arg3,
if (arg3 || arg4 || arg5)
return -EINVAL;

- if (bits & ~(PR_MDWE_REFUSE_EXEC_GAIN))
+ if (bits & ~(PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT))
return -EINVAL;

+ /* NO_INHERIT only makes sense with REFUSE_EXEC_GAIN */
+ if (bits & PR_MDWE_NO_INHERIT && !(bits & PR_MDWE_REFUSE_EXEC_GAIN))
+ return -EINVAL;
+
+ /* Can't gain NO_INHERIT from !NO_INHERIT */
+ if (bits & PR_MDWE_NO_INHERIT &&
+ test_bit(MMF_HAS_MDWE, &current->mm->flags) &&
+ !test_bit(MMF_HAS_MDWE_NO_INHERIT, &current->mm->flags))
+ return -EPERM;
+
+ if (bits & PR_MDWE_NO_INHERIT)
+ set_bit(MMF_HAS_MDWE_NO_INHERIT, &current->mm->flags);
+ else if (test_bit(MMF_HAS_MDWE_NO_INHERIT, &current->mm->flags)
+ && !(bits & PR_MDWE_REFUSE_EXEC_GAIN))
+ return -EPERM; /* Cannot unset the flag */
+
if (bits & PR_MDWE_REFUSE_EXEC_GAIN)
set_bit(MMF_HAS_MDWE, &current->mm->flags);
else if (test_bit(MMF_HAS_MDWE, &current->mm->flags))
@@ -2385,8 +2401,10 @@ static inline int prctl_get_mdwe(unsigned long arg2, unsigned long arg3,
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;

- return test_bit(MMF_HAS_MDWE, &current->mm->flags) ?
- PR_MDWE_REFUSE_EXEC_GAIN : 0;
+ return (test_bit(MMF_HAS_MDWE, &current->mm->flags) ?
+ PR_MDWE_REFUSE_EXEC_GAIN : 0) |
+ (test_bit(MMF_HAS_MDWE_NO_INHERIT, &current->mm->flags) ?
+ PR_MDWE_NO_INHERIT : 0);
}

static int prctl_get_auxv(void __user *addr, unsigned long len)
diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h
index 6e6563e97fef..f7448d99520c 100644
--- a/tools/include/uapi/linux/prctl.h
+++ b/tools/include/uapi/linux/prctl.h
@@ -284,6 +284,7 @@ struct prctl_mm_map {
/* Memory deny write / execute */
#define PR_SET_MDWE 65
# define PR_MDWE_REFUSE_EXEC_GAIN (1UL << 0)
+# define PR_MDWE_NO_INHERIT (1UL << 1)

#define PR_GET_MDWE 66

--
2.40.1.606.ga4b1b128d6-goog