diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 6e62b8cb19c8d..d7f7db41369c7 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -5665,6 +5665,33 @@
 	reset_devices	[KNL] Force drivers to reset the underlying device
 			during initialization.
 
+	restrict_proc_mem_read= [KNL]
+			Format: {all | ptracer}
+			Allows restricting read access to /proc/*/mem files.
+			Depending on restriction level, open for reads return -EACCESS.
+			Can be one of:
+			- 'all' restricts all access unconditionally.
+			- 'ptracer' allows access only for ptracer processes.
+			If not specified, then basic file permissions continue to apply.
+
+	restrict_proc_mem_write= [KNL]
+			Format: {all | ptracer}
+			Allows restricting write access to /proc/*/mem files.
+			Depending on restriction level, open for writes return -EACCESS.
+			Can be one of:
+			- 'all' restricts all access unconditionally.
+			- 'ptracer' allows access only for ptracer processes.
+			If not specified, then basic file permissions continue to apply.
+
+	restrict_proc_mem_foll_force= [KNL]
+			Format: {all | ptracer}
+			Restricts the use of the FOLL_FORCE flag for /proc/*/mem access.
+			If restricted, the FOLL_FORCE flag will not be added to vm accesses.
+			Can be one of:
+			- 'all' restricts all access unconditionally.
+			- 'ptracer' allows access only for ptracer processes.
+			If not specified, FOLL_FORCE is always used.
+
 	resume=		[SWSUSP]
 			Specify the partition device for software suspend
 			Format:
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 18550c071d71c..c733836c42a65 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -152,6 +152,41 @@ struct pid_entry {
 		NULL, &proc_pid_attr_operations,	\
 		{ .lsmid = LSMID })
 
+/*
+ * each restrict_proc_mem_* param controls the following static branches:
+ * key[0] = restrict all writes
+ * key[1] = restrict writes except for ptracers
+ * key[2] = restrict all reads
+ * key[3] = restrict reads except for ptracers
+ * key[4] = restrict all FOLL_FORCE usage
+ * key[5] = restrict FOLL_FORCE usage except for ptracers
+ */
+DEFINE_STATIC_KEY_ARRAY_FALSE_RO(restrict_proc_mem, 6);
+
+static int __init early_restrict_proc_mem(char *buf, int offset)
+{
+	if (!buf)
+		return -EINVAL;
+
+	if (strncmp(buf, "all", 3) == 0)
+		static_branch_enable(&restrict_proc_mem[offset]);
+	else if (strncmp(buf, "ptracer", 7) == 0)
+		static_branch_enable(&restrict_proc_mem[offset + 1]);
+
+	return 0;
+}
+
+#define DEFINE_EARLY_RESTRICT_PROC_MEM(name, offset)			\
+static int __init early_restrict_proc_mem_##name(char *buf)		\
+{									\
+	return early_restrict_proc_mem(buf, offset);			\
+}									\
+early_param("restrict_proc_mem_" #name, early_restrict_proc_mem_##name)
+
+DEFINE_EARLY_RESTRICT_PROC_MEM(write, 0);
+DEFINE_EARLY_RESTRICT_PROC_MEM(read, 2);
+DEFINE_EARLY_RESTRICT_PROC_MEM(foll_force, 4);
+
 /*
  * Count the number of hardlinks for the pid_entry table, excluding the .
  * and .. links.
@@ -825,9 +860,58 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
 	return 0;
 }
 
+static bool __mem_open_current_is_ptracer(struct file *file)
+{
+	struct inode *inode = file_inode(file);
+	struct task_struct *task = get_proc_task(inode);
+	int ret = false;
+
+	if (task) {
+		rcu_read_lock();
+		if (current == ptrace_parent(task))
+			ret = true;
+		rcu_read_unlock();
+		put_task_struct(task);
+	}
+
+	return ret;
+}
+
+static int __mem_open_check_access_restriction(struct file *file)
+{
+	if (file->f_mode & FMODE_WRITE) {
+		/* Deny if writes are unconditionally disabled via param */
+		if (static_branch_unlikely(&restrict_proc_mem[0]))
+			return -EACCES;
+
+		/* Deny if writes are allowed only for ptracers via param */
+		if (static_branch_unlikely(&restrict_proc_mem[1]) &&
+		    !__mem_open_current_is_ptracer(file))
+			return -EACCES;
+
+	} else if (file->f_mode & FMODE_READ) {
+		/* Deny if reads are unconditionally disabled via param */
+		if (static_branch_unlikely(&restrict_proc_mem[2]))
+			return -EACCES;
+
+		/* Deny if reads are allowed only for ptracers via param */
+		if (static_branch_unlikely(&restrict_proc_mem[3]) &&
+		    !__mem_open_current_is_ptracer(file))
+			return -EACCES;
+	}
+
+	return 0;
+}
+
 static int mem_open(struct inode *inode, struct file *file)
 {
-	int ret = __mem_open(inode, file, PTRACE_MODE_ATTACH);
+	int ret;
+
+	ret = __mem_open_check_access_restriction(file);
+	if (ret)
+		return ret;
+
+	ret = __mem_open(inode, file, PTRACE_MODE_ATTACH);
 
 	/* OK to pass negative loff_t, we can catch out-of-range */
 	file->f_mode |= FMODE_UNSIGNED_OFFSET;
@@ -835,6 +919,20 @@ static int mem_open(struct inode *inode, struct file *file)
 	return ret;
 }
 
+static unsigned int __mem_rw_get_foll_force_flag(struct file *file)
+{
+	/* Deny if FOLL_FORCE is disabled via param */
+	if (static_branch_unlikely(&restrict_proc_mem[4]))
+		return 0;
+
+	/* Deny if FOLL_FORCE is allowed only for ptracers via param */
+	if (static_branch_unlikely(&restrict_proc_mem[5]) &&
+	    !__mem_open_current_is_ptracer(file))
+		return 0;
+
+	return FOLL_FORCE;
+}
+
 static ssize_t mem_rw(struct file *file, char __user *buf,
 			size_t count, loff_t *ppos, int write)
 {
@@ -855,7 +953,8 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
 	if (!mmget_not_zero(mm))
 		goto free;
 
-	flags = FOLL_FORCE | (write ? FOLL_WRITE : 0);
+	flags = (write ? FOLL_WRITE : 0);
+	flags |= __mem_rw_get_foll_force_flag(file);
 
 	while (count > 0) {
 		size_t this_len = min_t(size_t, count, PAGE_SIZE);
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index f5a2727ca4a9a..ba2460fe878c5 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -398,6 +398,11 @@ struct static_key_false {
 		[0 ... (count) - 1] = STATIC_KEY_FALSE_INIT,	\
 	}
 
+#define DEFINE_STATIC_KEY_ARRAY_FALSE_RO(name, count)		\
+	struct static_key_false name[count] __ro_after_init = {	\
+		[0 ... (count) - 1] = STATIC_KEY_FALSE_INIT,	\
+	}
+
 #define _DEFINE_STATIC_KEY_1(name)	DEFINE_STATIC_KEY_TRUE(name)
 #define _DEFINE_STATIC_KEY_0(name)	DEFINE_STATIC_KEY_FALSE(name)
 #define DEFINE_STATIC_KEY_MAYBE(cfg, name)			\
