1 // Copyright 2006 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // minidump_file_writer.cc: Minidump file writer implementation.
30 //
31 // See minidump_file_writer.h for documentation.
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h> // Must come first
35 #endif
36
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "client/minidump_file_writer-inl.h"
44 #include "common/linux/linux_libc_support.h"
45 #include "common/string_conversion.h"
46 #if defined(__linux__) && __linux__
47 #include "third_party/lss/linux_syscall_support.h"
48 #endif
49
50 #if defined(__ANDROID__)
51 #include <errno.h>
52
53 namespace {
54
55 bool g_need_ftruncate_workaround = false;
56 bool g_checked_need_ftruncate_workaround = false;
57
CheckNeedsFTruncateWorkAround(int file)58 void CheckNeedsFTruncateWorkAround(int file) {
59 if (g_checked_need_ftruncate_workaround) {
60 return;
61 }
62 g_checked_need_ftruncate_workaround = true;
63
64 // Attempt an idempotent truncate that chops off nothing and see if we
65 // run into any sort of errors.
66 off_t offset = sys_lseek(file, 0, SEEK_END);
67 if (offset == -1) {
68 // lseek failed. Don't apply work around. It's unlikely that we can write
69 // to a minidump with either method.
70 return;
71 }
72
73 int result = ftruncate(file, offset);
74 if (result == -1 && errno == EACCES) {
75 // It very likely that we are running into the kernel bug in M devices.
76 // We are going to deploy the workaround for writing minidump files
77 // without uses of ftruncate(). This workaround should be fine even
78 // for kernels without the bug.
79 // See http://crbug.com/542840 for more details.
80 g_need_ftruncate_workaround = true;
81 }
82 }
83
NeedsFTruncateWorkAround()84 bool NeedsFTruncateWorkAround() {
85 return g_need_ftruncate_workaround;
86 }
87
88 } // namespace
89 #endif // defined(__ANDROID__)
90
91 namespace google_breakpad {
92
93 const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
94
MinidumpFileWriter()95 MinidumpFileWriter::MinidumpFileWriter()
96 : file_(-1),
97 close_file_when_destroyed_(true),
98 position_(0),
99 size_(0) {
100 }
101
~MinidumpFileWriter()102 MinidumpFileWriter::~MinidumpFileWriter() {
103 if (close_file_when_destroyed_)
104 Close();
105 }
106
Open(const char * path)107 bool MinidumpFileWriter::Open(const char* path) {
108 assert(file_ == -1);
109 #if defined(__linux__) && __linux__
110 file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
111 #else
112 file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
113 #endif
114
115 return file_ != -1;
116 }
117
SetFile(const int file)118 void MinidumpFileWriter::SetFile(const int file) {
119 assert(file_ == -1);
120 file_ = file;
121 close_file_when_destroyed_ = false;
122 #if defined(__ANDROID__)
123 CheckNeedsFTruncateWorkAround(file);
124 #endif
125 }
126
Close()127 bool MinidumpFileWriter::Close() {
128 bool result = true;
129
130 if (file_ != -1) {
131 #if defined(__ANDROID__)
132 if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
133 return false;
134 }
135 #else
136 if (ftruncate(file_, position_)) {
137 return false;
138 }
139 #endif
140 #if defined(__linux__) && __linux__
141 result = (sys_close(file_) == 0);
142 #else
143 result = (close(file_) == 0);
144 #endif
145 file_ = -1;
146 }
147
148 return result;
149 }
150
CopyStringToMDString(const wchar_t * str,unsigned int length,TypedMDRVA<MDString> * mdstring)151 bool MinidumpFileWriter::CopyStringToMDString(const wchar_t* str,
152 unsigned int length,
153 TypedMDRVA<MDString>* mdstring) {
154 bool result = true;
155 if (sizeof(wchar_t) == sizeof(uint16_t)) {
156 // Shortcut if wchar_t is the same size as MDString's buffer
157 result = mdstring->Copy(str, mdstring->get()->length);
158 } else {
159 uint16_t out[2];
160 int out_idx = 0;
161
162 // Copy the string character by character
163 while (length && result) {
164 UTF32ToUTF16Char(*str, out);
165 if (!out[0])
166 return false;
167
168 // Process one character at a time
169 --length;
170 ++str;
171
172 // Append the one or two UTF-16 characters. The first one will be non-
173 // zero, but the second one may be zero, depending on the conversion from
174 // UTF-32.
175 int out_count = out[1] ? 2 : 1;
176 size_t out_size = sizeof(uint16_t) * out_count;
177 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
178 out_idx += out_count;
179 }
180 }
181 return result;
182 }
183
CopyStringToMDString(const char * str,unsigned int length,TypedMDRVA<MDString> * mdstring)184 bool MinidumpFileWriter::CopyStringToMDString(const char* str,
185 unsigned int length,
186 TypedMDRVA<MDString>* mdstring) {
187 bool result = true;
188 uint16_t out[2];
189 int out_idx = 0;
190
191 // Copy the string character by character
192 while (length && result) {
193 int conversion_count = UTF8ToUTF16Char(str, length, out);
194 if (!conversion_count)
195 return false;
196
197 // Move the pointer along based on the nubmer of converted characters
198 length -= conversion_count;
199 str += conversion_count;
200
201 // Append the one or two UTF-16 characters
202 int out_count = out[1] ? 2 : 1;
203 size_t out_size = sizeof(uint16_t) * out_count;
204 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
205 out_idx += out_count;
206 }
207 return result;
208 }
209
210 template <typename CharType>
WriteStringCore(const CharType * str,unsigned int length,MDLocationDescriptor * location)211 bool MinidumpFileWriter::WriteStringCore(const CharType* str,
212 unsigned int length,
213 MDLocationDescriptor* location) {
214 assert(str);
215 assert(location);
216 // Calculate the mdstring length by either limiting to |length| as passed in
217 // or by finding the location of the NULL character.
218 unsigned int mdstring_length = 0;
219 if (!length)
220 length = INT_MAX;
221 for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
222 ;
223
224 // Allocate the string buffer
225 TypedMDRVA<MDString> mdstring(this);
226 if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
227 return false;
228
229 // Set length excluding the NULL and copy the string
230 mdstring.get()->length =
231 static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
232 bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
233
234 // NULL terminate
235 if (result) {
236 uint16_t ch = 0;
237 result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
238
239 if (result)
240 *location = mdstring.location();
241 }
242
243 return result;
244 }
245
WriteString(const wchar_t * str,unsigned int length,MDLocationDescriptor * location)246 bool MinidumpFileWriter::WriteString(const wchar_t* str, unsigned int length,
247 MDLocationDescriptor* location) {
248 return WriteStringCore(str, length, location);
249 }
250
WriteString(const char * str,unsigned int length,MDLocationDescriptor * location)251 bool MinidumpFileWriter::WriteString(const char* str, unsigned int length,
252 MDLocationDescriptor* location) {
253 return WriteStringCore(str, length, location);
254 }
255
WriteMemory(const void * src,size_t size,MDMemoryDescriptor * output)256 bool MinidumpFileWriter::WriteMemory(const void* src, size_t size,
257 MDMemoryDescriptor* output) {
258 assert(src);
259 assert(output);
260 UntypedMDRVA mem(this);
261
262 if (!mem.Allocate(size))
263 return false;
264 if (!mem.Copy(src, mem.size()))
265 return false;
266
267 output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
268 output->memory = mem.location();
269
270 return true;
271 }
272
Allocate(size_t size)273 MDRVA MinidumpFileWriter::Allocate(size_t size) {
274 assert(size);
275 assert(file_ != -1);
276 #if defined(__ANDROID__)
277 if (NeedsFTruncateWorkAround()) {
278 // If ftruncate() is not available. We simply increase the size beyond the
279 // current file size. sys_write() will expand the file when data is written
280 // to it. Because we did not over allocate to fit memory pages, we also
281 // do not need to ftruncate() the file once we are done.
282 size_ += size;
283
284 // We don't need to seek since the file is unchanged.
285 MDRVA current_position = position_;
286 position_ += static_cast<MDRVA>(size);
287 return current_position;
288 }
289 #endif
290 size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
291
292 if (position_ + aligned_size > size_) {
293 size_t growth = aligned_size;
294 size_t minimal_growth = getpagesize();
295
296 // Ensure that the file grows by at least the size of a memory page
297 if (growth < minimal_growth)
298 growth = minimal_growth;
299
300 size_t new_size = size_ + growth;
301 if (ftruncate(file_, new_size) != 0)
302 return kInvalidMDRVA;
303
304 size_ = new_size;
305 }
306
307 MDRVA current_position = position_;
308 position_ += static_cast<MDRVA>(aligned_size);
309
310 return current_position;
311 }
312
Copy(MDRVA position,const void * src,ssize_t size)313 bool MinidumpFileWriter::Copy(MDRVA position, const void* src, ssize_t size) {
314 assert(src);
315 assert(size);
316 assert(file_ != -1);
317
318 // Ensure that the data will fit in the allocated space
319 if (static_cast<size_t>(size + position) > size_)
320 return false;
321
322 // Seek and write the data
323 #if defined(__linux__) && __linux__
324 if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
325 if (sys_write(file_, src, size) == size) {
326 return true;
327 }
328 }
329 #else
330 if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
331 if (write(file_, src, size) == size) {
332 return true;
333 }
334 }
335 #endif
336 return false;
337 }
338
Allocate(size_t size)339 bool UntypedMDRVA::Allocate(size_t size) {
340 assert(size_ == 0);
341 size_ = size;
342 position_ = writer_->Allocate(size_);
343 return position_ != MinidumpFileWriter::kInvalidMDRVA;
344 }
345
Copy(MDRVA pos,const void * src,size_t size)346 bool UntypedMDRVA::Copy(MDRVA pos, const void* src, size_t size) {
347 assert(src);
348 assert(size);
349 assert(pos + size <= position_ + size_);
350 return writer_->Copy(pos, src, size);
351 }
352
353 } // namespace google_breakpad
354