/** * strnlen_user: - Get the size of a string in user space. * @s: The string to measure. * @n: The maximum valid length * * Get the size of a NUL-terminated string in user space. * * Returns the size of the string INCLUDING the terminating NUL. * On exception, returns 0. * If the string is too long, returns a value greater than @n. */ long strnlen_user(const char __user *s, long n) { unsigned long mask = -__addr_ok(s); unsigned long res, tmp; might_fault(); __asm__ __volatile__( " testl %0, %0\n" " jz 3f\n" " andl %0,%%ecx\n" "0: repne; scasb\n" " setne %%al\n" " subl %%ecx,%0\n" " addl %0,%%eax\n" "1:\n" ".section .fixup,\"ax\"\n" "2: xorl %%eax,%%eax\n" " jmp 1b\n" "3: movb $1,%%al\n" " jmp 1b\n" ".previous\n" ".section __ex_table,\"a\"\n" " .align 4\n" " .long 0b,2b\n" ".previous" :"=&r" (n), "=&D" (s), "=&a" (res), "=&c" (tmp) :"0" (n), "1" (s), "2" (0), "3" (mask) :"cc"); return res & mask; }
long strnlen_user(const char __user *s, long n) { unsigned long mask = -__addr_ok(s); unsigned long res; __asm__ __volatile__( " and %0, %5\n" " mv r1, %1\n" " beqz %0, strnlen_exit\n" " and3 r0, %1, #3\n" " bnez r0, strnlen_byte_loop\n" " cmpui %0, #4\n" " bc strnlen_byte_loop\n" " sll3 r3, %6, #7\n" "strnlen_word_loop:\n" "0: ld r0, @%1+\n" " not r2, r0\n" " sub r0, %6\n" " and r2, r3\n" " and r2, r0\n" " bnez r2, strnlen_last_bytes_fixup\n" " addi %0, #-4\n" " beqz %0, strnlen_exit\n" " bgtz %0, strnlen_word_loop\n" "strnlen_last_bytes:\n" " mv %0, %4\n" "strnlen_last_bytes_fixup:\n" " addi %1, #-4\n" "strnlen_byte_loop:\n" "1: ldb r0, @%1\n" " addi %0, #-1\n" " beqz r0, strnlen_exit\n"
long strnlen_user(const char __user *s, long n) { unsigned long mask = -__addr_ok(s); unsigned long res; __asm__ __volatile__( " and %0, %5\n" " mv r1, %1\n" " beqz %0, strnlen_exit\n" " and3 r0, %1, #3\n" " bnez r0, strnlen_byte_loop\n" " cmpui %0, #4\n" " bc strnlen_byte_loop\n" " sll3 r3, %6, #7\n" "strnlen_word_loop:\n" "0: ld r0, @%1+\n" " not r2, r0\n" " sub r0, %6\n" " and r2, r3\n" " and r2, r0\n" " bnez r2, strnlen_last_bytes_fixup\n" " addi %0, #-4\n" " beqz %0, strnlen_exit\n" " bgtz %0, strnlen_word_loop\n" "strnlen_last_bytes:\n" " mv %0, %4\n" "strnlen_last_bytes_fixup:\n" " addi %1, #-4\n" "strnlen_byte_loop:\n" "1: ldb r0, @%1\n" " addi %0, #-1\n" " beqz r0, strnlen_exit\n" " addi %1, #1\n" " bnez %0, strnlen_byte_loop\n" "strnlen_exit:\n" " sub %1, r1\n" " add3 %0, %1, #1\n" " .fillinsn\n" "9:\n" ".section .fixup,\"ax\"\n" " .balign 4\n" "4: addi %1, #-4\n" " .fillinsn\n" "5: ldi %0, #0\n" " seth r1, #high(9b)\n" " or3 r1, r1, #low(9b)\n" " jmp r1\n" ".previous\n" ".section __ex_table,\"a\"\n" " .balign 4\n" " .long 0b,4b\n" " .long 1b,5b\n" ".previous" : "=&r" (res), "=r" (s) : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) : "r0", "r1", "r2", "r3", "cbit"); /* NOTE: strnlen_user() algorithm: * { * char *p; * for (p = s; n-- && *p != '\0'; ++p) * ; * return p - s + 1; * } */ /* NOTE: If a null char. exists, return 0. * if ((x - 0x01010101) & ~x & 0x80808080)\n" * return 0;\n" */ return res & mask; }