LCOV - code coverage report
Current view: top level - arch/um/kernel/skas - uaccess.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 134 0.0 %
Date: 2023-03-27 20:00:47 Functions: 0 16 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
       4             :  */
       5             : 
       6             : #include <linux/err.h>
       7             : #include <linux/highmem.h>
       8             : #include <linux/mm.h>
       9             : #include <linux/module.h>
      10             : #include <linux/sched.h>
      11             : #include <asm/current.h>
      12             : #include <asm/page.h>
      13             : #include <kern_util.h>
      14             : #include <asm/futex.h>
      15             : #include <os.h>
      16             : 
      17           0 : pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
      18             : {
      19             :         pgd_t *pgd;
      20             :         p4d_t *p4d;
      21             :         pud_t *pud;
      22             :         pmd_t *pmd;
      23             : 
      24           0 :         if (mm == NULL)
      25             :                 return NULL;
      26             : 
      27           0 :         pgd = pgd_offset(mm, addr);
      28             :         if (!pgd_present(*pgd))
      29             :                 return NULL;
      30             : 
      31           0 :         p4d = p4d_offset(pgd, addr);
      32             :         if (!p4d_present(*p4d))
      33             :                 return NULL;
      34             : 
      35           0 :         pud = pud_offset(p4d, addr);
      36           0 :         if (!pud_present(*pud))
      37             :                 return NULL;
      38             : 
      39           0 :         pmd = pmd_offset(pud, addr);
      40           0 :         if (!pmd_present(*pmd))
      41             :                 return NULL;
      42             : 
      43           0 :         return pte_offset_kernel(pmd, addr);
      44             : }
      45             : 
      46           0 : static pte_t *maybe_map(unsigned long virt, int is_write)
      47             : {
      48           0 :         pte_t *pte = virt_to_pte(current->mm, virt);
      49             :         int err, dummy_code;
      50             : 
      51           0 :         if ((pte == NULL) || !pte_present(*pte) ||
      52           0 :             (is_write && !pte_write(*pte))) {
      53           0 :                 err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
      54           0 :                 if (err)
      55             :                         return NULL;
      56           0 :                 pte = virt_to_pte(current->mm, virt);
      57             :         }
      58           0 :         if (!pte_present(*pte))
      59           0 :                 pte = NULL;
      60             : 
      61             :         return pte;
      62             : }
      63             : 
      64           0 : static int do_op_one_page(unsigned long addr, int len, int is_write,
      65             :                  int (*op)(unsigned long addr, int len, void *arg), void *arg)
      66             : {
      67             :         struct page *page;
      68             :         pte_t *pte;
      69             :         int n;
      70             : 
      71           0 :         pte = maybe_map(addr, is_write);
      72           0 :         if (pte == NULL)
      73             :                 return -1;
      74             : 
      75           0 :         page = pte_page(*pte);
      76             : #ifdef CONFIG_64BIT
      77           0 :         pagefault_disable();
      78           0 :         addr = (unsigned long) page_address(page) +
      79           0 :                 (addr & ~PAGE_MASK);
      80             : #else
      81             :         addr = (unsigned long) kmap_atomic(page) +
      82             :                 (addr & ~PAGE_MASK);
      83             : #endif
      84           0 :         n = (*op)(addr, len, arg);
      85             : 
      86             : #ifdef CONFIG_64BIT
      87           0 :         pagefault_enable();
      88             : #else
      89             :         kunmap_atomic((void *)addr);
      90             : #endif
      91             : 
      92           0 :         return n;
      93             : }
      94             : 
      95           0 : static long buffer_op(unsigned long addr, int len, int is_write,
      96             :                       int (*op)(unsigned long, int, void *), void *arg)
      97             : {
      98             :         long size, remain, n;
      99             : 
     100           0 :         size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
     101           0 :         remain = len;
     102             : 
     103           0 :         n = do_op_one_page(addr, size, is_write, op, arg);
     104           0 :         if (n != 0) {
     105           0 :                 remain = (n < 0 ? remain : 0);
     106             :                 goto out;
     107             :         }
     108             : 
     109           0 :         addr += size;
     110           0 :         remain -= size;
     111           0 :         if (remain == 0)
     112             :                 goto out;
     113             : 
     114           0 :         while (addr < ((addr + remain) & PAGE_MASK)) {
     115           0 :                 n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
     116           0 :                 if (n != 0) {
     117           0 :                         remain = (n < 0 ? remain : 0);
     118             :                         goto out;
     119             :                 }
     120             : 
     121           0 :                 addr += PAGE_SIZE;
     122           0 :                 remain -= PAGE_SIZE;
     123             :         }
     124           0 :         if (remain == 0)
     125             :                 goto out;
     126             : 
     127           0 :         n = do_op_one_page(addr, remain, is_write, op, arg);
     128           0 :         if (n != 0) {
     129           0 :                 remain = (n < 0 ? remain : 0);
     130             :                 goto out;
     131             :         }
     132             : 
     133             :         return 0;
     134             :  out:
     135             :         return remain;
     136             : }
     137             : 
     138           0 : static int copy_chunk_from_user(unsigned long from, int len, void *arg)
     139             : {
     140           0 :         unsigned long *to_ptr = arg, to = *to_ptr;
     141             : 
     142           0 :         memcpy((void *) to, (void *) from, len);
     143           0 :         *to_ptr += len;
     144           0 :         return 0;
     145             : }
     146             : 
     147           0 : unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
     148             : {
     149           0 :         return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
     150             : }
     151             : EXPORT_SYMBOL(raw_copy_from_user);
     152             : 
     153           0 : static int copy_chunk_to_user(unsigned long to, int len, void *arg)
     154             : {
     155           0 :         unsigned long *from_ptr = arg, from = *from_ptr;
     156             : 
     157           0 :         memcpy((void *) to, (void *) from, len);
     158           0 :         *from_ptr += len;
     159           0 :         return 0;
     160             : }
     161             : 
     162           0 : unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
     163             : {
     164           0 :         return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
     165             : }
     166             : EXPORT_SYMBOL(raw_copy_to_user);
     167             : 
     168           0 : static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
     169             : {
     170           0 :         char **to_ptr = arg, *to = *to_ptr;
     171             :         int n;
     172             : 
     173           0 :         strncpy(to, (void *) from, len);
     174           0 :         n = strnlen(to, len);
     175           0 :         *to_ptr += n;
     176             : 
     177           0 :         if (n < len)
     178             :                 return 1;
     179           0 :         return 0;
     180             : }
     181             : 
     182           0 : long strncpy_from_user(char *dst, const char __user *src, long count)
     183             : {
     184             :         long n;
     185           0 :         char *ptr = dst;
     186             : 
     187           0 :         if (!access_ok(src, 1))
     188             :                 return -EFAULT;
     189           0 :         n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
     190             :                       &ptr);
     191           0 :         if (n != 0)
     192             :                 return -EFAULT;
     193           0 :         return strnlen(dst, count);
     194             : }
     195             : EXPORT_SYMBOL(strncpy_from_user);
     196             : 
     197           0 : static int clear_chunk(unsigned long addr, int len, void *unused)
     198             : {
     199           0 :         memset((void *) addr, 0, len);
     200           0 :         return 0;
     201             : }
     202             : 
     203           0 : unsigned long __clear_user(void __user *mem, unsigned long len)
     204             : {
     205           0 :         return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
     206             : }
     207             : EXPORT_SYMBOL(__clear_user);
     208             : 
     209           0 : static int strnlen_chunk(unsigned long str, int len, void *arg)
     210             : {
     211           0 :         int *len_ptr = arg, n;
     212             : 
     213           0 :         n = strnlen((void *) str, len);
     214           0 :         *len_ptr += n;
     215             : 
     216           0 :         if (n < len)
     217             :                 return 1;
     218           0 :         return 0;
     219             : }
     220             : 
     221           0 : long strnlen_user(const char __user *str, long len)
     222             : {
     223           0 :         int count = 0, n;
     224             : 
     225           0 :         if (!access_ok(str, 1))
     226             :                 return -EFAULT;
     227           0 :         n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
     228           0 :         if (n == 0)
     229           0 :                 return count + 1;
     230             :         return 0;
     231             : }
     232             : EXPORT_SYMBOL(strnlen_user);
     233             : 
     234             : /**
     235             :  * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
     236             :  *                        argument and comparison of the previous
     237             :  *                        futex value with another constant.
     238             :  *
     239             :  * @encoded_op: encoded operation to execute
     240             :  * @uaddr:      pointer to user space address
     241             :  *
     242             :  * Return:
     243             :  * 0 - On success
     244             :  * -EFAULT - User access resulted in a page fault
     245             :  * -EAGAIN - Atomic operation was unable to complete due to contention
     246             :  * -ENOSYS - Operation not supported
     247             :  */
     248             : 
     249           0 : int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
     250             : {
     251             :         int oldval, ret;
     252             :         struct page *page;
     253           0 :         unsigned long addr = (unsigned long) uaddr;
     254             :         pte_t *pte;
     255             : 
     256           0 :         ret = -EFAULT;
     257           0 :         if (!access_ok(uaddr, sizeof(*uaddr)))
     258             :                 return -EFAULT;
     259           0 :         preempt_disable();
     260           0 :         pte = maybe_map(addr, 1);
     261           0 :         if (pte == NULL)
     262             :                 goto out_inuser;
     263             : 
     264           0 :         page = pte_page(*pte);
     265             : #ifdef CONFIG_64BIT
     266           0 :         pagefault_disable();
     267           0 :         addr = (unsigned long) page_address(page) +
     268           0 :                         (((unsigned long) addr) & ~PAGE_MASK);
     269             : #else
     270             :         addr = (unsigned long) kmap_atomic(page) +
     271             :                 ((unsigned long) addr & ~PAGE_MASK);
     272             : #endif
     273           0 :         uaddr = (u32 *) addr;
     274           0 :         oldval = *uaddr;
     275             : 
     276           0 :         ret = 0;
     277             : 
     278           0 :         switch (op) {
     279             :         case FUTEX_OP_SET:
     280           0 :                 *uaddr = oparg;
     281           0 :                 break;
     282             :         case FUTEX_OP_ADD:
     283           0 :                 *uaddr += oparg;
     284           0 :                 break;
     285             :         case FUTEX_OP_OR:
     286           0 :                 *uaddr |= oparg;
     287           0 :                 break;
     288             :         case FUTEX_OP_ANDN:
     289           0 :                 *uaddr &= ~oparg;
     290           0 :                 break;
     291             :         case FUTEX_OP_XOR:
     292           0 :                 *uaddr ^= oparg;
     293           0 :                 break;
     294             :         default:
     295             :                 ret = -ENOSYS;
     296             :         }
     297             : #ifdef CONFIG_64BIT
     298             :         pagefault_enable();
     299             : #else
     300             :         kunmap_atomic((void *)addr);
     301             : #endif
     302             : 
     303             : out_inuser:
     304           0 :         preempt_enable();
     305             : 
     306           0 :         if (ret == 0)
     307           0 :                 *oval = oldval;
     308             : 
     309             :         return ret;
     310             : }
     311             : EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
     312             : 
     313             : /**
     314             :  * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
     315             :  *                              uaddr with newval if the current value is
     316             :  *                              oldval.
     317             :  * @uval:       pointer to store content of @uaddr
     318             :  * @uaddr:      pointer to user space address
     319             :  * @oldval:     old value
     320             :  * @newval:     new value to store to @uaddr
     321             :  *
     322             :  * Return:
     323             :  * 0 - On success
     324             :  * -EFAULT - User access resulted in a page fault
     325             :  * -EAGAIN - Atomic operation was unable to complete due to contention
     326             :  */
     327             : 
     328           0 : int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
     329             :                               u32 oldval, u32 newval)
     330             : {
     331             :         struct page *page;
     332             :         pte_t *pte;
     333           0 :         int ret = -EFAULT;
     334             : 
     335           0 :         if (!access_ok(uaddr, sizeof(*uaddr)))
     336             :                 return -EFAULT;
     337             : 
     338           0 :         preempt_disable();
     339           0 :         pte = maybe_map((unsigned long) uaddr, 1);
     340           0 :         if (pte == NULL)
     341             :                 goto out_inatomic;
     342             : 
     343           0 :         page = pte_page(*pte);
     344             : #ifdef CONFIG_64BIT
     345           0 :         pagefault_disable();
     346           0 :         uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK);
     347             : #else
     348             :         uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
     349             : #endif
     350             : 
     351           0 :         *uval = *uaddr;
     352             : 
     353           0 :         ret = cmpxchg(uaddr, oldval, newval);
     354             : 
     355             : #ifdef CONFIG_64BIT
     356           0 :         pagefault_enable();
     357             : #else
     358             :         kunmap_atomic(uaddr);
     359             : #endif
     360           0 :         ret = 0;
     361             : 
     362             : out_inatomic:
     363           0 :         preempt_enable();
     364           0 :         return ret;
     365             : }
     366             : EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);

Generated by: LCOV version 1.14