1 //===-- tsan_shadow.h -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef TSAN_SHADOW_H
10 #define TSAN_SHADOW_H
11 
12 #include "tsan_defs.h"
13 #include "tsan_trace.h"
14 
15 namespace __tsan {
16 
17 // FastState (from most significant bit):
18 //   ignore          : 1
19 //   tid             : kTidBits
20 //   unused          : -
21 //   history_size    : 3
22 //   epoch           : kClkBits
23 class FastState {
24  public:
FastState(u64 tid,u64 epoch)25   FastState(u64 tid, u64 epoch) {
26     x_ = tid << kTidShift;
27     x_ |= epoch;
28     DCHECK_EQ(tid, this->tid());
29     DCHECK_EQ(epoch, this->epoch());
30     DCHECK_EQ(GetIgnoreBit(), false);
31   }
32 
FastState(u64 x)33   explicit FastState(u64 x) : x_(x) {}
34 
raw()35   u64 raw() const { return x_; }
36 
tid()37   u64 tid() const {
38     u64 res = (x_ & ~kIgnoreBit) >> kTidShift;
39     return res;
40   }
41 
TidWithIgnore()42   u64 TidWithIgnore() const {
43     u64 res = x_ >> kTidShift;
44     return res;
45   }
46 
epoch()47   u64 epoch() const {
48     u64 res = x_ & ((1ull << kClkBits) - 1);
49     return res;
50   }
51 
IncrementEpoch()52   void IncrementEpoch() {
53     u64 old_epoch = epoch();
54     x_ += 1;
55     DCHECK_EQ(old_epoch + 1, epoch());
56     (void)old_epoch;
57   }
58 
SetIgnoreBit()59   void SetIgnoreBit() { x_ |= kIgnoreBit; }
ClearIgnoreBit()60   void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
GetIgnoreBit()61   bool GetIgnoreBit() const { return (s64)x_ < 0; }
62 
SetHistorySize(int hs)63   void SetHistorySize(int hs) {
64     CHECK_GE(hs, 0);
65     CHECK_LE(hs, 7);
66     x_ = (x_ & ~(kHistoryMask << kHistoryShift)) | (u64(hs) << kHistoryShift);
67   }
68 
69   ALWAYS_INLINE
GetHistorySize()70   int GetHistorySize() const {
71     return (int)((x_ >> kHistoryShift) & kHistoryMask);
72   }
73 
ClearHistorySize()74   void ClearHistorySize() { SetHistorySize(0); }
75 
76   ALWAYS_INLINE
GetTracePos()77   u64 GetTracePos() const {
78     const int hs = GetHistorySize();
79     // When hs == 0, the trace consists of 2 parts.
80     const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1;
81     return epoch() & mask;
82   }
83 
84  private:
85   friend class Shadow;
86   static const int kTidShift = 64 - kTidBits - 1;
87   static const u64 kIgnoreBit = 1ull << 63;
88   static const u64 kFreedBit = 1ull << 63;
89   static const u64 kHistoryShift = kClkBits;
90   static const u64 kHistoryMask = 7;
91   u64 x_;
92 };
93 
94 // Shadow (from most significant bit):
95 //   freed           : 1
96 //   tid             : kTidBits
97 //   unused          : 1
98 //   is_atomic       : 1
99 //   is_read         : 1
100 //   size_log        : 2
101 //   addr0           : 3
102 //   epoch           : kClkBits
103 class Shadow : public FastState {
104  public:
Shadow(u64 x)105   explicit Shadow(u64 x) : FastState(x) {}
106 
Shadow(const FastState & s)107   explicit Shadow(const FastState &s) : FastState(s.x_) { ClearHistorySize(); }
108 
SetAddr0AndSizeLog(u64 addr0,unsigned kAccessSizeLog)109   void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
110     DCHECK_EQ((x_ >> kClkBits) & 31, 0);
111     DCHECK_LE(addr0, 7);
112     DCHECK_LE(kAccessSizeLog, 3);
113     x_ |= ((kAccessSizeLog << 3) | addr0) << kClkBits;
114     DCHECK_EQ(kAccessSizeLog, size_log());
115     DCHECK_EQ(addr0, this->addr0());
116   }
117 
SetWrite(unsigned kAccessIsWrite)118   void SetWrite(unsigned kAccessIsWrite) {
119     DCHECK_EQ(x_ & kReadBit, 0);
120     if (!kAccessIsWrite)
121       x_ |= kReadBit;
122     DCHECK_EQ(kAccessIsWrite, IsWrite());
123   }
124 
SetAtomic(bool kIsAtomic)125   void SetAtomic(bool kIsAtomic) {
126     DCHECK(!IsAtomic());
127     if (kIsAtomic)
128       x_ |= kAtomicBit;
129     DCHECK_EQ(IsAtomic(), kIsAtomic);
130   }
131 
IsAtomic()132   bool IsAtomic() const { return x_ & kAtomicBit; }
133 
IsZero()134   bool IsZero() const { return x_ == 0; }
135 
TidsAreEqual(const Shadow s1,const Shadow s2)136   static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
137     u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
138     DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore());
139     return shifted_xor == 0;
140   }
141 
Addr0AndSizeAreEqual(const Shadow s1,const Shadow s2)142   static ALWAYS_INLINE bool Addr0AndSizeAreEqual(const Shadow s1,
143                                                  const Shadow s2) {
144     u64 masked_xor = ((s1.x_ ^ s2.x_) >> kClkBits) & 31;
145     return masked_xor == 0;
146   }
147 
TwoRangesIntersect(Shadow s1,Shadow s2,unsigned kS2AccessSize)148   static ALWAYS_INLINE bool TwoRangesIntersect(Shadow s1, Shadow s2,
149                                                unsigned kS2AccessSize) {
150     bool res = false;
151     u64 diff = s1.addr0() - s2.addr0();
152     if ((s64)diff < 0) {  // s1.addr0 < s2.addr0
153       // if (s1.addr0() + size1) > s2.addr0()) return true;
154       if (s1.size() > -diff)
155         res = true;
156     } else {
157       // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true;
158       if (kS2AccessSize > diff)
159         res = true;
160     }
161     DCHECK_EQ(res, TwoRangesIntersectSlow(s1, s2));
162     DCHECK_EQ(res, TwoRangesIntersectSlow(s2, s1));
163     return res;
164   }
165 
addr0()166   u64 ALWAYS_INLINE addr0() const { return (x_ >> kClkBits) & 7; }
size()167   u64 ALWAYS_INLINE size() const { return 1ull << size_log(); }
IsWrite()168   bool ALWAYS_INLINE IsWrite() const { return !IsRead(); }
IsRead()169   bool ALWAYS_INLINE IsRead() const { return x_ & kReadBit; }
170 
171   // The idea behind the freed bit is as follows.
172   // When the memory is freed (or otherwise unaccessible) we write to the shadow
173   // values with tid/epoch related to the free and the freed bit set.
174   // During memory accesses processing the freed bit is considered
175   // as msb of tid. So any access races with shadow with freed bit set
176   // (it is as if write from a thread with which we never synchronized before).
177   // This allows us to detect accesses to freed memory w/o additional
178   // overheads in memory access processing and at the same time restore
179   // tid/epoch of free.
MarkAsFreed()180   void MarkAsFreed() { x_ |= kFreedBit; }
181 
IsFreed()182   bool IsFreed() const { return x_ & kFreedBit; }
183 
GetFreedAndReset()184   bool GetFreedAndReset() {
185     bool res = x_ & kFreedBit;
186     x_ &= ~kFreedBit;
187     return res;
188   }
189 
IsBothReadsOrAtomic(bool kIsWrite,bool kIsAtomic)190   bool ALWAYS_INLINE IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const {
191     bool v = x_ & ((u64(kIsWrite ^ 1) << kReadShift) |
192                    (u64(kIsAtomic) << kAtomicShift));
193     DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic));
194     return v;
195   }
196 
IsRWNotWeaker(bool kIsWrite,bool kIsAtomic)197   bool ALWAYS_INLINE IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const {
198     bool v = ((x_ >> kReadShift) & 3) <= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
199     DCHECK_EQ(v, (IsAtomic() < kIsAtomic) ||
200                      (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite));
201     return v;
202   }
203 
IsRWWeakerOrEqual(bool kIsWrite,bool kIsAtomic)204   bool ALWAYS_INLINE IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const {
205     bool v = ((x_ >> kReadShift) & 3) >= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
206     DCHECK_EQ(v, (IsAtomic() > kIsAtomic) ||
207                      (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite));
208     return v;
209   }
210 
211  private:
212   static const u64 kReadShift = 5 + kClkBits;
213   static const u64 kReadBit = 1ull << kReadShift;
214   static const u64 kAtomicShift = 6 + kClkBits;
215   static const u64 kAtomicBit = 1ull << kAtomicShift;
216 
size_log()217   u64 size_log() const { return (x_ >> (3 + kClkBits)) & 3; }
218 
TwoRangesIntersectSlow(const Shadow s1,const Shadow s2)219   static bool TwoRangesIntersectSlow(const Shadow s1, const Shadow s2) {
220     if (s1.addr0() == s2.addr0())
221       return true;
222     if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
223       return true;
224     if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
225       return true;
226     return false;
227   }
228 };
229 
230 const RawShadow kShadowRodata = (RawShadow)-1;  // .rodata shadow marker
231 
232 }  // namespace __tsan
233 
234 #endif
235