Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 8137/1: fix get_user BE behavior for target variable with size of 8 bytes

e38361d 'ARM: 8091/2: add get_user() support for 8 byte types' commit
broke V7 BE get_user call when target var size is 64 bit, but '*ptr' size
is 32 bit or smaller. e38361d changed type of __r2 from 'register
unsigned long' to 'register typeof(x) __r2 asm("r2")' i.e before the change
even when target variable size was 64 bit, __r2 was still 32 bit.
But after e38361d commit, for target var of 64 bit size, __r2 became 64
bit and now it should occupy 2 registers r2, and r3. The issue in BE case
that r3 register is least significant word of __r2 and r2 register is most
significant word of __r2. But __get_user_4 still copies result into r2 (most
significant word of __r2). Subsequent code copies from __r2 into x, but
for situation described it will pick up only garbage from r3 register.

Special __get_user_64t_(124) functions are introduced. They are similar to
corresponding __get_user_(124) function but result stored in r3 register
(lsw in case of 64 bit __r2 in BE image). Those function are used by
get_user macro in case of BE and target var size is 64bit.

Also changed __get_user_lo8 name into __get_user_32t_8 to get consistent
naming accross all cases.

Signed-off-by: Victor Kamensky <victor.kamensky@linaro.org>
Suggested-by: Daniel Thompson <daniel.thompson@linaro.org>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Victor Kamensky and committed by
Russell King
d9981380 e918a62a

