1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Declares a quarantine, which is used to temporarily house allocations after
16 : // they've been freed, permitting use-after-frees to be detected.
17 :
18 : #ifndef SYZYGY_AGENT_ASAN_QUARANTINE_H_
19 : #define SYZYGY_AGENT_ASAN_QUARANTINE_H_
20 :
21 : #include <vector>
22 :
23 : #include "base/logging.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 : // Specifies the color of the quarantine, depending on its size. YELLOW means
29 : // that we are below the maximum size whereas BLACK means we are way overbudget.
30 : // We also have two other colors, GREEN and RED, that are used to add
31 : // hysteresis. Basically, the color order is as follows:
32 : // GREEN -> YELLOW -> RED -> BLACK
33 : // Having these multiple colors allows for trimming the quarantine at different
34 : // paces, depending on urgency (urgent trimming is done synchronously on the
35 : // critical path whereas non-urgent is done asynchronously in a background
36 : // thread). For more information about the colors, see implementation of
37 : // GetQuarantineColor.
38 : enum TrimColor { GREEN, YELLOW, RED, BLACK };
39 :
40 : // Used to indicate whether the quarantine must be trimmed synchronously, be
41 : // scheduled for trimming by the background thread (asynchronously) or both.
42 : using TrimStatus = uint32_t;
43 : enum TrimStatusBits : uint32_t {
44 : TRIM_NOT_REQUIRED = 0,
45 : ASYNC_TRIM_REQUIRED = 1 << 0,
46 : SYNC_TRIM_REQUIRED = 1 << 1
47 : };
48 :
49 : // Type returned by Push. It returns whether the push was successful or not and
50 : // whether the quarantine requires trimming (either sync and/or async).
51 : struct PushResult {
52 : bool push_successful;
53 : TrimStatus trim_status;
54 : };
55 :
56 : // Type returned by Pop. It returns whether the pop was successful or not and
57 : // the color of the quarantine post-pop.
58 : struct PopResult {
59 : bool pop_successful;
60 : TrimColor trim_color;
61 : };
62 :
63 : // The interface that quarantines must satisfy. They store literal copies of
64 : // objects of type |ObjectType|.
65 : //
66 : // Placing objects in the quarantine and removing them from it are factored
67 : // out as two separate steps. Thus it is possible for a quarantine invariant
68 : // to be invalidated by a call to 'Push', which won't be restored until
69 : // sufficient calls to 'Pop' have been made.
70 : //
71 : // This has been templated on the object type to allow easier unittesting.
72 : //
73 : // @tparam ObjectType The type of object stored by the quarantine.
74 : template<typename ObjectType>
75 : class QuarantineInterface {
76 : public:
77 : typedef ObjectType Object;
78 : typedef std::vector<Object> ObjectVector;
79 :
80 : // Constructor.
81 E : QuarantineInterface() { }
82 :
83 : // Virtual destructor.
84 E : virtual ~QuarantineInterface() { }
85 :
86 : // Places an allocation in the quarantine. This routine must be called under
87 : // Lock.
88 : // @param The object to place in the quarantine.
89 : // @returns a PushResult.
90 : virtual PushResult Push(const Object& object) = 0;
91 :
92 : // Potentially removes an object from the quarantine to maintain the
93 : // invariant. This routine must be thread-safe, and implement its own locking.
94 : // @param object Is filled in with a copy of the removed object.
95 : // @returns a PopResult.
96 : virtual PopResult Pop(Object* object) = 0;
97 :
98 : // Removes all objects from the quarantine, placing them in the provided
99 : // vector. This routine must be thread-safe, and implement its own locking.
100 : virtual void Empty(ObjectVector* objects) = 0;
101 :
102 : // The number of objects currently in the quarantine. Only used in testing, as
103 : // the implementation is racy.
104 : // @returns the number of objects in the quarantine.
105 : virtual size_t GetCountForTesting() = 0;
106 :
107 : // An automatic quarantine lock.
108 : //
109 : // This class is nested into the QuarantineInterface class to avoid a
110 : // complicated template definition. It also avoids exposing the Lock/Unlock
111 : // functions.
112 : class AutoQuarantineLock {
113 : public:
114 : // Constructor. Automatically lock the quarantine.
115 E : AutoQuarantineLock(QuarantineInterface* quarantine,
116 : const ObjectType& object)
117 E : : quarantine_(quarantine) {
118 E : DCHECK_NE(reinterpret_cast<QuarantineInterface*>(NULL), quarantine_);
119 E : lock_index_ = quarantine_->GetLockId(object);
120 E : quarantine_->Lock(lock_index_);
121 E : }
122 :
123 : // Destructor. Automatically unlock the quarantine.
124 E : ~AutoQuarantineLock() {
125 E : quarantine_->Unlock(lock_index_);
126 E : }
127 :
128 : private:
129 : // The bucket to lock in the quarantine.
130 : size_t lock_index_;
131 :
132 : // The quarantine to lock.
133 : QuarantineInterface* quarantine_;
134 :
135 : DISALLOW_COPY_AND_ASSIGN(AutoQuarantineLock);
136 : };
137 :
138 : private:
139 : // Get the lock ID associated with a given object in the quarantine. This is
140 : // useful in the case where there's several buckets in the quarantine.
141 : // @param object The object for which we want to retrieve the lock ID
142 : // associated with it.
143 : // @returns the lock ID associated with this object.
144 : virtual size_t GetLockId(const Object& object) = 0;
145 :
146 : // Lock the quarantine.
147 : // @param id The bucket to lock, ignored if the quarantine isn't sharded.
148 : virtual void Lock(size_t id) = 0;
149 :
150 : // Unlock the quarantine.
151 : // @param id The bucket to lock, ignored if the quarantine isn't sharded.
152 : virtual void Unlock(size_t id) = 0;
153 :
154 : DISALLOW_COPY_AND_ASSIGN(QuarantineInterface);
155 : };
156 :
157 : // Quarantines in Asan are typically storing blocks. Here they are represented
158 : // by a CompactBlockInfo, which contains information that the quarantine
159 : // frequently accesses.
160 : struct CompactBlockInfo; // Forward declaration.
161 : typedef QuarantineInterface<CompactBlockInfo> BlockQuarantineInterface;
162 :
163 : } // namespace asan
164 : } // namespace agent
165 :
166 : #endif // SYZYGY_AGENT_ASAN_QUARANTINE_H_
|