sockptr.h 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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. #include <linux/slab.h>
  11. #include <linux/uaccess.h>
  12. typedef struct {
  13. union {
  14. void *kernel;
  15. void __user *user;
  16. };
  17. bool is_kernel : 1;
  18. } sockptr_t;
  19. static inline bool sockptr_is_kernel(sockptr_t sockptr)
  20. {
  21. return sockptr.is_kernel;
  22. }
  23. static inline sockptr_t KERNEL_SOCKPTR(void *p)
  24. {
  25. return (sockptr_t) { .kernel = p, .is_kernel = true };
  26. }
  27. static inline sockptr_t USER_SOCKPTR(void __user *p)
  28. {
  29. return (sockptr_t) { .user = p };
  30. }
  31. static inline bool sockptr_is_null(sockptr_t sockptr)
  32. {
  33. if (sockptr_is_kernel(sockptr))
  34. return !sockptr.kernel;
  35. return !sockptr.user;
  36. }
  37. static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
  38. size_t offset, size_t size)
  39. {
  40. if (!sockptr_is_kernel(src))
  41. return copy_from_user(dst, src.user + offset, size);
  42. memcpy(dst, src.kernel + offset, size);
  43. return 0;
  44. }
  45. static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
  46. {
  47. return copy_from_sockptr_offset(dst, src, 0, size);
  48. }
  49. static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
  50. const void *src, size_t size)
  51. {
  52. if (!sockptr_is_kernel(dst))
  53. return copy_to_user(dst.user + offset, src, size);
  54. memcpy(dst.kernel + offset, src, size);
  55. return 0;
  56. }
  57. static inline void *memdup_sockptr(sockptr_t src, size_t len)
  58. {
  59. void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
  60. if (!p)
  61. return ERR_PTR(-ENOMEM);
  62. if (copy_from_sockptr(p, src, len)) {
  63. kfree(p);
  64. return ERR_PTR(-EFAULT);
  65. }
  66. return p;
  67. }
  68. static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
  69. {
  70. char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
  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. p[len] = '\0';
  78. return p;
  79. }
  80. static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
  81. {
  82. if (sockptr_is_kernel(src)) {
  83. size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
  84. memcpy(dst, src.kernel, len);
  85. return len;
  86. }
  87. return strncpy_from_user(dst, src.user, count);
  88. }
  89. #endif /* _LINUX_SOCKPTR_H */