XenevaOS
Loading...
Searching...
No Matches
nospec.h
Go to the documentation of this file.
1#ifndef _LINUX_NOSPEC_H
2#define _LINUX_NOSPEC_H
3
4/*
5 * DCL <linux/nospec.h> -- array_index_nospec(), the bounds-check clamp.
6 *
7 * Only the part a ported driver actually calls is here. mainline's header
8 * also carries barrier_nospec() and the arch_prctl / arch_seccomp speculation
9 * prctls; nothing in DCL uses those, and an uncompiled definition is worse
10 * than an absent one (it reads as supported).
11 *
12 * ### The one real difference from mainline: the second BUILD_BUG_ON is gone
13 *
14 * mainline's macro ends with:
15 *
16 * BUILD_BUG_ON(sizeof(_i) > sizeof(long));
17 * BUILD_BUG_ON(sizeof(_s) > sizeof(long));
18 *
19 * The first guard is kept. The second cannot survive on this target:
20 * aarch64-unknown-windows is LLP64, so sizeof(long) is 4 while sizeof(size_t)
21 * is 8 -- and `_s` is routinely a size_t, `sizeof(vi->data)` in virtio-rng.c
22 * being the current instance. mainline can only afford that check because on
23 * LP64 long and size_t are the same width; here it would make every use of
24 * the macro a compile error rather than catch anything.
25 *
26 * The truncation it guards against is real but harmless for a length: the
27 * value is a struct member's size, always far below 4 GB, and it is only ever
28 * compared against an index of the same object. So `_s` is cast to
29 * unsigned long explicitly rather than silently converted -- the narrowing is
30 * written down at the call site of the mask function, where a future reader
31 * will see it.
32 *
33 * If a _s value could ever exceed 4 GB, the right fix is to make the whole
34 * clamp size_t-wide and drop array_index_mask_nospec's unsigned long
35 * parameters, not to pretend the guard is still there.
36 *
37 * ### BITS_PER_LONG is 32 here, and that is load-bearing
38 *
39 * the mask is a sign-bit trick: it must shift the sign bit of the OR'ed
40 * value across the word, so that value is cast to signed `long` -- only then
41 * is the shift arithmetic and extends the sign -- and the shift count has to
42 * be sizeof(long)*8 - 1, i.e. BITS_PER_LONG - 1. kernel.h defines
43 * BITS_PER_LONG exactly that way (it was a hardcoded 64 until bringing this
44 * file in, which would have shifted a 32-bit value by 63 and produced mask
45 * 0 -- silently reading element 0 of every array, forever).
46 *
47 * The cast was `unsigned long` for a while, and a logical shift extends
48 * nothing: in bounds it left mask 1, so `index & mask` clamped every
49 * in-bounds index to its low bit. drm_ioctl()'s core dispatch read nr=0x0C
50 * (GET_CAP), masked it to 0 and called the VERSION handler. The signed cast
51 * is the whole mechanism, not a detail.
52 */
53
54#include <linux/kernel.h> /* size_t, BUILD_BUG_ON, typeof, OPTIMIZER_HIDE_VAR */
55
68static inline unsigned long array_index_mask_nospec(unsigned long index,
69 unsigned long size)
70{
71 /*
72 * Always compute the mask even if the compiler can prove the index is
73 * in range: it cannot prove it is in range *under speculation*, which
74 * is the whole threat model. OPTIMIZER_HIDE_VAR stops it from folding
75 * the answer away anyway.
76 */
78 return ~(long)(index | (size - 1UL - index)) >>
79 (BITS_PER_LONG - 1);
80}
81
96#define array_index_nospec(index, size) \
97({ \
98 typeof(index) _i = (index); \
99 typeof(size) _s = (size); \
100 /* see header: _i must fit an unsigned long; _s may be size_t */ \
101 BUILD_BUG_ON(sizeof(_i) > sizeof(unsigned long)); \
102 unsigned long _mask = \
103 array_index_mask_nospec((unsigned long)_i, \
104 (unsigned long)_s); \
105 (typeof(_i))(_i & _mask); \
106})
107
108#endif /* _LINUX_NOSPEC_H */
#define index(str, chr)
Definition string.h:66
#define BITS_PER_LONG
Definition bordoisila_bits.h:36
#define OPTIMIZER_HIDE_VAR(var)
Definition compiler.h:176
size_t size
Definition drm_bridge.h:1299