Skip to content

Commit

Permalink
uaccess: add force_uaccess_{begin,end} helpers
Browse files Browse the repository at this point in the history
Add helpers to wrap the get_fs/set_fs magic for undoing any damange done
by set_fs(KERNEL_DS).  There is no real functional benefit, but this
documents the intent of these calls better, and will allow stubbing the
functions out easily for kernels builds that do not allow address space
overrides in the future.

[[email protected]: drop two incorrect hunks, fix a commit log typo]
  Link: http://lkml.kernel.org/r/[email protected]

Signed-off-by: Christoph Hellwig <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Acked-by: Linus Torvalds <[email protected]>
Acked-by: Mark Rutland <[email protected]>
Acked-by: Greentime Hu <[email protected]>
Acked-by: Geert Uytterhoeven <[email protected]>
Cc: Nick Hu <[email protected]>
Cc: Vincent Chen <[email protected]>
Cc: Paul Walmsley <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Christoph Hellwig authored and torvalds committed Aug 12, 2020
1 parent 428e297 commit 3d13f31
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 56 deletions.
2 changes: 1 addition & 1 deletion arch/arm64/kernel/sdei.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ static __kprobes unsigned long _sdei_handler(struct pt_regs *regs,

/*
* We didn't take an exception to get here, set PAN. UAO will be cleared
* by sdei_event_handler()s set_fs(USER_DS) call.
* by sdei_event_handler()s force_uaccess_begin() call.
*/
__uaccess_enable_hw_pan();

Expand Down
6 changes: 3 additions & 3 deletions arch/m68k/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
if (vma->vm_mm == current->active_mm) {
mm_segment_t old_fs = get_fs();
set_fs(USER_DS);
mm_segment_t old_fs = force_uaccess_begin();

__flush_tlb_one(addr);
set_fs(old_fs);
force_uaccess_end(old_fs);
}
}

Expand Down
27 changes: 13 additions & 14 deletions arch/mips/kernel/unaligned.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,79 +191,78 @@ static void emulate_load_store_insn(struct pt_regs *regs,
* memory, so we need to "switch" the address limit to
* user space, so that address check can work properly.
*/
seg = get_fs();
set_fs(USER_DS);
seg = force_uaccess_begin();
switch (insn.spec3_format.func) {
case lhe_op:
if (!access_ok(addr, 2)) {
set_fs(seg);
force_uaccess_end(seg);
goto sigbus;
}
LoadHWE(addr, value, res);
if (res) {
set_fs(seg);
force_uaccess_end(seg);
goto fault;
}
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case lwe_op:
if (!access_ok(addr, 4)) {
set_fs(seg);
force_uaccess_end(seg);
goto sigbus;
}
LoadWE(addr, value, res);
if (res) {
set_fs(seg);
force_uaccess_end(seg);
goto fault;
}
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case lhue_op:
if (!access_ok(addr, 2)) {
set_fs(seg);
force_uaccess_end(seg);
goto sigbus;
}
LoadHWUE(addr, value, res);
if (res) {
set_fs(seg);
force_uaccess_end(seg);
goto fault;
}
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case she_op:
if (!access_ok(addr, 2)) {
set_fs(seg);
force_uaccess_end(seg);
goto sigbus;
}
compute_return_epc(regs);
value = regs->regs[insn.spec3_format.rt];
StoreHWE(addr, value, res);
if (res) {
set_fs(seg);
force_uaccess_end(seg);
goto fault;
}
break;
case swe_op:
if (!access_ok(addr, 4)) {
set_fs(seg);
force_uaccess_end(seg);
goto sigbus;
}
compute_return_epc(regs);
value = regs->regs[insn.spec3_format.rt];
StoreWE(addr, value, res);
if (res) {
set_fs(seg);
force_uaccess_end(seg);
goto fault;
}
break;
default:
set_fs(seg);
force_uaccess_end(seg);
goto sigill;
}
set_fs(seg);
force_uaccess_end(seg);
}
#endif
break;
Expand Down
7 changes: 3 additions & 4 deletions arch/nds32/mm/alignment.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,21 +512,20 @@ int do_unaligned_access(unsigned long addr, struct pt_regs *regs)
{
unsigned long inst;
int ret = -EFAULT;
mm_segment_t seg = get_fs();
mm_segment_t seg;

inst = get_inst(regs->ipc);

DEBUG((unalign_access_debug > 0), 1,
"Faulting addr: 0x%08lx, pc: 0x%08lx [inst: 0x%08lx ]\n", addr,
regs->ipc, inst);

set_fs(USER_DS);

seg = force_uaccess_begin();
if (inst & NDS32_16BIT_INSTRUCTION)
ret = do_16((inst >> 16) & 0xffff, regs);
else
ret = do_32(inst, regs);
set_fs(seg);
force_uaccess_end(seg);

return ret;
}
Expand Down
12 changes: 5 additions & 7 deletions arch/sh/kernel/traps_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,22 +482,20 @@ asmlinkage void do_address_error(struct pt_regs *regs,
error_code = lookup_exception_vector();
#endif

oldfs = get_fs();

if (user_mode(regs)) {
int si_code = BUS_ADRERR;
unsigned int user_action;

local_irq_enable();
inc_unaligned_user_access();

set_fs(USER_DS);
oldfs = force_uaccess_begin();
if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1),
sizeof(instruction))) {
set_fs(oldfs);
force_uaccess_end(oldfs);
goto uspace_segv;
}
set_fs(oldfs);
force_uaccess_end(oldfs);

