encoding.h 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. #ifndef _KERNEL_KCSAN_ENCODING_H
  3. #define _KERNEL_KCSAN_ENCODING_H
  4. #include <linux/bits.h>
  5. #include <linux/log2.h>
  6. #include <linux/mm.h>
  7. #include "kcsan.h"
  8. #define SLOT_RANGE PAGE_SIZE
  9. #define INVALID_WATCHPOINT 0
  10. #define CONSUMED_WATCHPOINT 1
  11. /*
  12. * The maximum useful size of accesses for which we set up watchpoints is the
  13. * max range of slots we check on an access.
  14. */
  15. #define MAX_ENCODABLE_SIZE (SLOT_RANGE * (1 + KCSAN_CHECK_ADJACENT))
  16. /*
  17. * Number of bits we use to store size info.
  18. */
  19. #define WATCHPOINT_SIZE_BITS bits_per(MAX_ENCODABLE_SIZE)
  20. /*
  21. * This encoding for addresses discards the upper (1 for is-write + SIZE_BITS);
  22. * however, most 64-bit architectures do not use the full 64-bit address space.
  23. * Also, in order for a false positive to be observable 2 things need to happen:
  24. *
  25. * 1. different addresses but with the same encoded address race;
  26. * 2. and both map onto the same watchpoint slots;
  27. *
  28. * Both these are assumed to be very unlikely. However, in case it still
  29. * happens, the report logic will filter out the false positive (see report.c).
  30. */
  31. #define WATCHPOINT_ADDR_BITS (BITS_PER_LONG-1 - WATCHPOINT_SIZE_BITS)
  32. /*
  33. * Masks to set/retrieve the encoded data.
  34. */
  35. #define WATCHPOINT_WRITE_MASK BIT(BITS_PER_LONG-1)
  36. #define WATCHPOINT_SIZE_MASK \
  37. GENMASK(BITS_PER_LONG-2, BITS_PER_LONG-2 - WATCHPOINT_SIZE_BITS)
  38. #define WATCHPOINT_ADDR_MASK \
  39. GENMASK(BITS_PER_LONG-3 - WATCHPOINT_SIZE_BITS, 0)
  40. static inline bool check_encodable(unsigned long addr, size_t size)
  41. {
  42. return size <= MAX_ENCODABLE_SIZE;
  43. }
  44. static inline long
  45. encode_watchpoint(unsigned long addr, size_t size, bool is_write)
  46. {
  47. return (long)((is_write ? WATCHPOINT_WRITE_MASK : 0) |
  48. (size << WATCHPOINT_ADDR_BITS) |
  49. (addr & WATCHPOINT_ADDR_MASK));
  50. }
  51. static __always_inline bool decode_watchpoint(long watchpoint,
  52. unsigned long *addr_masked,
  53. size_t *size,
  54. bool *is_write)
  55. {
  56. if (watchpoint == INVALID_WATCHPOINT ||
  57. watchpoint == CONSUMED_WATCHPOINT)
  58. return false;
  59. *addr_masked = (unsigned long)watchpoint & WATCHPOINT_ADDR_MASK;
  60. *size = ((unsigned long)watchpoint & WATCHPOINT_SIZE_MASK) >> WATCHPOINT_ADDR_BITS;
  61. *is_write = !!((unsigned long)watchpoint & WATCHPOINT_WRITE_MASK);
  62. return true;
  63. }
  64. /*
  65. * Return watchpoint slot for an address.
  66. */
  67. static __always_inline int watchpoint_slot(unsigned long addr)
  68. {
  69. return (addr / PAGE_SIZE) % CONFIG_KCSAN_NUM_WATCHPOINTS;
  70. }
  71. static __always_inline bool matching_access(unsigned long addr1, size_t size1,
  72. unsigned long addr2, size_t size2)
  73. {
  74. unsigned long end_range1 = addr1 + size1 - 1;
  75. unsigned long end_range2 = addr2 + size2 - 1;
  76. return addr1 <= end_range2 && addr2 <= end_range1;
  77. }
  78. #endif /* _KERNEL_KCSAN_ENCODING_H */