cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

strnlen_user.c (3436B)


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