Blame view

lib/strnlen_user.c 3.45 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
a08c5356a   Linus Torvalds   lib: add generic ...
2
3
4
5
6
7
8
9
10
11
  #include <linux/kernel.h>
  #include <linux/export.h>
  #include <linux/uaccess.h>
  
  #include <asm/word-at-a-time.h>
  
  /* Set bits in the first 'n' bytes when loaded from memory */
  #ifdef __LITTLE_ENDIAN
  #  define aligned_byte_mask(n) ((1ul << 8*(n))-1)
  #else
69ea64059   Paul Mackerras   lib: Fix generic ...
12
  #  define aligned_byte_mask(n) (~0xfful << (BITS_PER_LONG - 8 - 8*(n)))
a08c5356a   Linus Torvalds   lib: add generic ...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  #endif
  
  /*
   * Do a strnlen, return length of string *with* final '\0'.
   * 'count' is the user-supplied count, while 'max' is the
   * address space maximum.
   *
   * Return 0 for exceptions (which includes hitting the address
   * space maximum), or 'count+1' if hitting the user-supplied
   * maximum count.
   *
   * NOTE! We can sometimes overshoot the user-supplied maximum
   * if it fits in a aligned 'long'. The caller needs to check
   * the return value against "> max".
   */
  static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max)
  {
  	const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
  	long align, res = 0;
  	unsigned long c;
  
  	/*
  	 * Truncate 'max' to the user-specified limit, so that
  	 * we only have one limit we need to check in the loop
  	 */
  	if (max > count)
  		max = count;
  
  	/*
  	 * Do everything aligned. But that means that we
  	 * need to also expand the maximum..
  	 */
  	align = (sizeof(long) - 1) & (unsigned long)src;
  	src -= align;
  	max += align;
1bd4403d8   Linus Torvalds   unsafe_[get|put]_...
48
  	unsafe_get_user(c, (unsigned long __user *)src, efault);
a08c5356a   Linus Torvalds   lib: add generic ...
49
50
51
52
53
54
55
56
57
58
  	c |= aligned_byte_mask(align);
  
  	for (;;) {
  		unsigned long data;
  		if (has_zero(c, &data, &constants)) {
  			data = prep_zero_mask(c, data, &constants);
  			data = create_zero_mask(data);
  			return res + find_zero(data) + 1 - align;
  		}
  		res += sizeof(unsigned long);
f18c34e48   Jan Kara   lib: Fix strnlen_...
59
60
  		/* We already handled 'unsigned long' bytes. Did we do it all ? */
  		if (unlikely(max <= sizeof(unsigned long)))
a08c5356a   Linus Torvalds   lib: add generic ...
61
62
  			break;
  		max -= sizeof(unsigned long);
1bd4403d8   Linus Torvalds   unsafe_[get|put]_...
63
  		unsafe_get_user(c, (unsigned long __user *)(src+res), efault);
a08c5356a   Linus Torvalds   lib: add generic ...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  	}
  	res -= align;
  
  	/*
  	 * Uhhuh. We hit 'max'. But was that the user-specified maximum
  	 * too? If so, return the marker for "too long".
  	 */
  	if (res >= count)
  		return count+1;
  
  	/*
  	 * Nope: we hit the address space limit, and we still had more
  	 * characters the caller would have wanted. That's 0.
  	 */
1bd4403d8   Linus Torvalds   unsafe_[get|put]_...
78
  efault:
a08c5356a   Linus Torvalds   lib: add generic ...
79
80
81
82
83
84
85
86
  	return 0;
  }
  
  /**
   * strnlen_user: - Get the size of a user string INCLUDING final NUL.
   * @str: The string to measure.
   * @count: Maximum count (including NUL character)
   *
b3c395ef5   David Hildenbrand   mm/uaccess, mm/fa...
87
88
   * Context: User context only. This function may sleep if pagefaults are
   *          enabled.
a08c5356a   Linus Torvalds   lib: add generic ...
89
90
91
92
   *
   * Get the size of a NUL-terminated string in user space.
   *
   * Returns the size of the string INCLUDING the terminating NUL.
226a07ef0   Jan Kara   lib: Clarify the ...
93
94
   * If the string is too long, returns a number larger than @count. User
   * has to check the return value against "> count".
a08c5356a   Linus Torvalds   lib: add generic ...
95
   * On exception (or invalid count), returns 0.
226a07ef0   Jan Kara   lib: Clarify the ...
96
97
98
99
100
101
   *
   * NOTE! You should basically never use this function. There is
   * almost never any valid case for using the length of a user space
   * string, since the string can be changed at any time by other
   * threads. Use "strncpy_from_user()" instead to get a stable copy
   * of the string.
a08c5356a   Linus Torvalds   lib: add generic ...
102
103
104
105
106
107
108
109
110
111
112
113
   */
  long strnlen_user(const char __user *str, long count)
  {
  	unsigned long max_addr, src_addr;
  
  	if (unlikely(count <= 0))
  		return 0;
  
  	max_addr = user_addr_max();
  	src_addr = (unsigned long)str;
  	if (likely(src_addr < max_addr)) {
  		unsigned long max = max_addr - src_addr;
9fd4470ff   Linus Torvalds   Use the new batch...
114
115
116
117
118
119
  		long retval;
  
  		user_access_begin();
  		retval = do_strnlen_user(str, count, max);
  		user_access_end();
  		return retval;
a08c5356a   Linus Torvalds   lib: add generic ...
120
121
122
123
  	}
  	return 0;
  }
  EXPORT_SYMBOL(strnlen_user);