Skip to content

Commit d1d7f01

Browse files
committed
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: Johannes Berg <johannes.berg@intel.com> Co-developed-by: Benjamin Berg <benjamin.berg@intel.com> Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Link: https://patch.msgid.link/20250210160926.420133-2-benjamin@sipsolutions.net Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 5550187 commit d1d7f01

File tree

15 files changed

+108
-23
lines changed

15 files changed

+108
-23
lines changed

arch/um/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ config UML
1212
select ARCH_HAS_KCOV
1313
select ARCH_HAS_STRNCPY_FROM_USER
1414
select ARCH_HAS_STRNLEN_USER
15+
select ARCH_HAS_STRICT_KERNEL_RWX
1516
select HAVE_ARCH_AUDITSYSCALL
1617
select HAVE_ARCH_KASAN if X86_64
1718
select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN

arch/um/include/asm/processor-generic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ struct thread_struct {
3131
} thread;
3232
} request;
3333

34+
void *segv_continue;
35+
3436
/* Contains variable sized FP registers */
3537
struct pt_regs regs;
3638
};

arch/um/include/asm/uaccess.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <asm/elf.h>
1111
#include <linux/unaligned.h>
12+
#include <sysdep/faultinfo.h>
1213

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

47-
/* no pagefaults for kernel addresses in um */
4848
#define __get_kernel_nofault(dst, src, type, err_label) \
4949
do { \
50-
*((type *)dst) = get_unaligned((type *)(src)); \
51-
if (0) /* make sure the label looks used to the compiler */ \
50+
int __faulted; \
51+
\
52+
___backtrack_faulted(__faulted); \
53+
if (__faulted) { \
54+
*((type *)dst) = (type) 0; \
5255
goto err_label; \
56+
} \
57+
*((type *)dst) = get_unaligned((type *)(src)); \
58+
current->thread.segv_continue = NULL; \
5359
} while (0)
5460

5561
#define __put_kernel_nofault(dst, src, type, err_label) \
5662
do { \
57-
put_unaligned(*((type *)src), (type *)(dst)); \
58-
if (0) /* make sure the label looks used to the compiler */ \
63+
int __faulted; \
64+
\
65+
___backtrack_faulted(__faulted); \
66+
if (__faulted) \
5967
goto err_label; \
68+
put_unaligned(*((type *)src), (type *)(dst)); \
69+
current->thread.segv_continue = NULL; \
6070
} while (0)
6171

6272
#endif

arch/um/include/shared/arch.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ extern void arch_check_bugs(void);
1212
extern int arch_fixup(unsigned long address, struct uml_pt_regs *regs);
1313
extern void arch_examine_signal(int sig, struct uml_pt_regs *regs);
1414

15+
void mc_set_rip(void *_mc, void *target);
16+
1517
#endif

arch/um/include/shared/as-layout.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extern int linux_main(int argc, char **argv, char **envp);
5050
extern void uml_finishsetup(void);
5151

5252
struct siginfo;
53-
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *);
53+
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *, void *);
5454

5555
#endif
5656

arch/um/include/shared/irq_user.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ enum um_irq_type {
1515
};
1616

1717
struct siginfo;
18-
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
18+
extern void sigio_handler(int sig, struct siginfo *unused_si,
19+
struct uml_pt_regs *regs, void *mc);
1920
void sigio_run_timetravel_handlers(void);
2021
extern void free_irq_by_fd(int fd);
2122
extern void deactivate_fd(int fd, int irqnum);

arch/um/include/shared/kern_util.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ extern void free_stack(unsigned long stack, int order);
2424
struct pt_regs;
2525
extern void do_signal(struct pt_regs *regs);
2626
extern void interrupt_end(void);
27-
extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs);
27+
extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs,
28+
void *mc);
2829

2930
extern unsigned long segv(struct faultinfo fi, unsigned long ip,
30-
int is_user, struct uml_pt_regs *regs);
31+
int is_user, struct uml_pt_regs *regs,
32+
void *mc);
3133
extern int handle_page_fault(unsigned long address, unsigned long ip,
3234
int is_write, int is_user, int *code_out);
3335

