llvm.org GIT mirror llvm / d947f15
Fix use after free in BinaryStream library. This was reported by the ASAN bot, and it turned out to be a fairly fundamental problem with the design of VarStreamArray and the way it passes context information to the extractor. The fix was cumbersome, and I'm not entirely pleased with it, so I plan to revisit this design in the future when I'm not pressed to get the bots green again. For now, this fixes the issue by storing the context information by value instead of by reference, and introduces some impossibly-confusing template magic to make things "work". git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301999 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 3 years ago
12 changed file(s) with 238 addition(s) and 156 deletion(s). Raw diff Collapse all Expand all
5252 typedef void ContextType;
5353
5454 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
55 codeview::CVRecord &Item, void *Ctx) {
55 codeview::CVRecord &Item) {
5656 using namespace codeview;
5757 const RecordPrefix *Prefix = nullptr;
5858 BinaryStreamReader Reader(Stream);
3434 typedef void ContextType;
3535
3636 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
37 codeview::FileChecksumEntry &Item, void *Ctx);
37 codeview::FileChecksumEntry &Item);
3838 };
3939 }
4040
5454
5555 Error initialize(BinaryStreamReader Reader);
5656
57 Iterator begin() const { return Checksums.begin(); }
58 Iterator end() const { return Checksums.end(); }
57 Iterator begin() { return Checksums.begin(); }
58 Iterator end() { return Checksums.end(); }
5959
6060 const FileChecksumArray &getArray() const { return Checksums; }
6161
5656 ModuleDebugFragment &Frag;
5757 };
5858
59 typedef VarStreamArray ModuleDebugFragmentArray;
60
6159 } // namespace codeview
6260
6361 template <>
6563 typedef void ContextType;
6664
6765 static Error extract(BinaryStreamRef Stream, uint32_t &Length,
68 codeview::ModuleDebugFragmentRecord &Info, void *Ctx) {
66 codeview::ModuleDebugFragmentRecord &Info) {
6967 if (auto EC = codeview::ModuleDebugFragmentRecord::initialize(Stream, Info))
7068 return EC;
7169 Length = Info.getRecordLength();
7270 return Error::success();
7371 }
7472 };
73
74 namespace codeview {
75 typedef VarStreamArray ModuleDebugFragmentArray;
76 }
7577 } // namespace llvm
7678
7779 #endif // LLVM_DEBUGINFO_CODEVIEW_MODULEDEBUGFRAGMENTRECORD_H
4141 }
4242
4343 template <> struct VarStreamArrayExtractor {
44 typedef codeview::ModuleDebugInlineeLineFragmentRef ContextType;
44 typedef bool ContextType;
4545
4646 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
47 codeview::InlineeSourceLine &Item,
48 ContextType *Fragment);
47 codeview::InlineeSourceLine &Item, bool HasExtraFiles);
4948 };
5049
5150 namespace codeview {
6060
6161 class LineColumnExtractor {
6262 public:
63 typedef const LineFragmentHeader ContextType;
63 typedef const LineFragmentHeader *ContextType;
6464
6565 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
66 LineColumnEntry &Item, const LineFragmentHeader *Header);
66 LineColumnEntry &Item, const LineFragmentHeader *Ctx);
6767 };
6868
6969 class ModuleDebugLineFragmentRef final : public ModuleDebugFragmentRef {
6565 template <> struct VarStreamArrayExtractor {
6666 typedef void ContextType;
6767 static Error extract(BinaryStreamRef Stream, uint32_t &Length,
68 pdb::DbiModuleDescriptor &Info, void *Ctx) {
68 pdb::DbiModuleDescriptor &Info) {
6969 if (auto EC = pdb::DbiModuleDescriptor::initialize(Stream, Info))
7070 return EC;
7171 Length = Info.getRecordLength();
4141 /// having to specify a second template argument to VarStreamArray (documented
4242 /// below).
4343 template struct VarStreamArrayExtractor {
44 typedef void Context;
44 struct ContextType {};
4545
4646 // Method intentionally deleted. You must provide an explicit specialization
47 // with the following method implemented.
47 // with one of the following two methods implemented.
48 static Error extract(BinaryStreamRef Stream, uint32_t &Len, T &Item) = delete;
49
4850 static Error extract(BinaryStreamRef Stream, uint32_t &Len, T &Item,
49 Context *Ctx) = delete;
51 const ContextType &Ctx) = delete;
52 };
53
54 template
55 typename WrappedCtx>
56 class VarStreamArrayIterator
57 : public iterator_facade_base<
58 VarStreamArrayIterator,
59 std::forward_iterator_tag, Value> {
60 typedef VarStreamArrayIterator
61 IterType;
62
63 public:
64 VarStreamArrayIterator() = default;
65 VarStreamArrayIterator(const ArrayType &Array, const WrappedCtx &Ctx,
66 BinaryStreamRef Stream, bool *HadError = nullptr)
67 : IterRef(Stream), Ctx(&Ctx), Array(&Array), HadError(HadError) {
68 if (IterRef.getLength() == 0)
69 moveToEnd();
70 else {
71 auto EC = Ctx.template invoke(IterRef, ThisLen, ThisValue);
72 if (EC) {
73 consumeError(std::move(EC));
74 markError();
75 }
76 }
77 }
78
79 VarStreamArrayIterator(const ArrayType &Array, const WrappedCtx &Ctx,
80 bool *HadError = nullptr)
81 : VarStreamArrayIterator(Array, Ctx, Array.Stream, HadError) {}
82
83 VarStreamArrayIterator(const WrappedCtx &Ctx) : Ctx(&Ctx) {}
84 VarStreamArrayIterator(const VarStreamArrayIterator &Other) = default;
85
86 ~VarStreamArrayIterator() = default;
87
88 bool operator==(const IterType &R) const {
89 if (Array && R.Array) {
90 // Both have a valid array, make sure they're same.
91 assert(Array == R.Array);
92 return IterRef == R.IterRef;
93 }
94
95 // Both iterators are at the end.
96 if (!Array && !R.Array)
97 return true;
98
99 // One is not at the end and one is.
100 return false;
101 }
102
103 const Value &operator*() const {
104 assert(Array && !HasError);
105 return ThisValue;
106 }
107
108 Value &operator*() {
109 assert(Array && !HasError);
110 return ThisValue;
111 }
112
113 IterType &operator+=(unsigned N) {
114 for (unsigned I = 0; I < N; ++I) {
115 // We are done with the current record, discard it so that we are
116 // positioned at the next record.
117 IterRef = IterRef.drop_front(ThisLen);
118 if (IterRef.getLength() == 0) {
119 // There is nothing after the current record, we must make this an end
120 // iterator.
121 moveToEnd();
122 } else {
123 // There is some data after the current record.
124 auto EC = Ctx->template invoke(IterRef, ThisLen, ThisValue);
125 if (EC) {
126 consumeError(std::move(EC));
127 markError();
128 } else if (ThisLen == 0) {
129 // An empty record? Make this an end iterator.
130 moveToEnd();
131 }
132 }
133 }
134 return *this;
135 }
136
137 private:
138 void moveToEnd() {
139 Array = nullptr;
140 ThisLen = 0;
141 }
142 void markError() {
143 moveToEnd();
144 HasError = true;
145 if (HadError != nullptr)
146 *HadError = true;
147 }
148
149 Value ThisValue;
150 BinaryStreamRef IterRef;
151 const WrappedCtx *Ctx{nullptr};
152 const ArrayType *Array{nullptr};
153 uint32_t ThisLen{0};
154 bool HasError{false};
155 bool *HadError{nullptr};
156 };
157
158 template struct ContextWrapper {
159 ContextWrapper() = default;
160
161 explicit ContextWrapper(Context &&Ctx) : Ctx(Ctx) {}
162
163 template
164 Error invoke(BinaryStreamRef Stream, uint32_t &Len, T &Item) const {
165 return Extractor::extract(Stream, Len, Item, Ctx);
166 }
167
168 Context Ctx;
169 };
170
171 template struct ContextWrapper {
172 ContextWrapper() = default;
173
174 template
175 Error invoke(BinaryStreamRef Stream, uint32_t &Len, T &Item) const {
176 return Extractor::extract(Stream, Len, Item);
177 }
50178 };
51179
52180 /// VarStreamArray represents an array of variable length records backed by a
71199 /// context field during construction of the VarStreamArray that will be
72200 /// passed to each call to extract.
73201 ///
74 template
75 class VarStreamArrayIterator;
76
77 template
78 typename ExtractorType = VarStreamArrayExtractor>
79 class VarStreamArray {
80 public:
81 typedef typename ExtractorType::ContextType ContextType;
82 typedef VarStreamArrayIterator Iterator;
202 template
203 class VarStreamArrayBase {
204 typedef VarStreamArrayBase MyType;
205
206 public:
207 typedef VarStreamArrayIterator Iterator;
83208 friend Iterator;
84209
85 VarStreamArray() = default;
86
87 explicit VarStreamArray(BinaryStreamRef Stream,
88 ContextType *Context = nullptr)
89 : Stream(Stream), Context(Context) {}
90
91 VarStreamArray(const VarStreamArray &Other)
92 : Stream(Other.Stream), Context(Other.Context) {}
210 VarStreamArrayBase() = default;
211
212 VarStreamArrayBase(BinaryStreamRef Stream, const WrappedCtx &Ctx)
213 : Stream(Stream), Ctx(Ctx) {}
214
215 VarStreamArrayBase(const MyType &Other)
216 : Stream(Other.Stream), Ctx(Other.Ctx) {}
93217
94218 Iterator begin(bool *HadError = nullptr) const {
95219 if (empty())
96220 return end();
97221
98 return Iterator(*this, Context, HadError);
99 }
100
101 Iterator end() const { return Iterator(); }
222 return Iterator(*this, Ctx, Stream, HadError);
223 }
224
225 Iterator end() const { return Iterator(Ctx); }
102226
103227 bool empty() const { return Stream.getLength() == 0; }
104228
107231 /// since the behavior is undefined if \p Offset does not refer to the
108232 /// beginning of a valid record.
109233 Iterator at(uint32_t Offset) const {
110 return Iterator(*this, Context, Stream.drop_front(Offset), nullptr);
234 return Iterator(*this, Ctx, Stream.drop_front(Offset), nullptr);
111235 }
112236
113237 BinaryStreamRef getUnderlyingStream() const { return Stream; }
114238
115239 private:
116240 BinaryStreamRef Stream;
117 ContextType *Context = nullptr;
118 };
119
120 template
121 class VarStreamArrayIterator
122 : public iterator_facade_base<
123 VarStreamArrayIterator,
124 std::forward_iterator_tag, ValueType> {
125 typedef typename ExtractorType::ContextType ContextType;
126 typedef VarStreamArrayIterator IterType;
127 typedef VarStreamArray ArrayType;
128
129 public:
130 VarStreamArrayIterator(const ArrayType &Array, ContextType *Context,
131 BinaryStreamRef Stream, bool *HadError = nullptr)
132 : IterRef(Stream), Context(Context), Array(&Array), HadError(HadError) {
133 if (IterRef.getLength() == 0)
134 moveToEnd();
135 else {
136 auto EC = ExtractorType::extract(IterRef, ThisLen, ThisValue, Context);
137 if (EC) {
138 consumeError(std::move(EC));
139 markError();
140 }
141 }
142 }
143
144 VarStreamArrayIterator(const ArrayType &Array, ContextType *Context,
145 bool *HadError = nullptr)
146 : VarStreamArrayIterator(Array, Context, Array.Stream, HadError) {}
147
148 VarStreamArrayIterator() = default;
149 ~VarStreamArrayIterator() = default;
150
151 bool operator==(const IterType &R) const {
152 if (Array && R.Array) {
153 // Both have a valid array, make sure they're same.
154 assert(Array == R.Array);
155 return IterRef == R.IterRef;
156 }
157
158 // Both iterators are at the end.
159 if (!Array && !R.Array)
160 return true;
161
162 // One is not at the end and one is.
163 return false;
164 }
165
166 const ValueType &operator*() const {
167 assert(Array && !HasError);
168 return ThisValue;
169 }
170
171 ValueType &operator*() {
172 assert(Array && !HasError);
173 return ThisValue;
174 }
175
176 IterType &operator+=(unsigned N) {
177 for (unsigned I = 0; I < N; ++I) {
178 // We are done with the current record, discard it so that we are
179 // positioned at the next record.
180 IterRef = IterRef.drop_front(ThisLen);
181 if (IterRef.getLength() == 0) {
182 // There is nothing after the current record, we must make this an end
183 // iterator.
184 moveToEnd();
185 } else {
186 // There is some data after the current record.
187 auto EC = ExtractorType::extract(IterRef, ThisLen, ThisValue, Context);
188 if (EC) {
189 consumeError(std::move(EC));
190 markError();
191 } else if (ThisLen == 0) {
192 // An empty record? Make this an end iterator.
193 moveToEnd();
194 }
195 }
196 }
197 return *this;
198 }
199
200 private:
201 void moveToEnd() {
202 Array = nullptr;
203 ThisLen = 0;
204 }
205 void markError() {
206 moveToEnd();
207 HasError = true;
208 if (HadError != nullptr)
209 *HadError = true;
210 }
211
212 ValueType ThisValue;
213 BinaryStreamRef IterRef;
214 ContextType *Context{nullptr};
215 const ArrayType *Array{nullptr};
216 uint32_t ThisLen{0};
217 bool HasError{false};
218 bool *HadError{nullptr};
219 };
241 WrappedCtx Ctx;
242 };
243
244 template
245 class VarStreamArrayImpl
246 : public VarStreamArrayBase
247 ContextWrapper> {
248 typedef ContextWrapper WrappedContext;
249 typedef VarStreamArrayImpl MyType;
250 typedef VarStreamArrayBase BaseType;
251
252 public:
253 typedef Context ContextType;
254
255 VarStreamArrayImpl() = default;
256 VarStreamArrayImpl(BinaryStreamRef Stream, Context &&Ctx)
257 : BaseType(Stream, WrappedContext(std::forward(Ctx))) {}
258 };
259
260 template
261 class VarStreamArrayImpl
262 : public VarStreamArrayBase> {
263 typedef ContextWrapper WrappedContext;
264 typedef VarStreamArrayImpl MyType;
265 typedef VarStreamArrayBase BaseType;
266
267 public:
268 VarStreamArrayImpl() = default;
269 VarStreamArrayImpl(BinaryStreamRef Stream)
270 : BaseType(Stream, WrappedContext()) {}
271 };
272
273 template >
274 using VarStreamArray =
275 VarStreamArrayImpl;
220276
221277 template class FixedStreamArrayIterator;
222278
172172 /// \returns a success error code if the data was successfully read, otherwise
173173 /// returns an appropriate error code.
174174 template
175 Error
176 readArray(VarStreamArray &Array, uint32_t Size,
177 typename VarStreamArray::ContextType *Context = nullptr) {
175 Error readArray(VarStreamArray &Array, uint32_t Size) {
178176 BinaryStreamRef S;
179177 if (auto EC = readStreamRef(S, Size))
180178 return EC;
181 Array = VarStreamArray(S, Context);
179 Array = VarStreamArray(S);
180 return Error::success();
181 }
182
183 /// Read a VarStreamArray of size \p Size bytes and store the result into
184 /// \p Array. Updates the stream's offset to point after the newly read
185 /// array. Never causes a copy (although iterating the elements of the
186 /// VarStreamArray may, depending upon the implementation of the underlying
187 /// stream).
188 ///
189 /// \returns a success error code if the data was successfully read, otherwise
190 /// returns an appropriate error code.
191 template
192 Error readArray(VarStreamArray &Array, uint32_t Size,
193 ContextType &&Context) {
194 BinaryStreamRef S;
195 if (auto EC = readStreamRef(S, Size))
196 return EC;
197 Array = VarStreamArray(S, std::move(Context));
182198 return Error::success();
183199 }
184200
2424 };
2525
2626 Error llvm::VarStreamArrayExtractor::extract(
27 BinaryStreamRef Stream, uint32_t &Len, FileChecksumEntry &Item, void *Ctx) {
27 BinaryStreamRef Stream, uint32_t &Len, FileChecksumEntry &Item) {
2828 BinaryStreamReader Reader(Stream);
2929
3030 const FileChecksumEntryHeader *Header;
1616
1717 Error VarStreamArrayExtractor::extract(
1818 BinaryStreamRef Stream, uint32_t &Len, InlineeSourceLine &Item,
19 ContextType *Fragment) {
19 bool HasExtraFiles) {
2020 BinaryStreamReader Reader(Stream);
2121
2222 if (auto EC = Reader.readObject(Item.Header))
2323 return EC;
2424
25 if (Fragment->hasExtraFiles()) {
25 if (HasExtraFiles) {
2626 uint32_t ExtraFileCount;
2727 if (auto EC = Reader.readInteger(ExtraFileCount))
2828 return EC;
4141 if (auto EC = Reader.readEnum(Signature))
4242 return EC;
4343
44 if (auto EC = Reader.readArray(Lines, Reader.bytesRemaining(), this))
44 if (auto EC =
45 Reader.readArray(Lines, Reader.bytesRemaining(), hasExtraFiles()))
4546 return EC;
4647
4748 assert(Reader.bytesRemaining() == 0);
8787 uint32_t O = ObjFileName.size() + 1;
8888 return alignTo(L + M + O, sizeof(uint32_t));
8989 }
90
91 template struct Foo {
92 explicit Foo(T &&Answer) : Answer(Answer) {}
93
94 T Answer;
95 };
96
97 template Foo makeFoo(T &&t) { return Foo(std::move(t)); }
9098
9199 void DbiModuleDescriptorBuilder::finalize() {
92100 Layout.FileNameOffs = 0; // TODO: Fix this
357357
358358 struct StringExtractor {
359359 public:
360 typedef uint32_t ContextType;
360 typedef uint32_t &ContextType;
361361 static Error extract(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item,
362 uint32_t *Index) {
363 if (*Index == 0)
362 uint32_t &Index) {
363 if (Index == 0)
364364 Len = strlen("1. Test");
365 else if (*Index == 1)
365 else if (Index == 1)
366366 Len = strlen("2. Longer Test");
367 else if (*Index == 2)
367 else if (Index == 2)
368368 Len = strlen("3. Really Long Test");
369369 else
370370 Len = strlen("4. Super Extra Longest Test Of All");
373373 return EC;
374374 Item =
375375 StringRef(reinterpret_cast(Bytes.data()), Bytes.size());
376 ++(*Index);
376 ++Index;
377377 return Error::success();
378378 }
379379 };
380380
381381 for (auto &Stream : Streams) {
382382 uint32_t Context = 0;
383 VarStreamArray Array(*Stream.Input, &Context);
383 VarStreamArray Array(*Stream.Input, Context);
384384 auto Iter = Array.begin();
385385 ASSERT_EQ("1. Test", *Iter++);
386386 ASSERT_EQ("2. Longer Test", *Iter++);