[Bug report] hash_name() may cross page boundary and trigger
david laight
david.laight at runbox.com
Sat Nov 29 02:45:28 PST 2025
On Sat, 29 Nov 2025 09:08:13 +0000
Al Viro <viro at zeniv.linux.org.uk> wrote:
> On Sat, Nov 29, 2025 at 12:08:17PM +0800, Xie Yuanbin wrote:
>
> > I think the `user_mode(regs)` check is necessary because the label
> > no_context actually jumps to __do_kernel_fault(), whereas page fault
> > from user mode should jump to `__do_user_fault()`.
> >
> > Alternatively, we would need to change `goto no_context` to
> > `goto bad_area`. Or perhaps I misunderstood something, please point it out.
>
> FWIW, goto bad_area has an obvious problem: uses of 'fault' value, which
> contains garbage.
>
> The cause of problem is the heuristics in get_mmap_lock_carefully():
> if (regs && !user_mode(regs)) {
> unsigned long ip = exception_ip(regs);
> if (!search_exception_tables(ip))
> return false;
> }
> trylock has failed and we are trying to decide whether it's safe to block.
> The assumption (inherited from old logics in assorted page fault handlers)
> is "by that point we know that fault in kernel mode is either an oops
> or #PF on uaccess; in the latter case we should be OK with locking mm,
> in the former we should just get to oopsing without risking deadlocks".
>
> load_unaligned_zeropad() is where that assumption breaks - there is
> an exception handler and it's not an uaccess attempt; the address is
> not going to match any VMA and we really don't want to do anything
> blocking.
Doesn't that also affect code that (ab)uses get_user() for kernel addresss?
For x86 even __get_kernel_nofault() does that.
In that case it hits a normal 'user fault' exception table entry rather
a 'special' one that could be marked as such.
>
> Note that VMA lookup will return NULL there anyway - there won't be a VMA
> for that address. What we get is exactly the same thing we'd get from
> do_bad_area(), whether we get a kernel or userland insn faulting.
>
> The minimal fix would be something like
> if (unlikely(addr >= TASK_SIZE) && !(flags & FAULT_FLAG_USER))
> goto no_context;
Is there an issue with TASK_SIZE being process dependant?
Don't you want 'the bottom of kernel addresses' not 'the top of the current process'.
David
>
> right before
> if (!(flags & FAULT_FLAG_USER))
> goto lock_mmap;
>
> in do_page_fault(). Alternatively,
> if (unlikely(addr >= TASK_SIZE)) {
> do_bad_area(addr, fsr, regs);
> return 0;
> }
> or
> if (unlikely(addr >= TASK_SIZE)) {
> fault = 0;
> code = SEGV_MAPERR;
> goto bad_area;
> }
> at the same place. Incidentally, making do_bad_area() return 0 would
> seem to make all callers happier...
>
More information about the linux-arm-kernel
mailing list