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