diff --git a/include/linux/eh_frame.h b/include/linux/eh_frame.h
index 3c88ab2f7858..3a61ed0ae739 100644
--- a/include/linux/eh_frame.h
+++ b/include/linux/eh_frame.h
@@ -6,6 +6,8 @@
#include <linux/srcu.h>
#include <linux/unwind_user_types.h>
+struct eh_frame_setup;
+
#ifdef CONFIG_HAVE_UNWIND_USER_EH_FRAME
struct eh_frame_section {@@ -40,6 +42,12 @@ extern int eh_frame_add_section(unsigned long eh_frame_hdr_start,
unsigned long text_start,
unsigned long text_end);
extern int eh_frame_remove_section(unsigned long eh_frame_hdr_start);
+
+extern int eh_frame_register(struct eh_frame_setup __user *user_data,
+ __kernel_size_t size);
+extern int eh_frame_unregister(struct eh_frame_setup __user *user_data,
+ __kernel_size_t size);
+
extern int eh_frame_find(unsigned long ip, struct unwind_user_frame *frame);
static inline bool current_has_eh_frame(void)
@@ -78,6 +86,18 @@ static inline int eh_frame_find(unsigned long ip, struct unwind_user_frame *fram
return -ENOSYS;
}
+static inline int eh_frame_register(struct eh_frame_setup __user *user_data,
+ __kernel_size_t size)
+{
+ return -EINVAL;
+}
+
+static inline int eh_frame_unregister(struct eh_frame_setup __user *user_data,
+ __kernel_size_t size)
+{
+ return -EINVAL;
+}
+
static inline bool current_has_eh_frame(void) { return false; }
#endif /* CONFIG_HAVE_UNWIND_USER_EH_FRAME */diff --git a/include/uapi/linux/eh_frame.h b/include/uapi/linux/eh_frame.h
new file mode 100644
index 000000000000..8cca3792f323
--- /dev/null
+++ b/include/uapi/linux/eh_frame.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_EH_FRAME_H
+#define _UAPI_LINUX_EH_FRAME_H
+
+#include <linux/types.h>
+
+struct eh_frame_setup {
+ __u64 eh_frame_hdr_start;
+ __u64 eh_frame_hdr_size;
+ __u64 text_start;
+ __u64 text_size;
+};
+
+#endif /* _UAPI_LINUX_EH_FRAME_H */diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index b6ec6f693719..801a209bab3f 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -416,4 +416,8 @@ struct prctl_mm_map {
# define PR_CFI_DISABLE _BITUL(1)
# define PR_CFI_LOCK _BITUL(2)
+/* EH_FRAME management */
+#define PR_REGISTER_EH_FRAME 82
+#define PR_UNREGISTER_EH_FRAME 83
+
#endif /* _LINUX_PRCTL_H */diff --git a/kernel/sys.c b/kernel/sys.c
index df69bd71de03..ee67badf267c 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -65,6 +65,7 @@
#include <linux/rcupdate.h>
#include <linux/uidgid.h>
#include <linux/cred.h>
+#include <linux/eh_frame.h>
#include <linux/nospec.h>
@@ -2907,6 +2908,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
if (arg3 & PR_CFI_LOCK && !(arg3 & PR_CFI_DISABLE))
error = arch_prctl_lock_branch_landing_pad_state(me);
break;
+ case PR_REGISTER_EH_FRAME:
+ if (arg4 || arg5)
+ return -EINVAL;
+ error = eh_frame_register((void __user *)arg2, arg3);
+ break;
+ case PR_UNREGISTER_EH_FRAME:
+ if (arg4 || arg5)
+ return -EINVAL;
+ error = eh_frame_unregister((void __user *)arg2, arg3);
+ break;
default:
trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);
error = -EINVAL;
diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
index d1656f5d40f1..063133d1bf97 100644
--- a/kernel/unwind/eh_frame.c
+++ b/kernel/unwind/eh_frame.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/unwind_user_types.h>
#include <linux/unwind_user_eh_frame_types.h>
+#include <uapi/linux/eh_frame.h>
#include "eh_frame.h"
#include "eh_frame_debug.h"
@@ -1609,3 +1610,48 @@ void eh_frame_free_mm(struct mm_struct *mm)
mtree_destroy(&mm->eh_frame_mt);
}
+
+int eh_frame_register(struct eh_frame_setup __user *user_data, __kernel_size_t size)
+{
+ struct eh_frame_setup data;
+ unsigned long eh_frame_hdr_end, text_end;
+
+ if (!user_data || !size)
+ return -EINVAL;
+
+ if (size != sizeof(data))
+ return -EINVAL;
+
+ if (copy_from_user(&data, user_data, sizeof(data)))
+ return -EFAULT;
+
+ if (check_add_overflow(data.eh_frame_hdr_start, data.eh_frame_hdr_size,
+ &eh_frame_hdr_end))
+ return -EINVAL;
+
+ if (check_add_overflow(data.text_start, data.text_size, &text_end))
+ return -EINVAL;
+
+ return eh_frame_add_section(data.eh_frame_hdr_start, eh_frame_hdr_end,
+ data.text_start, text_end);
+}
+
+int eh_frame_unregister(struct eh_frame_setup __user *user_data, __kernel_size_t size)
+{
+ struct eh_frame_setup data;
+
+ if (!user_data || !size)
+ return -EINVAL;
+
+ if (size != sizeof(data))
+ return -EINVAL;
+
+ if (copy_from_user(&data, user_data, sizeof(data)))
+ return -EFAULT;
+
+ /* Unregister only uses eh_frame_hdr_start */
+ if (data.eh_frame_hdr_size || data.text_start || data.text_size)
+ return -EINVAL;
+
+ return eh_frame_remove_section(data.eh_frame_hdr_start);
+}--
2.53.0