Commit d1d7f01f authored by Johannes Berg's avatar Johannes Berg
Browse files

um: mark rodata read-only and implement _nofault accesses



Mark read-only data actually read-only (simple mprotect), and
to be able to test it also implement _nofault accesses. This
works by setting up a new "segv_continue" pointer in current,
and then when we hit a segfault we change the signal return
context so that we continue at that address. The code using
this sets it up so that it jumps to a label and then aborts
the access that way, returning -EFAULT.

It's possible to optimize the ___backtrack_faulted() thing by
using asm goto (compiler version dependent) and/or gcc's (not
sure if clang has it) &&label extension, but at least in one
attempt I made the && caused the compiler to not load -EFAULT
into the register in case of jumping to the &&label from the
fault handler. So leave it like this for now.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Co-developed-by: default avatarBenjamin Berg <benjamin.berg@intel.com>
Signed-off-by: default avatarBenjamin Berg <benjamin.berg@intel.com>
Link: https://patch.msgid.link/20250210160926.420133-2-benjamin@sipsolutions.net


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5550187c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ config UML
	select ARCH_HAS_KCOV
	select ARCH_HAS_STRNCPY_FROM_USER
	select ARCH_HAS_STRNLEN_USER
	select ARCH_HAS_STRICT_KERNEL_RWX
	select HAVE_ARCH_AUDITSYSCALL
	select HAVE_ARCH_KASAN if X86_64
	select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ struct thread_struct {
		} thread;
	} request;

	void *segv_continue;

	/* Contains variable sized FP registers */
	struct pt_regs regs;
};
+15 −5
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@

#include <asm/elf.h>
#include <linux/unaligned.h>
#include <sysdep/faultinfo.h>

#define __under_task_size(addr, size) \
	(((unsigned long) (addr) < TASK_SIZE) && \
@@ -44,19 +45,28 @@ static inline int __access_ok(const void __user *ptr, unsigned long size)
		 __access_ok_vsyscall(addr, size));
}

/* no pagefaults for kernel addresses in um */
#define __get_kernel_nofault(dst, src, type, err_label)			\
do {									\
	*((type *)dst) = get_unaligned((type *)(src));			\
	if (0) /* make sure the label looks used to the compiler */	\
	int __faulted;							\
									\
	___backtrack_faulted(__faulted);				\
	if (__faulted) {						\
		*((type *)dst) = (type) 0;				\
		goto err_label;						\
	}								\
	*((type *)dst) = get_unaligned((type *)(src));			\
	current->thread.segv_continue = NULL;				\
} while (0)

#define __put_kernel_nofault(dst, src, type, err_label)			\
do {									\
	put_unaligned(*((type *)src), (type *)(dst));			\
	if (0) /* make sure the label looks used to the compiler */	\
	int __faulted;							\
									\
	___backtrack_faulted(__faulted);				\
	if (__faulted)							\
		goto err_label;						\
	put_unaligned(*((type *)src), (type *)(dst));			\
	current->thread.segv_continue = NULL;				\
} while (0)

#endif
+2 −0
Original line number Diff line number Diff line
@@ -12,4 +12,6 @@ extern void arch_check_bugs(void);
extern int arch_fixup(unsigned long address, struct uml_pt_regs *regs);
extern void arch_examine_signal(int sig, struct uml_pt_regs *regs);

void mc_set_rip(void *_mc, void *target);

#endif
+1 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ extern int linux_main(int argc, char **argv, char **envp);
extern void uml_finishsetup(void);

struct siginfo;
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *);
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *, void *);

#endif

Loading