Blame view

lib/strnlen_user.c 4.26 KB
a08c5356a   Linus Torvalds   lib: add generic ...
1
2
3
4
5
6
7
8
9
10
  #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 ...
11
  #  define aligned_byte_mask(n) (~0xfful << (BITS_PER_LONG - 8 - 8*(n)))
a08c5356a   Linus Torvalds   lib: add generic ...
12
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
  #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]_...
47
  	unsafe_get_user(c, (unsigned long __user *)src, efault);
a08c5356a   Linus Torvalds   lib: add generic ...
48
49
50
51
52
53
54
55
56
57
  	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_...
58
59
  		/* We already handled 'unsigned long' bytes. Did we do it all ? */
  		if (unlikely(max <= sizeof(unsigned long)))
a08c5356a   Linus Torvalds   lib: add generic ...
60
61
  			break;
  		max -= sizeof(unsigned long);
1bd4403d8   Linus Torvalds   unsafe_[get|put]_...
62
  		unsafe_get_user(c, (unsigned long __user *)(src+res), efault);
a08c5356a   Linus Torvalds   lib: add generic ...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  	}
  	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]_...
77
  efault:
a08c5356a   Linus Torvalds   lib: add generic ...
78
79
80
81
82
83
84
85
  	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...
86
87
   * Context: User context only. This function may sleep if pagefaults are
   *          enabled.
a08c5356a   Linus Torvalds   lib: add generic ...
88
89
90
91
   *
   * 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 ...
92
93
   * 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 ...
94
   * On exception (or invalid count), returns 0.
226a07ef0   Jan Kara   lib: Clarify the ...
95
96
97
98
99
100
   *
   * 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 ...
101
102
103
104
105
106
107
108
109
110
111
112
   */
  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...
113
114
115
116
117
118
  		long retval;
  
  		user_access_begin();
  		retval = do_strnlen_user(str, count, max);
  		user_access_end();
  		return retval;
a08c5356a   Linus Torvalds   lib: add generic ...
119
120
121
122
123
124
125
126
127
  	}
  	return 0;
  }
  EXPORT_SYMBOL(strnlen_user);
  
  /**
   * strlen_user: - Get the size of a user string INCLUDING final NUL.
   * @str: The string to measure.
   *
b3c395ef5   David Hildenbrand   mm/uaccess, mm/fa...
128
129
   * Context: User context only. This function may sleep if pagefaults are
   *          enabled.
a08c5356a   Linus Torvalds   lib: add generic ...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
   *
   * 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 there is a limit on the length of a valid string, you may wish to
   * consider using strnlen_user() instead.
   */
  long strlen_user(const char __user *str)
  {
  	unsigned long max_addr, src_addr;
  
  	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...
147
148
149
150
151
152
  		long retval;
  
  		user_access_begin();
  		retval = do_strnlen_user(str, ~0ul, max);
  		user_access_end();
  		return retval;
a08c5356a   Linus Torvalds   lib: add generic ...
153
154
155
156
  	}
  	return 0;
  }
  EXPORT_SYMBOL(strlen_user);