/* shout about userspace fixups */
unaligned_fixups_notify(current, instruction, regs);
Expand All @@ -520,11 +518,11 @@ asmlinkage void do_address_error(struct pt_regs *regs,
goto uspace_segv;
}

set_fs(USER_DS);
oldfs = force_uaccess_begin();
tmp = handle_unaligned_access(instruction, regs,
&user_mem_access, 0,
address);
set_fs(oldfs);
force_uaccess_end(oldfs);

if (tmp == 0)
return; /* sorted */
Expand Down
5 changes: 2 additions & 3 deletions drivers/firmware/arm_sdei.c
Original file line number Diff line number Diff line change
Expand Up @@ -1136,15 +1136,14 @@ int sdei_event_handler(struct pt_regs *regs,
* access kernel memory.
* Do the same here because this doesn't come via the same entry code.
*/
orig_addr_limit = get_fs();
set_fs(USER_DS);
orig_addr_limit = force_uaccess_begin();

err = arg->callback(event_num, regs, arg->callback_arg);
if (err)
pr_err_ratelimited("event %u on CPU %u failed with error: %d\n",
event_num, smp_processor_id(), err);

set_fs(orig_addr_limit);
force_uaccess_end(orig_addr_limit);

return err;
}
Expand Down
18 changes: 18 additions & 0 deletions include/linux/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@

#include <asm/uaccess.h>

/*
* Force the uaccess routines to be wired up for actual userspace access,
* overriding any possible set_fs(KERNEL_DS) still lingering around. Undone
* using force_uaccess_end below.
*/
static inline mm_segment_t force_uaccess_begin(void)
{
mm_segment_t fs = get_fs();

set_fs(USER_DS);
return fs;
}

static inline void force_uaccess_end(mm_segment_t oldfs)
{
set_fs(oldfs);
}

/*
* Architectures should provide two primitives (raw_copy_{to,from}_user())
* and get rid of their private instances of copy_{to,from}_user() and
Expand Down
5 changes: 2 additions & 3 deletions kernel/events/callchain.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,9 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
if (add_mark)
perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);

fs = get_fs();
set_fs(USER_DS);
fs = force_uaccess_begin();
perf_callchain_user(&ctx, regs);
set_fs(fs);
force_uaccess_end(fs);
}
}

Expand Down
5 changes: 2 additions & 3 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -6453,10 +6453,9 @@ perf_output_sample_ustack(struct perf_output_handle *handle, u64 dump_size,

/* Data. */
sp = perf_user_stack_pointer(regs);
fs = get_fs();
set_fs(USER_DS);
fs = force_uaccess_begin();
rem = __output_copy_user(handle, (void *) sp, dump_size);
set_fs(fs);
force_uaccess_end(fs);
dyn_size = dump_size - rem;