@@ -59,8 +61,10 @@ extern unsigned long from_irq_stack(int nested);
5961

6062
extern int singlestepping(void);
6163

62-
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
63-
extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
64+
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
65+
void *mc);
66+
extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
67+
void *mc);
6468
extern void fatal_sigsegv(void) __attribute__ ((noreturn));
6569

6670
void um_idle_sleep(void);

arch/um/kernel/irq.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ static void _sigio_handler(struct uml_pt_regs *regs,
236236
free_irqs();
237237
}
238238

239-
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
239+
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
240+
void *mc)
240241
{
241242
preempt_disable();
242243
_sigio_handler(regs, irqs_suspended);

arch/um/kernel/mem.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <linux/mm.h>
1010
#include <linux/swap.h>
1111
#include <linux/slab.h>
12+
#include <linux/init.h>
13+
#include <asm/sections.h>
1214
#include <asm/page.h>
1315
#include <asm/pgalloc.h>
1416
#include <as-layout.h>
@@ -241,3 +243,11 @@ static const pgprot_t protection_map[16] = {
241243
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED
242244
};
243245
DECLARE_VM_GET_PAGE_PROT
246+
247+
void mark_rodata_ro(void)
248+
{
249+
unsigned long rodata_start = PFN_ALIGN(__start_rodata);
250+
unsigned long rodata_end = PFN_ALIGN(__end_rodata);
251+
252+
os_protect_memory((void *)rodata_start, rodata_end - rodata_start, 1, 0, 0);
253+
}

arch/um/kernel/trap.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <kern_util.h>
1717
#include <os.h>
1818
#include <skas.h>
19+
#include <arch.h>
1920

2021
/*
2122
* Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
@@ -175,12 +176,14 @@ void fatal_sigsegv(void)
175176
* @sig: the signal number
176177
* @unused_si: the signal info struct; unused in this handler
177178
* @regs: the ptrace register information
179+
* @mc: the mcontext of the signal
178180
*
179181
* The handler first extracts the faultinfo from the UML ptrace regs struct.
180182
* If the userfault did not happen in an UML userspace process, bad_segv is called.
181183
* Otherwise the signal did happen in a cloned userspace process, handle it.
182184
*/
183-
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
185+
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
186+
void *mc)
184187
{
185188
struct faultinfo * fi = UPT_FAULTINFO(regs);
186189

@@ -189,7 +192,7 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
189192
bad_segv(*fi, UPT_IP(regs));
190193
return;
191194
}
192-
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs);
195+
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs, mc);
193196
}
194197

195198
/*
@@ -199,7 +202,7 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
199202
* give us bad data!
200203
*/
201204
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
202-
struct uml_pt_regs *regs)
205+
struct uml_pt_regs *regs, void *mc)
203206
{
204207
int si_code;
205208
int err;
@@ -223,6 +226,19 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
223226
goto out;
224227
}
225228
else if (current->mm == NULL) {
229+
if (current->pagefault_disabled) {
230+
if (!mc) {
231+
show_regs(container_of(regs, struct pt_regs, regs));
232+
panic("Segfault with pagefaults disabled but no mcontext");
233+
}
234+
if (!current->thread.segv_continue) {
235+
show_regs(container_of(regs, struct pt_regs, regs));
236+
panic("Segfault without recovery target");
237+
}
238+
mc_set_rip(mc, current->thread.segv_continue);
239+
current->thread.segv_continue = NULL;
240+
goto out;
241+
}
226242
show_regs(container_of(regs, struct pt_regs, regs));
227243
panic("Segfault with no mm");
228244
}
@@ -274,7 +290,8 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
274290
return 0;
275291
}
276292

277-
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
293+
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs,
294+
void *mc)
278295
{
279296
int code, err;
280297
if (!UPT_IS_USER(regs)) {
@@ -302,7 +319,8 @@ void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
302319
}
303320
}
304321

305-
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
322+
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
323+
void *mc)
306324
{
307325
do_IRQ(WINCH_IRQ, regs);
308326
}

0 commit comments

Comments
 (0)