Skip to content

Commit 1bd4403

Browse files
committed
unsafe_[get|put]_user: change interface to use a error target label
When I initially added the unsafe_[get|put]_user() helpers in commit 5b24a7a ("Add 'unsafe' user access functions for batched accesses"), I made the mistake of modeling the interface on our traditional __[get|put]_user() functions, which return zero on success, or -EFAULT on failure. That interface is fairly easy to use, but it's actually fairly nasty for good code generation, since it essentially forces the caller to check the error value for each access. In particular, since the error handling is already internally implemented with an exception handler, and we already use "asm goto" for various other things, we could fairly easily make the error cases just jump directly to an error label instead, and avoid the need for explicit checking after each operation. So switch the interface to pass in an error label, rather than checking the error value in the caller. Best do it now before we start growing more users (the signal handling code in particular would be a good place to use the new interface). So rather than if (unsafe_get_user(x, ptr)) ... handle error .. the interface is now unsafe_get_user(x, ptr, label); where an error during the user mode fetch will now just cause a jump to 'label' in the caller. Right now the actual _implementation_ of this all still ends up being a "if (err) goto label", and does not take advantage of any exception label tricks, but for "unsafe_put_user()" in particular it should be fairly straightforward to convert to using the exception table model. Note that "unsafe_get_user()" is much harder to convert to a clever exception table model, because current versions of gcc do not allow the use of "asm goto" (for the exception) with output values (for the actual value to be fetched). But that is hopefully not a limitation in the long term. [ Also note that it might be a good idea to switch unsafe_get_user() to actually _return_ the value it fetches from user space, but this commit only changes the error handling semantics ] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 574673c commit 1bd4403

File tree

4 files changed

+17
-18
lines changed

4 files changed

+17
-18
lines changed

arch/x86/include/asm/uaccess.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -812,21 +812,21 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
812812
#define user_access_begin() __uaccess_begin()
813813
#define user_access_end() __uaccess_end()
814814

815-
#define unsafe_put_user(x, ptr) \
816-
({ \
815+
#define unsafe_put_user(x, ptr, err_label) \
816+
do { \
817817
int __pu_err; \
818818
__put_user_size((x), (ptr), sizeof(*(ptr)), __pu_err, -EFAULT); \
819-
__builtin_expect(__pu_err, 0); \
820-
})
819+
if (unlikely(__pu_err)) goto err_label; \
820+
} while (0)
821821

822-
#define unsafe_get_user(x, ptr) \
823-
({ \
822+
#define unsafe_get_user(x, ptr, err_label) \
823+
do { \
824824
int __gu_err; \
825825
unsigned long __gu_val; \
826826
__get_user_size(__gu_val, (ptr), sizeof(*(ptr)), __gu_err, -EFAULT); \
827827
(x) = (__force __typeof__(*(ptr)))__gu_val; \
828-
__builtin_expect(__gu_err, 0); \
829-
})
828+
if (unlikely(__gu_err)) goto err_label; \
829+
} while (0)
830830

831831
#endif /* _ASM_X86_UACCESS_H */
832832

include/linux/uaccess.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
114114
#ifndef user_access_begin
115115
#define user_access_begin() do { } while (0)
116116
#define user_access_end() do { } while (0)
117-
#define unsafe_get_user(x, ptr) __get_user(x, ptr)
118-
#define unsafe_put_user(x, ptr) __put_user(x, ptr)
117+
#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0)
118+
#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)
119119
#endif
120120

121121
#endif /* __LINUX_UACCESS_H__ */

lib/strncpy_from_user.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
4040
unsigned long c, data;
4141

4242
/* Fall back to byte-at-a-time if we get a page fault */
43-
if (unlikely(unsafe_get_user(c,(unsigned long __user *)(src+res))))
44-
break;
43+
unsafe_get_user(c, (unsigned long __user *)(src+res), byte_at_a_time);
44+
4545
*(unsigned long *)(dst+res) = c;
4646
if (has_zero(c, &data, &constants)) {
4747
data = prep_zero_mask(c, data, &constants);
@@ -56,8 +56,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
5656
while (max) {
5757
char c;
5858

59-
if (unlikely(unsafe_get_user(c,src+res)))
60-
return -EFAULT;
59+
unsafe_get_user(c,src+res, efault);
6160
dst[res] = c;
6261
if (!c)
6362
return res;
@@ -76,6 +75,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
7675
* Nope: we hit the address space limit, and we still had more
7776
* characters the caller would have wanted. That's an EFAULT.
7877
*/
78+
efault:
7979
return -EFAULT;
8080
}
8181

lib/strnlen_user.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
4545
src -= align;
4646
max += align;
4747

48-
if (unlikely(unsafe_get_user(c,(unsigned long __user *)src)))
49-
return 0;
48+
unsafe_get_user(c, (unsigned long __user *)src, efault);
5049
c |= aligned_byte_mask(align);
5150

5251
for (;;) {
@@ -61,8 +60,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
6160
if (unlikely(max <= sizeof(unsigned long)))
6261
break;
6362
max -= sizeof(unsigned long);
64-
if (unlikely(unsafe_get_user(c,(unsigned long __user *)(src+res))))
65-
return 0;
63+
unsafe_get_user(c, (unsigned long __user *)(src+res), efault);
6664
}
6765
res -= align;
6866

@@ -77,6 +75,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
7775
* Nope: we hit the address space limit, and we still had more
7876
* characters the caller would have wanted. That's 0.
7977
*/
78+
efault:
8079
return 0;
8180
}
8281

0 commit comments

Comments
 (0)