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