Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5fe5d80 authored by Linus Torvalds's avatar Linus Torvalds Committed by H. Peter Anvin
Browse files

x86: Replace assembly access_ok() with a C variant

It turns out that the assembly variant doesn't actually produce that
good code, presumably partly because it creates a long dependency
chain with no scheduling, and partly because we cannot get a flags
result out of gcc (which could be fixed with asm goto, but it turns
out not to be worth it.)

The C code allows gcc to schedule and generate multiple (easily
predictable) branches, and as a side benefit we can really optimize
the case where the size is constant.

Link: http://lkml.kernel.org/r/CA%2B55aFzPBdbfKovMT8Edr4SmE2_=%2BOKJFac9XW2awegogTkVTA@mail.gmail.com


Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
parent 661c8019
Loading
Loading
Loading
Loading
+17 −11
Original line number Diff line number Diff line
@@ -40,22 +40,28 @@
/*
 * Test whether a block of memory is a valid user space address.
 * Returns 0 if the range is valid, nonzero otherwise.
 *
 * This is equivalent to the following test:
 * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64)
 *
 * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
 */
static inline int __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit)
{
	/*
	 * If we have used "sizeof()" for the size,
	 * we know it won't overflow the limit (but
	 * it might overflow the 'addr', so it's
	 * important to subtract the size from the
	 * limit, not add it to the address).
	 */
	if (__builtin_constant_p(size))
		return addr > limit - size;

	/* Arbitrary sizes? Be careful about overflow */
	addr += size;
	return (addr < size) || (addr > limit);
}

#define __range_not_ok(addr, size, limit)				\
({									\
	unsigned long flag, roksum;					\
	__chk_user_ptr(addr);						\
	asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"		\
	    : "=&r" (flag), "=r" (roksum)				\
	    : "1" (addr), "g" ((long)(size)),				\
	      "rm" (limit));						\
	flag;								\
	__chk_range_not_ok((unsigned long __force)(addr), size, limit); \
})

/**