+75 -11
+39 -9
arch/arm/include/asm/uaccess.h
··· 107 107 extern int __get_user_1(void *); 108 108 extern int __get_user_2(void *); 109 109 extern int __get_user_4(void *); 110 - extern int __get_user_lo8(void *); 110 + extern int __get_user_32t_8(void *); 111 111 extern int __get_user_8(void *); 112 + extern int __get_user_64t_1(void *); 113 + extern int __get_user_64t_2(void *); 114 + extern int __get_user_64t_4(void *); 112 115 113 116 #define __GUP_CLOBBER_1 "lr", "cc" 114 117 #ifdef CONFIG_CPU_USE_DOMAINS ··· 120 117 #define __GUP_CLOBBER_2 "lr", "cc" 121 118 #endif 122 119 #define __GUP_CLOBBER_4 "lr", "cc" 123 - #define __GUP_CLOBBER_lo8 "lr", "cc" 120 + #define __GUP_CLOBBER_32t_8 "lr", "cc" 124 121 #define __GUP_CLOBBER_8 "lr", "cc" 125 122 126 123 #define __get_user_x(__r2,__p,__e,__l,__s) \ ··· 134 131 135 132 /* narrowing a double-word get into a single 32bit word register: */ 136 133 #ifdef __ARMEB__ 137 - #define __get_user_xb(__r2, __p, __e, __l, __s) \ 138 - __get_user_x(__r2, __p, __e, __l, lo8) 134 + #define __get_user_x_32t(__r2, __p, __e, __l, __s) \ 135 + __get_user_x(__r2, __p, __e, __l, 32t_8) 139 136 #else 140 - #define __get_user_xb __get_user_x 137 + #define __get_user_x_32t __get_user_x 141 138 #endif 139 + 140 + /* 141 + * storing result into proper least significant word of 64bit target var, 142 + * different only for big endian case where 64 bit __r2 lsw is r3: 143 + */ 144 + #ifdef __ARMEB__ 145 + #define __get_user_x_64t(__r2, __p, __e, __l, __s) \ 146 + __asm__ __volatile__ ( \ 147 + __asmeq("%0", "r0") __asmeq("%1", "r2") \ 148 + __asmeq("%3", "r1") \ 149 + "bl __get_user_64t_" #__s \ 150 + : "=&r" (__e), "=r" (__r2) \ 151 + : "0" (__p), "r" (__l) \ 152 + : __GUP_CLOBBER_##__s) 153 + #else 154 + #define __get_user_x_64t __get_user_x 155 + #endif 156 + 142 157 143 158 #define __get_user_check(x,p) \ 144 159 ({ \ ··· 167 146 register int __e asm("r0"); \ 168 147 switch (sizeof(*(__p))) { \ 169 148 case 1: \ 170 - __get_user_x(__r2, __p, __e, __l, 1); \ 149 + if (sizeof((x)) >= 8) \ 150 + __get_user_x_64t(__r2, __p, __e, __l, 1); \ 151 + else \ 152 + __get_user_x(__r2, __p, __e, __l, 1); \ 171 153 break; \ 172 154 case 2: \ 173 - __get_user_x(__r2, __p, __e, __l, 2); \ 155 + if (sizeof((x)) >= 8) \ 156 + __get_user_x_64t(__r2, __p, __e, __l, 2); \ 157 + else \ 158 + __get_user_x(__r2, __p, __e, __l, 2); \ 174 159 break; \ 175 160 case 4: \ 176 - __get_user_x(__r2, __p, __e, __l, 4); \ 161 + if (sizeof((x)) >= 8) \ 162 + __get_user_x_64t(__r2, __p, __e, __l, 4); \ 163 + else \ 164 + __get_user_x(__r2, __p, __e, __l, 4); \ 177 165 break; \ 178 166 case 8: \ 179 167 if (sizeof((x)) < 8) \ 180 - __get_user_xb(__r2, __p, __e, __l, 4); \ 168 + __get_user_x_32t(__r2, __p, __e, __l, 4); \ 181 169 else \ 182 170 __get_user_x(__r2, __p, __e, __l, 8); \ 183 171 break; \
+36 -2
arch/arm/lib/getuser.S
··· 80 80 ENDPROC(__get_user_8) 81 81 82 82 #ifdef __ARMEB__ 83 - ENTRY(__get_user_lo8) 83 + ENTRY(__get_user_32t_8) 84 84 check_uaccess r0, 8, r1, r2, __get_user_bad 85 85 #ifdef CONFIG_CPU_USE_DOMAINS 86 86 add r0, r0, #4 ··· 90 90 #endif 91 91 mov r0, #0 92 92 ret lr 93 - ENDPROC(__get_user_lo8) 93 + ENDPROC(__get_user_32t_8) 94 + 95 + ENTRY(__get_user_64t_1) 96 + check_uaccess r0, 1, r1, r2, __get_user_bad8 97 + 8: TUSER(ldrb) r3, [r0] 98 + mov r0, #0 99 + ret lr 100 + ENDPROC(__get_user_64t_1) 101 + 102 + ENTRY(__get_user_64t_2) 103 + check_uaccess r0, 2, r1, r2, __get_user_bad8 104 + #ifdef CONFIG_CPU_USE_DOMAINS 105 + rb .req ip 106 + 9: ldrbt r3, [r0], #1 107 + 10: ldrbt rb, [r0], #0 108 + #else 109 + rb .req r0 110 + 9: ldrb r3, [r0] 111 + 10: ldrb rb, [r0, #1] 112 + #endif 113 + orr r3, rb, r3, lsl #8 114 + mov r0, #0 115 + ret lr 116 + ENDPROC(__get_user_64t_2) 117 + 118 + ENTRY(__get_user_64t_4) 119 + check_uaccess r0, 4, r1, r2, __get_user_bad8 120 + 11: TUSER(ldr) r3, [r0] 121 + mov r0, #0 122 + ret lr 123 + ENDPROC(__get_user_64t_4) 94 124 #endif 95 125 96 126 __get_user_bad8: ··· 141 111 .long 6b, __get_user_bad8 142 112 #ifdef __ARMEB__ 143 113 .long 7b, __get_user_bad 114 + .long 8b, __get_user_bad8 115 + .long 9b, __get_user_bad8 116 + .long 10b, __get_user_bad8 117 + .long 11b, __get_user_bad8 144 118 #endif 145 119 .popsection