um: fix shutdown __inittext access

Since first moving __init text into the right place in commit
8966ed0b2d ("um: move init text between __init_begin/end")
and then making all init sections inaccessible, shutdown and
reboot crash instead of working correctly, for the obvious
reason that the functions are __init but run on the way out.

Removing the __init markers would work in some way, but that
would bubble through a lot of functions.

Instead, mark the relevant functions __noreturn (including
longjmp() since that's now needed so start_idle_thread() can
be __noreturn), and split out the part of main() that's done
on the way out into a new os_exit() function that's called
directly.

Reported-by: Hajime Tazaki <thehajime@gmail.com>
Closes: https://lore.kernel.org/linux-um/m24ifc4v6m.wl-thehajime@gmail.com/
Fixes: a418ab8fdd ("um: mprotect() __init memory")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Tested-by: Hajime Tazaki <thehajime@gmail.com>
Link: https://patch.msgid.link/20260928093934.ca7af44c59ca.I470ece16d176ea704aa6e5767b755055d106da35@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-09-28 09:39:35 +02:00
parent 0e70b1de88
commit 2f88f5689d
8 changed files with 30 additions and 23 deletions

View File

@ -29,6 +29,7 @@
#ifndef __ASSEMBLER__
#include <linux/compiler_types.h>
#include <sysdep/ptrace.h>
struct task_struct;
@ -46,7 +47,7 @@ extern unsigned long brk_start;
extern unsigned long stub_start;
extern int linux_main(int argc, char **argv, char **envp);
extern void __noreturn linux_main(int argc, char **argv, char **envp);
extern void uml_finishsetup(void);
struct siginfo;

View File

@ -6,6 +6,7 @@
#ifndef __KERN_UTIL_H__
#define __KERN_UTIL_H__
#include <linux/compiler_types.h>
#include <sysdep/ptrace.h>
#include <sysdep/faultinfo.h>
@ -37,7 +38,7 @@ extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs
extern void uml_pm_wake(void);
extern int start_uml(void);
extern void __noreturn start_uml(void);
extern void uml_cleanup(void);
extern void do_uml_exitcalls(void);

View File

@ -2,11 +2,12 @@
#ifndef __UML_LONGJMP_H
#define __UML_LONGJMP_H
#include <linux/compiler_types.h>
#include <sysdep/archsetjmp.h>
#include <os.h>
extern int setjmp(jmp_buf);
extern void longjmp(jmp_buf, int);
extern void __noreturn longjmp(jmp_buf, int);
#define UML_LONGJMP(buf, val) do { \
longjmp(*buf, val); \

View File

@ -15,6 +15,7 @@
#ifndef __UM_HOST__
#include <linux/types.h>
#else
#include <stdbool.h>
#include <sys/types.h>
#endif
@ -196,6 +197,9 @@ extern int create_mem_file(unsigned long long len);
/* tlb.c */
extern void report_enomem(void);
/* main.c */
extern void __noreturn os_exit(bool reboot);
/* process.c */
pid_t os_reap_child(void);
extern void os_alarm_process(int pid);
@ -295,7 +299,7 @@ extern int start_userspace(struct mm_id *mm_id);
extern void userspace(struct uml_pt_regs *regs);
extern void new_thread(void *stack, jmp_buf *buf, void (*handler)(void));
extern void switch_threads(jmp_buf *me, jmp_buf *you);
extern int start_idle_thread(void *stack, jmp_buf *switch_buf);
extern void __noreturn start_idle_thread(void *stack, jmp_buf *switch_buf);
extern void initial_thread_cb_skas(void (*proc)(void *),
void *arg);
extern void halt_skas(void);

View File

@ -27,7 +27,7 @@ static int __init start_kernel_proc(void *unused)
char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
int __init start_uml(void)
void __init start_uml(void)
{
stack_protections((unsigned long) &cpu_irqstacks[0]);
set_sigstack(cpu_irqstacks[0], THREAD_SIZE);
@ -36,8 +36,8 @@ int __init start_uml(void)
init_task.thread.request.thread.proc = start_kernel_proc;
init_task.thread.request.thread.arg = NULL;
return start_idle_thread(task_stack_page(&init_task),
&init_task.thread.switch_buf);
start_idle_thread(task_stack_page(&init_task),
&init_task.thread.switch_buf);
}
static DEFINE_SPINLOCK(initial_jmpbuf_spinlock);

View File

@ -308,7 +308,7 @@ static unsigned long __init get_top_address(char **envp)
return PAGE_ALIGN(top_addr + 1);
}
int __init linux_main(int argc, char **argv, char **envp)
void __init linux_main(int argc, char **argv, char **envp)
{
unsigned long avail, diff;
unsigned long virtmem_size, max_physmem;
@ -401,7 +401,7 @@ int __init linux_main(int argc, char **argv, char **envp)
os_flush_stdout();
return start_uml();
start_uml();
}
int __init __weak read_initrd(void)

View File

@ -100,10 +100,11 @@ static void __init setup_env_path(void)
}
}
static char **new_argv;
int __init main(int argc, char **argv, char **envp)
{
char **new_argv;
int ret, i, err;
int ret, i;
/* Disable randomization and re-exec if it was changed successfully */
ret = personality(PER_LINUX | ADDR_NO_RANDOMIZE);
@ -150,7 +151,12 @@ int __init main(int argc, char **argv, char **envp)
scan_elf_aux(envp);
change_sig(SIGPIPE, 0);
ret = linux_main(argc, argv, envp);
linux_main(argc, argv, envp);
}
void os_exit(bool reboot)
{
int err;
/*
* Disable SIGPROF - I have no idea why libc doesn't do this or turn
@ -183,12 +189,11 @@ int __init main(int argc, char **argv, char **envp)
os_info("\n");
/* Reboot */
if (ret) {
if (reboot) {
execvp(new_argv[0], new_argv);
perror("Failed to exec kernel");
ret = 1;
}
return uml_exitcode;
exit(uml_exitcode);
}
extern void *__real_malloc(int);

View File

@ -816,7 +816,7 @@ static __thread void (*cb_proc)(void *arg);
static __thread void *cb_arg;
static __thread jmp_buf *cb_back;
int start_idle_thread(void *stack, jmp_buf *switch_buf)
void start_idle_thread(void *stack, jmp_buf *switch_buf)
{
int n;
@ -843,21 +843,16 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf)
break;
case INIT_JMP_HALT:
kmalloc_ok = 0;
return 0;
os_exit(false);
case INIT_JMP_REBOOT:
kmalloc_ok = 0;
return 1;
os_exit(true);
default:
printk(UM_KERN_ERR "Bad sigsetjmp return in %s - %d\n",
__func__, n);
fatal_sigsegv();
}
longjmp(*switch_buf, 1);
/* unreachable */
printk(UM_KERN_ERR "impossible long jump!");
fatal_sigsegv();
return 0;
}
void initial_thread_cb_skas(void (*proc)(void *), void *arg)