linux/include/linux/sockptr.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-only */
   2/*
   3 * Copyright (c) 2020 Christoph Hellwig.
   4 *
   5 * Support for "universal" pointers that can point to either kernel or userspace
   6 * memory.
   7 */
   8#ifndef _LINUX_SOCKPTR_H
   9#define _LINUX_SOCKPTR_H
  10
  11#include <linux/slab.h>
  12#include <linux/uaccess.h>
  13
  14typedef struct {
  15        union {
  16                void            *kernel;
  17                void __user     *user;
  18        };
  19        bool            is_kernel : 1;
  20} sockptr_t;
  21
  22static inline bool sockptr_is_kernel(sockptr_t sockptr)
  23{
  24        return sockptr.is_kernel;
  25}
  26
  27static inline sockptr_t KERNEL_SOCKPTR(void *p)
  28{
  29        return (sockptr_t) { .kernel = p, .is_kernel = true };
  30}
  31
  32static inline sockptr_t USER_SOCKPTR(void __user *p)
  33{
  34        return (sockptr_t) { .user = p };
  35}
  36
  37static inline bool sockptr_is_null(sockptr_t sockptr)
  38{
  39        if (sockptr_is_kernel(sockptr))
  40                return !sockptr.kernel;
  41        return !sockptr.user;
  42}
  43
  44static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
  45                size_t offset, size_t size)
  46{
  47        if (!sockptr_is_kernel(src))
  48                return copy_from_user(dst, src.user + offset, size);
  49        memcpy(dst, src.kernel + offset, size);
  50        return 0;
  51}
  52
  53static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
  54{
  55        return copy_from_sockptr_offset(dst, src, 0, size);
  56}
  57
  58static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
  59                const void *src, size_t size)
  60{
  61        if (!sockptr_is_kernel(dst))
  62                return copy_to_user(dst.user + offset, src, size);
  63        memcpy(dst.kernel + offset, src, size);
  64        return 0;
  65}
  66
  67static inline void *memdup_sockptr(sockptr_t src, size_t len)
  68{
  69        void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
  70
  71        if (!p)
  72                return ERR_PTR(-ENOMEM);
  73        if (copy_from_sockptr(p, src, len)) {
  74                kfree(p);
  75                return ERR_PTR(-EFAULT);
  76        }
  77        return p;
  78}
  79
  80static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
  81{
  82        char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
  83
  84        if (!p)
  85                return ERR_PTR(-ENOMEM);
  86        if (copy_from_sockptr(p, src, len)) {
  87                kfree(p);
  88                return ERR_PTR(-EFAULT);
  89        }
  90        p[len] = '\0';
  91        return p;
  92}
  93
  94static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
  95{
  96        if (sockptr_is_kernel(src)) {
  97                size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
  98
  99                memcpy(dst, src.kernel, len);
 100                return len;
 101        }
 102        return strncpy_from_user(dst, src.user, count);
 103}
 104
 105#endif /* _LINUX_SOCKPTR_H */
 106