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 namespace google_breakpad {
48 
49 const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
50 
MinidumpFileWriter()51 MinidumpFileWriter::MinidumpFileWriter()
52     : file_(-1),
53       close_file_when_destroyed_(true),
54       position_(0),
55       size_(0) {
56 }
57 
~MinidumpFileWriter()58 MinidumpFileWriter::~MinidumpFileWriter() {
59   if (close_file_when_destroyed_)
60     Close();
61 }
62 
Open(const char * path)63 bool MinidumpFileWriter::Open(const char *path) {
64   assert(file_ == -1);
65 #if defined(__linux__) && __linux__
66   file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
67 #else
68   file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
69 #endif
70 
71   return file_ != -1;
72 }
73 
SetFile(const int file)74 void MinidumpFileWriter::SetFile(const int file) {
75   assert(file_ == -1);
76   file_ = file;
77   close_file_when_destroyed_ = false;
78 }
79 
Close()80 bool MinidumpFileWriter::Close() {
81   bool result = true;
82 
83   if (file_ != -1) {
84     if (-1 == ftruncate(file_, position_)) {
85        return false;
86     }
87 #if defined(__linux__) && __linux__
88     result = (sys_close(file_) == 0);
89 #else
90     result = (close(file_) == 0);
91 #endif
92     file_ = -1;
93   }
94 
95   return result;
96 }
97 
CopyStringToMDString(const wchar_t * str,unsigned int length,TypedMDRVA<MDString> * mdstring)98 bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
99                                               unsigned int length,
100                                               TypedMDRVA<MDString> *mdstring) {
101   bool result = true;
102   if (sizeof(wchar_t) == sizeof(uint16_t)) {
103     // Shortcut if wchar_t is the same size as MDString's buffer
104     result = mdstring->Copy(str, mdstring->get()->length);
105   } else {
106     uint16_t out[2];
107     int out_idx = 0;
108 
109     // Copy the string character by character
110     while (length && result) {
111       UTF32ToUTF16Char(*str, out);
112       if (!out[0])
113         return false;
114 
115       // Process one character at a time
116       --length;
117       ++str;
118 
119       // Append the one or two UTF-16 characters.  The first one will be non-
120       // zero, but the second one may be zero, depending on the conversion from
121       // UTF-32.
122       int out_count = out[1] ? 2 : 1;
123       size_t out_size = sizeof(uint16_t) * out_count;
124       result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
125       out_idx += out_count;
126     }
127   }
128   return result;
129 }
130 
CopyStringToMDString(const char * str,unsigned int length,TypedMDRVA<MDString> * mdstring)131 bool MinidumpFileWriter::CopyStringToMDString(const char *str,
132                                               unsigned int length,
133                                               TypedMDRVA<MDString> *mdstring) {
134   bool result = true;
135   uint16_t out[2];
136   int out_idx = 0;
137 
138   // Copy the string character by character
139   while (length && result) {
140     int conversion_count = UTF8ToUTF16Char(str, length, out);
141     if (!conversion_count)
142       return false;
143 
144     // Move the pointer along based on the nubmer of converted characters
145     length -= conversion_count;
146     str += conversion_count;
147 
148     // Append the one or two UTF-16 characters
149     int out_count = out[1] ? 2 : 1;
150     size_t out_size = sizeof(uint16_t) * out_count;
151     result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
152     out_idx += out_count;
153   }
154   return result;
155 }
156 
157 template <typename CharType>
WriteStringCore(const CharType * str,unsigned int length,MDLocationDescriptor * location)158 bool MinidumpFileWriter::WriteStringCore(const CharType *str,
159                                          unsigned int length,
160                                          MDLocationDescriptor *location) {
161   assert(str);
162   assert(location);
163   // Calculate the mdstring length by either limiting to |length| as passed in
164   // or by finding the location of the NULL character.
165   unsigned int mdstring_length = 0;
166   if (!length)
167     length = INT_MAX;
168   for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
169     ;
170 
171   // Allocate the string buffer
172   TypedMDRVA<MDString> mdstring(this);
173   if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
174     return false;
175 
176   // Set length excluding the NULL and copy the string
177   mdstring.get()->length =
178       static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
179   bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
180 
181   // NULL terminate
182   if (result) {
183     uint16_t ch = 0;
184     result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
185 
186     if (result)
187       *location = mdstring.location();
188   }
189 
190   return result;
191 }
192 
WriteString(const wchar_t * str,unsigned int length,MDLocationDescriptor * location)193 bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
194                  MDLocationDescriptor *location) {
195   return WriteStringCore(str, length, location);
196 }
197 
WriteString(const char * str,unsigned int length,MDLocationDescriptor * location)198 bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
199                  MDLocationDescriptor *location) {
200   return WriteStringCore(str, length, location);
201 }
202 
WriteMemory(const void * src,size_t size,MDMemoryDescriptor * output)203 bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
204                                      MDMemoryDescriptor *output) {
205   assert(src);
206   assert(output);
207   UntypedMDRVA mem(this);
208 
209   if (!mem.Allocate(size))
210     return false;
211   if (!mem.Copy(src, mem.size()))
212     return false;
213 
214   output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
215   output->memory = mem.location();
216 
217   return true;
218 }
219 
Allocate(size_t size)220 MDRVA MinidumpFileWriter::Allocate(size_t size) {
221   assert(size);
222   assert(file_ != -1);
223   size_t aligned_size = (size + 7) & ~7;  // 64-bit alignment
224 
225   if (position_ + aligned_size > size_) {
226     size_t growth = aligned_size;
227     size_t minimal_growth = getpagesize();
228 
229     // Ensure that the file grows by at least the size of a memory page
230     if (growth < minimal_growth)
231       growth = minimal_growth;
232 
233     size_t new_size = size_ + growth;
234     if (ftruncate(file_, new_size) != 0)
235       return kInvalidMDRVA;
236 
237     size_ = new_size;
238   }
239 
240   MDRVA current_position = position_;
241   position_ += static_cast<MDRVA>(aligned_size);
242 
243   return current_position;
244 }
245 
Copy(MDRVA position,const void * src,ssize_t size)246 bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
247   assert(src);
248   assert(size);
249   assert(file_ != -1);
250 
251   // Ensure that the data will fit in the allocated space
252   if (static_cast<size_t>(size + position) > size_)
253     return false;
254 
255   // Seek and write the data
256 #if defined(__linux__) && __linux__
257   if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
258     if (sys_write(file_, src, size) == size) {
259 #else
260   if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
261     if (write(file_, src, size) == size) {
262 #endif
263       return true;
264     }
265   }
266 
267   return false;
268 }
269 
270 bool UntypedMDRVA::Allocate(size_t size) {
271   assert(size_ == 0);
272   size_ = size;
273   position_ = writer_->Allocate(size_);
274   return position_ != MinidumpFileWriter::kInvalidMDRVA;
275 }
276 
277 bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
278   assert(src);
279   assert(size);
280   assert(pos + size <= position_ + size_);
281   return writer_->Copy(pos, src, size);
282 }
283 
284 }  // namespace google_breakpad
285