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

s390/uaccess: use exception handler to zero result on get_user() failure

Historically the uaccess code pre-initializes the result of get_user()
(and now also __get_kernel_nofault()) to zero and uses the result as
input parameter for inline assemblies. This is different to what most,
if not all, other architectures are doing, which set the result to
zero within the exception handler in case of a fault.

Use the new extable mechanism and handle zeroing of the result within
the exception handler in case of a fault.

Signed-off-by: Heiko Carstens <hca@linux.ibm.com>

+143 -59
+63 -28
arch/s390/include/asm/asm-extable.h
··· 3 3 #define __ASM_EXTABLE_H 4 4 5 5 #include <linux/stringify.h> 6 + #include <linux/bits.h> 6 7 #include <asm/asm-const.h> 7 8 8 - #define EX_TYPE_NONE 0 9 - #define EX_TYPE_FIXUP 1 10 - #define EX_TYPE_BPF 2 11 - #define EX_TYPE_UACCESS 3 9 + #define EX_TYPE_NONE 0 10 + #define EX_TYPE_FIXUP 1 11 + #define EX_TYPE_BPF 2 12 + #define EX_TYPE_UA_STORE 3 13 + #define EX_TYPE_UA_LOAD_MEM 4 14 + #define EX_TYPE_UA_LOAD_REG 5 15 + 16 + #define EX_DATA_REG_ERR_SHIFT 0 17 + #define EX_DATA_REG_ERR GENMASK(3, 0) 18 + 19 + #define EX_DATA_REG_ADDR_SHIFT 4 20 + #define EX_DATA_REG_ADDR GENMASK(7, 4) 21 + 22 + #define EX_DATA_LEN_SHIFT 8 23 + #define EX_DATA_LEN GENMASK(11, 8) 12 24 13 25 #define __EX_TABLE(_section, _fault, _target, _type) \ 14 26 stringify_in_c(.section _section,"a";) \ ··· 31 19 stringify_in_c(.short 0;) \ 32 20 stringify_in_c(.previous) 33 21 34 - #define __EX_TABLE_UA(_section, _fault, _target, _type, _reg) \ 35 - stringify_in_c(.section _section,"a";) \ 36 - stringify_in_c(.align 4;) \ 37 - stringify_in_c(.long (_fault) - .;) \ 38 - stringify_in_c(.long (_target) - .;) \ 39 - stringify_in_c(.short (_type);) \ 40 - stringify_in_c(.macro extable_reg reg;) \ 41 - stringify_in_c(.set .Lfound, 0;) \ 42 - stringify_in_c(.set .Lregnr, 0;) \ 43 - stringify_in_c(.irp rs,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15;) \ 44 - stringify_in_c(.ifc "\reg", "%%\rs";) \ 45 - stringify_in_c(.set .Lfound, 1;) \ 46 - stringify_in_c(.short .Lregnr;) \ 47 - stringify_in_c(.endif;) \ 48 - stringify_in_c(.set .Lregnr, .Lregnr+1;) \ 49 - stringify_in_c(.endr;) \ 50 - stringify_in_c(.ifne (.Lfound != 1);) \ 51 - stringify_in_c(.error "extable_reg: bad register argument";) \ 52 - stringify_in_c(.endif;) \ 53 - stringify_in_c(.endm;) \ 54 - stringify_in_c(extable_reg _reg;) \ 55 - stringify_in_c(.purgem extable_reg;) \ 22 + #define __EX_TABLE_UA(_section, _fault, _target, _type, _regerr, _regaddr, _len)\ 23 + stringify_in_c(.section _section,"a";) \ 24 + stringify_in_c(.align 4;) \ 25 + stringify_in_c(.long (_fault) - .;) \ 26 + stringify_in_c(.long (_target) - .;) \ 27 + stringify_in_c(.short (_type);) \ 28 + stringify_in_c(.macro extable_reg regerr, regaddr;) \ 29 + stringify_in_c(.set .Lfound, 0;) \ 30 + stringify_in_c(.set .Lcurr, 0;) \ 31 + stringify_in_c(.irp rs,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;) \ 32 + stringify_in_c( .ifc "\regerr", "%%r\rs";) \ 33 + stringify_in_c( .set .Lfound, 1;) \ 34 + stringify_in_c( .set .Lregerr, .Lcurr;) \ 35 + stringify_in_c( .endif;) \ 36 + stringify_in_c( .set .Lcurr, .Lcurr+1;) \ 37 + stringify_in_c(.endr;) \ 38 + stringify_in_c(.ifne (.Lfound != 1);) \ 39 + stringify_in_c( .error "extable_reg: bad register argument1";) \ 40 + stringify_in_c(.endif;) \ 41 + stringify_in_c(.set .Lfound, 0;) \ 42 + stringify_in_c(.set .Lcurr, 0;) \ 43 + stringify_in_c(.irp rs,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;) \ 44 + stringify_in_c( .ifc "\regaddr", "%%r\rs";) \ 45 + stringify_in_c( .set .Lfound, 1;) \ 46 + stringify_in_c( .set .Lregaddr, .Lcurr;) \ 47 + stringify_in_c( .endif;) \ 48 + stringify_in_c( .set .Lcurr, .Lcurr+1;) \ 49 + stringify_in_c(.endr;) \ 50 + stringify_in_c(.ifne (.Lfound != 1);) \ 51 + stringify_in_c( .error "extable_reg: bad register argument2";) \ 52 + stringify_in_c(.endif;) \ 53 + stringify_in_c(.short .Lregerr << EX_DATA_REG_ERR_SHIFT | \ 54 + .Lregaddr << EX_DATA_REG_ADDR_SHIFT | \ 55 + _len << EX_DATA_LEN_SHIFT;) \ 56 + stringify_in_c(.endm;) \ 57 + stringify_in_c(extable_reg _regerr,_regaddr;) \ 58 + stringify_in_c(.purgem extable_reg;) \ 56 59 stringify_in_c(.previous) 57 60 58 61 #define EX_TABLE(_fault, _target) \ 59 62 __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FIXUP) 63 + 60 64 #define EX_TABLE_AMODE31(_fault, _target) \ 61 65 __EX_TABLE(.amode31.ex_table, _fault, _target, EX_TYPE_FIXUP) 62 - #define EX_TABLE_UA(_fault, _target, _reg) \ 63 - __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UACCESS, _reg) 66 + 67 + #define EX_TABLE_UA_STORE(_fault, _target, _regerr) \ 68 + __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UA_STORE, _regerr, _regerr, 0) 69 + 70 + #define EX_TABLE_UA_LOAD_MEM(_fault, _target, _regerr, _regmem, _len) \ 71 + __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_MEM, _regerr, _regmem, _len) 72 + 73 + #define EX_TABLE_UA_LOAD_REG(_fault, _target, _regerr, _regzero) \ 74 + __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REG, _regerr, _regzero, 0) 64 75 65 76 #endif /* __ASM_EXTABLE_H */
+45 -27
arch/s390/include/asm/uaccess.h
··· 80 80 }; 81 81 }; 82 82 83 - #define __put_get_user_asm(to, from, size, oac_spec) \ 83 + #define __put_user_asm(to, from, size) \ 84 84 ({ \ 85 + union oac __oac_spec = { \ 86 + .oac1.as = PSW_BITS_AS_SECONDARY, \ 87 + .oac1.a = 1, \ 88 + }; \ 85 89 int __rc; \ 86 90 \ 87 91 asm volatile( \ ··· 93 89 "0: mvcos %[_to],%[_from],%[_size]\n" \ 94 90 "1: xr %[rc],%[rc]\n" \ 95 91 "2:\n" \ 96 - EX_TABLE_UA(0b,2b,%[rc]) EX_TABLE_UA(1b,2b,%[rc]) \ 92 + EX_TABLE_UA_STORE(0b, 2b, %[rc]) \ 93 + EX_TABLE_UA_STORE(1b, 2b, %[rc]) \ 97 94 : [rc] "=&d" (__rc), [_to] "+Q" (*(to)) \ 98 95 : [_size] "d" (size), [_from] "Q" (*(from)), \ 99 - [spec] "d" (oac_spec.val) \ 96 + [spec] "d" (__oac_spec.val) \ 100 97 : "cc", "0"); \ 101 98 __rc; \ 102 99 }) 103 - 104 - #define __put_user_asm(to, from, size) \ 105 - __put_get_user_asm(to, from, size, ((union oac) { \ 106 - .oac1.as = PSW_BITS_AS_SECONDARY, \ 107 - .oac1.a = 1 \ 108 - })) 109 - 110 - #define __get_user_asm(to, from, size) \ 111 - __put_get_user_asm(to, from, size, ((union oac) { \ 112 - .oac2.as = PSW_BITS_AS_SECONDARY, \ 113 - .oac2.a = 1 \ 114 - })) \ 115 100 116 101 static __always_inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) 117 102 { ··· 133 140 } 134 141 return rc; 135 142 } 143 + 144 + #define __get_user_asm(to, from, size) \ 145 + ({ \ 146 + union oac __oac_spec = { \ 147 + .oac2.as = PSW_BITS_AS_SECONDARY, \ 148 + .oac2.a = 1, \ 149 + }; \ 150 + int __rc; \ 151 + \ 152 + asm volatile( \ 153 + " lr 0,%[spec]\n" \ 154 + "0: mvcos 0(%[_to]),%[_from],%[_size]\n" \ 155 + "1: xr %[rc],%[rc]\n" \ 156 + "2:\n" \ 157 + EX_TABLE_UA_LOAD_MEM(0b, 2b, %[rc], %[_to], %[_ksize]) \ 158 + EX_TABLE_UA_LOAD_MEM(1b, 2b, %[rc], %[_to], %[_ksize]) \ 159 + : [rc] "=&d" (__rc), "=Q" (*(to)) \ 160 + : [_size] "d" (size), [_from] "Q" (*(from)), \ 161 + [spec] "d" (__oac_spec.val), [_to] "a" (to), \ 162 + [_ksize] "K" (size) \ 163 + : "cc", "0"); \ 164 + __rc; \ 165 + }) 136 166 137 167 static __always_inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size) 138 168 { ··· 226 210 __chk_user_ptr(ptr); \ 227 211 switch (sizeof(*(ptr))) { \ 228 212 case 1: { \ 229 - unsigned char __x = 0; \ 213 + unsigned char __x; \ 230 214 __gu_err = __get_user_fn(&__x, ptr, \ 231 215 sizeof(*(ptr))); \ 232 216 (x) = *(__force __typeof__(*(ptr)) *) &__x; \ 233 217 break; \ 234 218 }; \ 235 219 case 2: { \ 236 - unsigned short __x = 0; \ 220 + unsigned short __x; \ 237 221 __gu_err = __get_user_fn(&__x, ptr, \ 238 222 sizeof(*(ptr))); \ 239 223 (x) = *(__force __typeof__(*(ptr)) *) &__x; \ 240 224 break; \ 241 225 }; \ 242 226 case 4: { \ 243 - unsigned int __x = 0; \ 227 + unsigned int __x; \ 244 228 __gu_err = __get_user_fn(&__x, ptr, \ 245 229 sizeof(*(ptr))); \ 246 230 (x) = *(__force __typeof__(*(ptr)) *) &__x; \ 247 231 break; \ 248 232 }; \ 249 233 case 8: { \ 250 - unsigned long long __x = 0; \ 234 + unsigned long __x; \ 251 235 __gu_err = __get_user_fn(&__x, ptr, \ 252 236 sizeof(*(ptr))); \ 253 237 (x) = *(__force __typeof__(*(ptr)) *) &__x; \ ··· 297 281 "0: " insn " %[_val],%[_to]\n" \ 298 282 "1: xr %[rc],%[rc]\n" \ 299 283 "2:\n" \ 300 - EX_TABLE_UA(0b,2b,%0) EX_TABLE_UA(1b,2b,%0) \ 284 + EX_TABLE_UA_STORE(0b, 2b, %[rc]) \ 285 + EX_TABLE_UA_STORE(1b, 2b, %[rc]) \ 301 286 : [rc] "=d" (__rc), [_to] "+Q" (*(to)) \ 302 287 : [_val] "d" (val) \ 303 288 : "cc"); \ ··· 307 290 308 291 #define __put_kernel_nofault(dst, src, type, err_label) \ 309 292 do { \ 310 - u64 __x = (u64)(*((type *)(src))); \ 293 + unsigned long __x = (unsigned long)(*((type *)(src))); \ 311 294 int __pk_err; \ 312 295 \ 313 296 switch (sizeof(type)) { \ ··· 341 324 "0: " insn " %[_val],%[_from]\n" \ 342 325 "1: xr %[rc],%[rc]\n" \ 343 326 "2:\n" \ 344 - EX_TABLE_UA(0b,2b,%0) EX_TABLE_UA(1b,2b,%0) \ 345 - : [rc] "=d" (__rc), [_val] "+d" (val) \ 327 + EX_TABLE_UA_LOAD_REG(0b, 2b, %[rc], %[_val]) \ 328 + EX_TABLE_UA_LOAD_REG(1b, 2b, %[rc], %[_val]) \ 329 + : [rc] "=d" (__rc), [_val] "=d" (val) \ 346 330 : [_from] "Q" (*(from)) \ 347 331 : "cc"); \ 348 332 __rc; \ ··· 355 337 \ 356 338 switch (sizeof(type)) { \ 357 339 case 1: { \ 358 - u8 __x = 0; \ 340 + unsigned char __x; \ 359 341 \ 360 342 __gk_err = __get_kernel_asm(__x, (type *)(src), "ic"); \ 361 343 *((type *)(dst)) = (type)__x; \ 362 344 break; \ 363 345 }; \ 364 346 case 2: { \ 365 - u16 __x = 0; \ 347 + unsigned short __x; \ 366 348 \ 367 349 __gk_err = __get_kernel_asm(__x, (type *)(src), "lh"); \ 368 350 *((type *)(dst)) = (type)__x; \ 369 351 break; \ 370 352 }; \ 371 353 case 4: { \ 372 - u32 __x = 0; \ 354 + unsigned int __x; \ 373 355 \ 374 356 __gk_err = __get_kernel_asm(__x, (type *)(src), "l"); \ 375 357 *((type *)(dst)) = (type)__x; \ 376 358 break; \ 377 359 }; \ 378 360 case 8: { \ 379 - u64 __x = 0; \ 361 + unsigned long __x; \ 380 362 \ 381 363 __gk_err = __get_kernel_asm(__x, (type *)(src), "lg"); \ 382 364 *((type *)(dst)) = (type)__x; \
+35 -4
arch/s390/mm/extable.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 + #include <linux/bitfield.h> 3 4 #include <linux/extable.h> 5 + #include <linux/string.h> 4 6 #include <linux/errno.h> 5 7 #include <linux/panic.h> 6 8 #include <asm/asm-extable.h> ··· 26 24 return true; 27 25 } 28 26 29 - static bool ex_handler_uaccess(const struct exception_table_entry *ex, struct pt_regs *regs) 27 + static bool ex_handler_ua_store(const struct exception_table_entry *ex, struct pt_regs *regs) 30 28 { 31 - regs->gprs[ex->data] = -EFAULT; 29 + unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 30 + 31 + regs->gprs[reg_err] = -EFAULT; 32 + regs->psw.addr = extable_fixup(ex); 33 + return true; 34 + } 35 + 36 + static bool ex_handler_ua_load_mem(const struct exception_table_entry *ex, struct pt_regs *regs) 37 + { 38 + unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); 39 + unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 40 + size_t len = FIELD_GET(EX_DATA_LEN, ex->data); 41 + 42 + regs->gprs[reg_err] = -EFAULT; 43 + memset((void *)regs->gprs[reg_addr], 0, len); 44 + regs->psw.addr = extable_fixup(ex); 45 + return true; 46 + } 47 + 48 + static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, struct pt_regs *regs) 49 + { 50 + unsigned int reg_zero = FIELD_GET(EX_DATA_REG_ADDR, ex->data); 51 + unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 52 + 53 + regs->gprs[reg_err] = -EFAULT; 54 + regs->gprs[reg_zero] = 0; 32 55 regs->psw.addr = extable_fixup(ex); 33 56 return true; 34 57 } ··· 70 43 return ex_handler_fixup(ex, regs); 71 44 case EX_TYPE_BPF: 72 45 return ex_handler_bpf(ex, regs); 73 - case EX_TYPE_UACCESS: 74 - return ex_handler_uaccess(ex, regs); 46 + case EX_TYPE_UA_STORE: 47 + return ex_handler_ua_store(ex, regs); 48 + case EX_TYPE_UA_LOAD_MEM: 49 + return ex_handler_ua_load_mem(ex, regs); 50 + case EX_TYPE_UA_LOAD_REG: 51 + return ex_handler_ua_load_reg(ex, regs); 75 52 } 76 53 panic("invalid exception table entry"); 77 54 }