perf_output_skip(handle, rem);
Expand Down
5 changes: 2 additions & 3 deletions kernel/kthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,8 +1258,7 @@ void kthread_use_mm(struct mm_struct *mm)
if (active_mm != mm)
mmdrop(active_mm);

to_kthread(tsk)->oldfs = get_fs();
set_fs(USER_DS);
to_kthread(tsk)->oldfs = force_uaccess_begin();
}
EXPORT_SYMBOL_GPL(kthread_use_mm);

Expand All @@ -1274,7 +1273,7 @@ void kthread_unuse_mm(struct mm_struct *mm)
WARN_ON_ONCE(!(tsk->flags & PF_KTHREAD));
WARN_ON_ONCE(!tsk->mm);

set_fs(to_kthread(tsk)->oldfs);
force_uaccess_end(to_kthread(tsk)->oldfs);

task_lock(tsk);
sync_mm_rss(mm);
Expand Down
5 changes: 2 additions & 3 deletions kernel/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,9 @@ unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
if (current->flags & PF_KTHREAD)
return 0;

fs = get_fs();
set_fs(USER_DS);
fs = force_uaccess_begin();
arch_stack_walk_user(consume_entry, &c, task_pt_regs(current));
set_fs(fs);
force_uaccess_end(fs);

return c.len;
}
Expand Down
22 changes: 10 additions & 12 deletions mm/maccess.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,14 @@ long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
{
long ret = -EFAULT;
mm_segment_t old_fs = get_fs();
mm_segment_t old_fs = force_uaccess_begin();

set_fs(USER_DS);
if (access_ok(src, size)) {
pagefault_disable();
ret = __copy_from_user_inatomic(dst, src, size);
pagefault_enable();
}
set_fs(old_fs);
force_uaccess_end(old_fs);

if (ret)
return -EFAULT;
Expand All @@ -233,15 +232,14 @@ EXPORT_SYMBOL_GPL(copy_from_user_nofault);
long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
{
long ret = -EFAULT;
mm_segment_t old_fs = get_fs();
mm_segment_t old_fs = force_uaccess_begin();

set_fs(USER_DS);
if (access_ok(dst, size)) {
pagefault_disable();
ret = __copy_to_user_inatomic(dst, src, size);
pagefault_enable();
}
set_fs(old_fs);
force_uaccess_end(old_fs);

if (ret)
return -EFAULT;
Expand Down Expand Up @@ -270,17 +268,17 @@ EXPORT_SYMBOL_GPL(copy_to_user_nofault);
long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count)
{
mm_segment_t old_fs = get_fs();
mm_segment_t old_fs;
long ret;

if (unlikely(count <= 0))
return 0;

set_fs(USER_DS);
old_fs = force_uaccess_begin();
pagefault_disable();
ret = strncpy_from_user(dst, unsafe_addr, count);
pagefault_enable();
set_fs(old_fs);
force_uaccess_end(old_fs);

if (ret >= count) {
ret = count;
Expand Down Expand Up @@ -310,14 +308,14 @@ long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
*/
long strnlen_user_nofault(const void __user *unsafe_addr, long count)
{
mm_segment_t old_fs = get_fs();
mm_segment_t old_fs;
int ret;

set_fs(USER_DS);
old_fs = force_uaccess_begin();
pagefault_disable();
ret = strnlen_user(unsafe_addr, count);
pagefault_enable();
set_fs(old_fs);
force_uaccess_end(old_fs);

return ret;
}

0 comments on commit 3d13f31

Please sign in to comment.