xref: /aosp_15_r20/external/google-breakpad/src/client/minidump_file_writer.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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