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
31#define VERBOSE 0
32
33#if VERBOSE
34  static bool gDebugLog = true;
35#else
36  static bool gDebugLog = false;
37#endif
38
39#define DEBUGLOG if (gDebugLog) fprintf
40#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
41
42#import "common/mac/MachIPC.h"
43#import "common/mac/SimpleStringDictionary.h"
44
45#import "client/mac/crash_generation/Inspector.h"
46#import "client/mac/handler/exception_handler.h"
47#import "client/mac/Framework/Breakpad.h"
48#import "client/mac/Framework/OnDemandServer.h"
49#import "client/mac/handler/protected_memory_allocator.h"
50
51#import <sys/stat.h>
52#import <sys/sysctl.h>
53
54#import <Foundation/Foundation.h>
55
56
57using google_breakpad::KeyValueEntry;
58using google_breakpad::MachPortSender;
59using google_breakpad::MachReceiveMessage;
60using google_breakpad::MachSendMessage;
61using google_breakpad::ReceivePort;
62using google_breakpad::SimpleStringDictionary;
63using google_breakpad::SimpleStringDictionaryIterator;
64
65//=============================================================================
66// We want any memory allocations which are used by breakpad during the
67// exception handling process (after a crash has happened) to be read-only
68// to prevent them from being smashed before a crash occurs.  Unfortunately
69// we cannot protect against smashes to our exception handling thread's
70// stack.
71//
72// NOTE: Any memory allocations which are not used during the exception
73// handling process may be allocated in the normal ways.
74//
75// The ProtectedMemoryAllocator class provides an Allocate() method which
76// we'll using in conjunction with placement operator new() to control
77// allocation of C++ objects.  Note that we don't use operator delete()
78// but instead call the objects destructor directly:  object->~ClassName();
79//
80ProtectedMemoryAllocator *gMasterAllocator = NULL;
81ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
82ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
83
84// Mutex for thread-safe access to the key/value dictionary used by breakpad.
85// It's a global instead of an instance variable of Breakpad
86// since it can't live in a protected memory area.
87pthread_mutex_t gDictionaryMutex;
88
89//=============================================================================
90// Stack-based object for thread-safe access to a memory-protected region.
91// It's assumed that normally the memory block (allocated by the allocator)
92// is protected (read-only).  Creating a stack-based instance of
93// ProtectedMemoryLocker will unprotect this block after taking the lock.
94// Its destructor will first re-protect the memory then release the lock.
95class ProtectedMemoryLocker {
96public:
97  // allocator may be NULL, in which case no Protect() or Unprotect() calls
98  // will be made, but a lock will still be taken
99  ProtectedMemoryLocker(pthread_mutex_t *mutex,
100                        ProtectedMemoryAllocator *allocator)
101  : mutex_(mutex), allocator_(allocator) {
102    // Lock the mutex
103    assert(pthread_mutex_lock(mutex_) == 0);
104
105    // Unprotect the memory
106    if (allocator_ ) {
107      allocator_->Unprotect();
108    }
109  }
110
111  ~ProtectedMemoryLocker() {
112    // First protect the memory
113    if (allocator_) {
114      allocator_->Protect();
115    }
116
117    // Then unlock the mutex
118    assert(pthread_mutex_unlock(mutex_) == 0);
119  };
120
121private:
122  //  Keep anybody from ever creating one of these things not on the stack.
123  ProtectedMemoryLocker() { }
124  ProtectedMemoryLocker(const ProtectedMemoryLocker&);
125  ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
126
127  pthread_mutex_t           *mutex_;
128  ProtectedMemoryAllocator  *allocator_;
129};
130
131//=============================================================================
132class Breakpad {
133 public:
134  // factory method
135  static Breakpad *Create(NSDictionary *parameters) {
136    // Allocate from our special allocation pool
137    Breakpad *breakpad =
138      new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
139        Breakpad();
140
141    if (!breakpad)
142      return NULL;
143
144    if (!breakpad->Initialize(parameters)) {
145      // Don't use operator delete() here since we allocated from special pool
146      breakpad->~Breakpad();
147      return NULL;
148    }
149
150    return breakpad;
151  }
152
153  ~Breakpad();
154
155  void SetKeyValue(NSString *key, NSString *value);
156  NSString *KeyValue(NSString *key);
157  void RemoveKeyValue(NSString *key);
158
159  void GenerateAndSendReport();
160
161  void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
162    filter_callback_ = callback;
163    filter_callback_context_ = context;
164  }
165
166 private:
167  Breakpad()
168    : handler_(NULL),
169      config_params_(NULL),
170      send_and_exit_(true),
171      filter_callback_(NULL),
172      filter_callback_context_(NULL) {
173    inspector_path_[0] = 0;
174  }
175
176  bool Initialize(NSDictionary *parameters);
177
178  bool ExtractParameters(NSDictionary *parameters);
179
180  // Dispatches to HandleException()
181  static bool ExceptionHandlerDirectCallback(void *context,
182                                             int exception_type,
183                                             int exception_code,
184                                             int exception_subcode,
185                                             mach_port_t crashing_thread);
186
187  bool HandleException(int exception_type,
188                       int exception_code,
189                       int exception_subcode,
190                       mach_port_t crashing_thread);
191
192  // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
193  // MachineExceptions.h, we have to explicitly name the handler.
194  google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
195
196  char                    inspector_path_[PATH_MAX];  // Path to inspector tool
197
198  SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
199
200  OnDemandServer          inspector_;
201
202  bool                    send_and_exit_;  // Exit after sending, if true
203
204  BreakpadFilterCallback  filter_callback_;
205  void                    *filter_callback_context_;
206};
207
208#pragma mark -
209#pragma mark Helper functions
210
211//=============================================================================
212// Helper functions
213
214//=============================================================================
215static BOOL IsDebuggerActive() {
216  BOOL result = NO;
217  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
218
219  // We check both defaults and the environment variable here
220
221  BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
222
223  if (!ignoreDebugger) {
224    char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
225    ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
226  }
227
228  if (!ignoreDebugger) {
229    pid_t pid = getpid();
230    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
231    int mibSize = sizeof(mib) / sizeof(int);
232    size_t actualSize;
233
234    if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
235      struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
236
237      if (info) {
238        // This comes from looking at the Darwin xnu Kernel
239        if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
240          result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
241
242        free(info);
243      }
244    }
245  }
246
247  return result;
248}
249
250//=============================================================================
251bool Breakpad::ExceptionHandlerDirectCallback(void *context,
252                                                    int exception_type,
253                                                    int exception_code,
254                                                    int exception_subcode,
255                                                    mach_port_t crashing_thread) {
256  Breakpad *breakpad = (Breakpad *)context;
257
258  // If our context is damaged or something, just return false to indicate that
259  // the handler should continue without us.
260  if (!breakpad)
261    return false;
262
263  return breakpad->HandleException( exception_type,
264                                    exception_code,
265                                    exception_subcode,
266                                    crashing_thread);
267}
268
269//=============================================================================
270#pragma mark -
271
272#include <dlfcn.h>
273
274//=============================================================================
275// Returns the pathname to the Resources directory for this version of
276// Breakpad which we are now running.
277//
278// Don't make the function static, since _dyld_lookup_and_bind_fully needs a
279// simple non-static C name
280//
281extern "C" {
282NSString * GetResourcePath();
283NSString * GetResourcePath() {
284  NSString *resourcePath = nil;
285
286  // If there are multiple breakpads installed then calling bundleWithIdentifier
287  // will not work properly, so only use that as a backup plan.
288  // We want to find the bundle containing the code where this function lives
289  // and work from there
290  //
291
292  // Get the pathname to the code which contains this function
293  Dl_info info;
294  if (dladdr((const void*)GetResourcePath, &info) != 0) {
295    NSFileManager *filemgr = [NSFileManager defaultManager];
296    NSString *filePath =
297        [filemgr stringWithFileSystemRepresentation:info.dli_fname
298                                             length:strlen(info.dli_fname)];
299    NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
300    // The "Resources" directory should be in the same directory as the
301    // executable code, since that's how the Breakpad framework is built.
302    resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
303  } else {
304    DEBUGLOG(stderr, "Could not find GetResourcePath\n");
305    // fallback plan
306    NSBundle *bundle =
307        [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
308    resourcePath = [bundle resourcePath];
309  }
310
311  return resourcePath;
312}
313}  // extern "C"
314
315//=============================================================================
316bool Breakpad::Initialize(NSDictionary *parameters) {
317  // Initialize
318  config_params_ = NULL;
319  handler_ = NULL;
320
321  // Check for debugger
322  if (IsDebuggerActive()) {
323    DEBUGLOG(stderr, "Debugger is active:  Not installing handler\n");
324    return true;
325  }
326
327  // Gather any user specified parameters
328  if (!ExtractParameters(parameters)) {
329    return false;
330  }
331
332  // Get path to Inspector executable.
333  NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
334
335  // Standardize path (resolve symlinkes, etc.)  and escape spaces
336  inspectorPathString = [inspectorPathString stringByStandardizingPath];
337  inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
338                                              componentsJoinedByString:@"\\ "];
339
340  // Create an on-demand server object representing the Inspector.
341  // In case of a crash, we simply need to call the LaunchOnDemand()
342  // method on it, then send a mach message to its service port.
343  // It will then launch and perform a process inspection of our crashed state.
344  // See the HandleException() method for the details.
345#define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
346
347  name_t portName;
348  snprintf(portName, sizeof(name_t),  "%s%d", RECEIVE_PORT_NAME, getpid());
349
350  // Save the location of the Inspector
351  strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
352          sizeof(inspector_path_));
353
354  // Append a single command-line argument to the Inspector path
355  // representing the bootstrap name of the launch-on-demand receive port.
356  // When the Inspector is launched, it can use this to lookup the port
357  // by calling bootstrap_check_in().
358  strlcat(inspector_path_, " ", sizeof(inspector_path_));
359  strlcat(inspector_path_, portName, sizeof(inspector_path_));
360
361  kern_return_t kr = inspector_.Initialize(inspector_path_,
362                                           portName,
363                                           true);        // shutdown on exit
364
365  if (kr != KERN_SUCCESS) {
366    return false;
367  }
368
369  // Create the handler (allocating it in our special protected pool)
370  handler_ =
371      new (gBreakpadAllocator->Allocate(
372          sizeof(google_breakpad::ExceptionHandler)))
373          google_breakpad::ExceptionHandler(
374              Breakpad::ExceptionHandlerDirectCallback, this, true);
375  return true;
376}
377
378//=============================================================================
379Breakpad::~Breakpad() {
380  // Note that we don't use operator delete() on these pointers,
381  // since they were allocated by ProtectedMemoryAllocator objects.
382  //
383  if (config_params_) {
384    config_params_->~SimpleStringDictionary();
385  }
386
387  if (handler_)
388    handler_->~ExceptionHandler();
389}
390
391//=============================================================================
392bool Breakpad::ExtractParameters(NSDictionary *parameters) {
393  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
394  NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
395  NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
396
397  NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
398  NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
399  NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
400  NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
401  NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
402  NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
403  NSString *inspectorPathString =
404      [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
405  NSString *reporterPathString =
406      [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
407  NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
408  NSArray  *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
409  NSString *logFileTailSize =
410      [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
411  NSString *requestUserText =
412      [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
413  NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
414  NSString *vendor =
415      [parameters objectForKey:@BREAKPAD_VENDOR];
416  NSString *dumpSubdirectory =
417      [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
418
419  NSDictionary *serverParameters =
420      [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
421
422  // These may have been set above as user prefs, which take priority.
423  if (!skipConfirm) {
424    skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
425  }
426  if (!sendAndExit) {
427    sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
428  }
429
430  if (!product)
431    product = [parameters objectForKey:@"CFBundleName"];
432
433  if (!display) {
434    display = [parameters objectForKey:@"CFBundleDisplayName"];
435    if (!display) {
436      display = product;
437    }
438  }
439
440  if (!version)
441    version = [parameters objectForKey:@"CFBundleVersion"];
442
443  if (!interval)
444    interval = @"3600";
445
446  if (!timeout)
447    timeout = @"300";
448
449  if (!logFileTailSize)
450    logFileTailSize = @"200000";
451
452  if (!vendor) {
453    vendor = @"Vendor not specified";
454  }
455
456  // Normalize the values.
457  if (skipConfirm) {
458    skipConfirm = [skipConfirm uppercaseString];
459
460    if ([skipConfirm isEqualToString:@"YES"] ||
461        [skipConfirm isEqualToString:@"TRUE"] ||
462        [skipConfirm isEqualToString:@"1"])
463      skipConfirm = @"YES";
464    else
465      skipConfirm = @"NO";
466  } else {
467    skipConfirm = @"NO";
468  }
469
470  send_and_exit_ = true;
471  if (sendAndExit) {
472    sendAndExit = [sendAndExit uppercaseString];
473
474    if ([sendAndExit isEqualToString:@"NO"] ||
475        [sendAndExit isEqualToString:@"FALSE"] ||
476        [sendAndExit isEqualToString:@"0"])
477      send_and_exit_ = false;
478  }
479
480  if (requestUserText) {
481    requestUserText = [requestUserText uppercaseString];
482
483    if ([requestUserText isEqualToString:@"YES"] ||
484        [requestUserText isEqualToString:@"TRUE"] ||
485        [requestUserText isEqualToString:@"1"])
486      requestUserText = @"YES";
487    else
488      requestUserText = @"NO";
489  } else {
490    requestUserText = @"NO";
491  }
492
493  // Find the helper applications if not specified in user config.
494  NSString *resourcePath = nil;
495  if (!inspectorPathString || !reporterPathString) {
496    resourcePath = GetResourcePath();
497    if (!resourcePath) {
498      DEBUGLOG(stderr, "Could not get resource path\n");
499      return false;
500    }
501  }
502
503  // Find Inspector.
504  if (!inspectorPathString) {
505    inspectorPathString =
506        [resourcePath stringByAppendingPathComponent:@"Inspector"];
507  }
508
509  // Verify that there is an Inspector tool.
510  if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
511    DEBUGLOG(stderr, "Cannot find Inspector tool\n");
512    return false;
513  }
514
515  // Find Reporter.
516  if (!reporterPathString) {
517    reporterPathString =
518        [resourcePath
519         stringByAppendingPathComponent:@"crash_report_sender.app"];
520    reporterPathString =
521        [[NSBundle bundleWithPath:reporterPathString] executablePath];
522  }
523
524  // Verify that there is a Reporter application.
525  if (![[NSFileManager defaultManager]
526             fileExistsAtPath:reporterPathString]) {
527    DEBUGLOG(stderr, "Cannot find Reporter tool\n");
528    return false;
529  }
530
531  if (!dumpSubdirectory) {
532    dumpSubdirectory = @"";
533  }
534
535  // The product, version, and URL are required values.
536  if (![product length]) {
537    DEBUGLOG(stderr, "Missing required product key.\n");
538    return false;
539  }
540
541  if (![version length]) {
542    DEBUGLOG(stderr, "Missing required version key.\n");
543    return false;
544  }
545
546  if (![urlStr length]) {
547    DEBUGLOG(stderr, "Missing required URL key.\n");
548    return false;
549  }
550
551  config_params_ =
552      new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
553        SimpleStringDictionary();
554
555  SimpleStringDictionary &dictionary = *config_params_;
556
557  dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
558  dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
559  dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
560  dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
561  dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
562  dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
563  dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM,    [skipConfirm UTF8String]);
564  dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
565  dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
566                         [inspectorPathString fileSystemRepresentation]);
567  dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
568                         [reporterPathString fileSystemRepresentation]);
569  dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
570                         [logFileTailSize UTF8String]);
571  dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
572                         [requestUserText UTF8String]);
573  dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
574  dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
575  dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
576                         [dumpSubdirectory UTF8String]);
577
578  struct timeval tv;
579  gettimeofday(&tv, NULL);
580  char timeStartedString[32];
581  sprintf(timeStartedString, "%zd", tv.tv_sec);
582  dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
583                         timeStartedString);
584
585  if (logFilePaths) {
586    char logFileKey[255];
587    for(unsigned int i = 0; i < [logFilePaths count]; i++) {
588      sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
589      dictionary.SetKeyValue(logFileKey,
590                             [[logFilePaths objectAtIndex:i]
591                               fileSystemRepresentation]);
592    }
593  }
594
595  if (serverParameters) {
596    // For each key-value pair, call BreakpadAddUploadParameter()
597    NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
598    NSString *aParameter;
599    while ((aParameter = [keyEnumerator nextObject])) {
600      BreakpadAddUploadParameter(this, aParameter,
601				 [serverParameters objectForKey:aParameter]);
602    }
603  }
604  return true;
605}
606
607//=============================================================================
608void Breakpad::SetKeyValue(NSString *key, NSString *value) {
609  // We allow nil values. This is the same as removing the keyvalue.
610  if (!config_params_ || !key)
611    return;
612
613  config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
614}
615
616//=============================================================================
617NSString *Breakpad::KeyValue(NSString *key) {
618  if (!config_params_ || !key)
619    return nil;
620
621  const char *value = config_params_->GetValueForKey([key UTF8String]);
622  return value ? [NSString stringWithUTF8String:value] : nil;
623}
624
625//=============================================================================
626void Breakpad::RemoveKeyValue(NSString *key) {
627  if (!config_params_ || !key) return;
628
629  config_params_->RemoveKey([key UTF8String]);
630}
631
632//=============================================================================
633void Breakpad::GenerateAndSendReport() {
634  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
635  HandleException(0, 0, 0, mach_thread_self());
636  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
637}
638
639//=============================================================================
640bool Breakpad::HandleException(int exception_type,
641                               int exception_code,
642                               int exception_subcode,
643                               mach_port_t crashing_thread) {
644  DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
645
646  if (filter_callback_) {
647    bool should_handle = filter_callback_(exception_type,
648                                          exception_code,
649                                          crashing_thread,
650                                          filter_callback_context_);
651    if (!should_handle) return false;
652  }
653
654  // We need to reset the memory protections to be read/write,
655  // since LaunchOnDemand() requires changing state.
656  gBreakpadAllocator->Unprotect();
657  // Configure the server to launch when we message the service port.
658  // The reason we do this here, rather than at startup, is that we
659  // can leak a bootstrap service entry if this method is called and
660  // there never ends up being a crash.
661  inspector_.LaunchOnDemand();
662  gBreakpadAllocator->Protect();
663
664  // The Inspector should send a message to this port to verify it
665  // received our information and has finished the inspection.
666  ReceivePort acknowledge_port;
667
668  // Send initial information to the Inspector.
669  MachSendMessage message(kMsgType_InspectorInitialInfo);
670  message.AddDescriptor(mach_task_self());          // our task
671  message.AddDescriptor(crashing_thread);           // crashing thread
672  message.AddDescriptor(mach_thread_self());        // exception-handling thread
673  message.AddDescriptor(acknowledge_port.GetPort());// message receive port
674
675  InspectorInfo info;
676  info.exception_type = exception_type;
677  info.exception_code = exception_code;
678  info.exception_subcode = exception_subcode;
679  info.parameter_count = config_params_->GetCount();
680  message.SetData(&info, sizeof(info));
681
682  MachPortSender sender(inspector_.GetServicePort());
683
684  kern_return_t result = sender.SendMessage(message, 2000);
685
686  if (result == KERN_SUCCESS) {
687    // Now, send a series of key-value pairs to the Inspector.
688    const KeyValueEntry *entry = NULL;
689    SimpleStringDictionaryIterator iter(*config_params_);
690
691    while ( (entry = iter.Next()) ) {
692      KeyValueMessageData keyvalue_data(*entry);
693
694      MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
695      keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
696
697      result = sender.SendMessage(keyvalue_message, 2000);
698
699      if (result != KERN_SUCCESS) {
700        break;
701      }
702    }
703
704    if (result == KERN_SUCCESS) {
705      // Wait for acknowledgement that the inspection has finished.
706      MachReceiveMessage acknowledge_messsage;
707      result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000);
708    }
709  }
710
711#if VERBOSE
712  PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
713  printf("Breakpad: Inspector service port = %#x\n",
714    inspector_.GetServicePort());
715#endif
716
717  // If we don't want any forwarding, return true here to indicate that we've
718  // processed things as much as we want.
719  if (send_and_exit_) return true;
720
721  return false;
722}
723
724//=============================================================================
725//=============================================================================
726
727#pragma mark -
728#pragma mark Public API
729
730//=============================================================================
731BreakpadRef BreakpadCreate(NSDictionary *parameters) {
732  try {
733    // This is confusing.  Our two main allocators for breakpad memory are:
734    //    - gKeyValueAllocator for the key/value memory
735    //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
736    //      breakpad allocations which are accessed at exception handling time.
737    //
738    // But in order to avoid these two allocators themselves from being smashed,
739    // we'll protect them as well by allocating them with gMasterAllocator.
740    //
741    // gMasterAllocator itself will NOT be protected, but this doesn't matter,
742    // since once it does its allocations and locks the memory, smashes to itself
743    // don't affect anything we care about.
744    gMasterAllocator =
745        new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
746
747    gKeyValueAllocator =
748        new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
749            ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
750
751    // Create a mutex for use in accessing the SimpleStringDictionary
752    int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
753    if (mutexResult == 0) {
754
755      // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
756      // Let's round up to the nearest page size.
757      //
758      int breakpad_pool_size = 4096;
759
760      /*
761       sizeof(Breakpad)
762       + sizeof(google_breakpad::ExceptionHandler)
763       + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
764       */
765
766      gBreakpadAllocator =
767          new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
768              ProtectedMemoryAllocator(breakpad_pool_size);
769
770      // Stack-based autorelease pool for Breakpad::Create() obj-c code.
771      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
772      Breakpad *breakpad = Breakpad::Create(parameters);
773
774      if (breakpad) {
775        // Make read-only to protect against memory smashers
776        gMasterAllocator->Protect();
777        gKeyValueAllocator->Protect();
778        gBreakpadAllocator->Protect();
779        // Can uncomment this line to figure out how much space was actually
780        // allocated using this allocator
781        //     printf("gBreakpadAllocator allocated size = %d\n",
782        //         gBreakpadAllocator->GetAllocatedSize() );
783        [pool release];
784        return (BreakpadRef)breakpad;
785      }
786
787      [pool release];
788    }
789  } catch(...) {    // don't let exceptions leave this C API
790    fprintf(stderr, "BreakpadCreate() : error\n");
791  }
792
793  if (gKeyValueAllocator) {
794    gKeyValueAllocator->~ProtectedMemoryAllocator();
795    gKeyValueAllocator = NULL;
796  }
797
798  if (gBreakpadAllocator) {
799    gBreakpadAllocator->~ProtectedMemoryAllocator();
800    gBreakpadAllocator = NULL;
801  }
802
803  delete gMasterAllocator;
804  gMasterAllocator = NULL;
805
806  return NULL;
807}
808
809//=============================================================================
810void BreakpadRelease(BreakpadRef ref) {
811  try {
812    Breakpad *breakpad = (Breakpad *)ref;
813
814    if (gMasterAllocator) {
815      gMasterAllocator->Unprotect();
816      gKeyValueAllocator->Unprotect();
817      gBreakpadAllocator->Unprotect();
818
819      breakpad->~Breakpad();
820
821      // Unfortunately, it's not possible to deallocate this stuff
822      // because the exception handling thread is still finishing up
823      // asynchronously at this point...  OK, it could be done with
824      // locks, etc.  But since BreakpadRelease() should usually only
825      // be called right before the process exits, it's not worth
826      // deallocating this stuff.
827#if 0
828      gKeyValueAllocator->~ProtectedMemoryAllocator();
829      gBreakpadAllocator->~ProtectedMemoryAllocator();
830      delete gMasterAllocator;
831
832      gMasterAllocator = NULL;
833      gKeyValueAllocator = NULL;
834      gBreakpadAllocator = NULL;
835#endif
836
837      pthread_mutex_destroy(&gDictionaryMutex);
838    }
839  } catch(...) {    // don't let exceptions leave this C API
840    fprintf(stderr, "BreakpadRelease() : error\n");
841  }
842}
843
844//=============================================================================
845void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
846  try {
847    // Not called at exception time
848    Breakpad *breakpad = (Breakpad *)ref;
849
850    if (breakpad && key && gKeyValueAllocator) {
851      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
852
853      breakpad->SetKeyValue(key, value);
854    }
855  } catch(...) {    // don't let exceptions leave this C API
856    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
857  }
858}
859
860void BreakpadAddUploadParameter(BreakpadRef ref,
861                                NSString *key,
862                                NSString *value) {
863  // The only difference, internally, between an upload parameter and
864  // a key value one that is set with BreakpadSetKeyValue is that we
865  // prepend the keyname with a special prefix.  This informs the
866  // crash sender that the parameter should be sent along with the
867  // POST of the crash dump upload.
868  try {
869    Breakpad *breakpad = (Breakpad *)ref;
870
871    if (breakpad && key && gKeyValueAllocator) {
872      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
873
874      NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
875				stringByAppendingString:key];
876      breakpad->SetKeyValue(prefixedKey, value);
877    }
878  } catch(...) {    // don't let exceptions leave this C API
879    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
880  }
881}
882
883void BreakpadRemoveUploadParameter(BreakpadRef ref,
884                                   NSString *key) {
885  try {
886    // Not called at exception time
887    Breakpad *breakpad = (Breakpad *)ref;
888
889    if (breakpad && key && gKeyValueAllocator) {
890      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
891
892      NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
893                                        @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
894      breakpad->RemoveKeyValue(prefixedKey);
895    }
896  } catch(...) {    // don't let exceptions leave this C API
897    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
898  }
899}
900//=============================================================================
901NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
902  NSString *value = nil;
903
904  try {
905    // Not called at exception time
906    Breakpad *breakpad = (Breakpad *)ref;
907
908    if (!breakpad || !key || !gKeyValueAllocator)
909      return nil;
910
911    ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
912
913    value = breakpad->KeyValue(key);
914  } catch(...) {    // don't let exceptions leave this C API
915    fprintf(stderr, "BreakpadKeyValue() : error\n");
916  }
917
918  return value;
919}
920
921//=============================================================================
922void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
923  try {
924    // Not called at exception time
925    Breakpad *breakpad = (Breakpad *)ref;
926
927    if (breakpad && key && gKeyValueAllocator) {
928      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
929
930      breakpad->RemoveKeyValue(key);
931    }
932  } catch(...) {    // don't let exceptions leave this C API
933    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
934  }
935}
936
937//=============================================================================
938void BreakpadGenerateAndSendReport(BreakpadRef ref) {
939  try {
940    Breakpad *breakpad = (Breakpad *)ref;
941
942    if (breakpad && gKeyValueAllocator) {
943      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
944
945      gBreakpadAllocator->Unprotect();
946      breakpad->GenerateAndSendReport();
947      gBreakpadAllocator->Protect();
948    }
949  } catch(...) {    // don't let exceptions leave this C API
950    fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
951  }
952}
953
954//=============================================================================
955void BreakpadSetFilterCallback(BreakpadRef ref,
956                               BreakpadFilterCallback callback,
957                               void *context) {
958
959  try {
960    Breakpad *breakpad = (Breakpad *)ref;
961
962    if (breakpad && gBreakpadAllocator) {
963      // share the dictionary mutex here (we really don't need a mutex)
964      ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
965
966      breakpad->SetFilterCallback(callback, context);
967    }
968  } catch(...) {    // don't let exceptions leave this C API
969    fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
970  }
971}
972
973//============================================================================
974void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
975  int logFileCounter = 0;
976
977  NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
978                                   @BREAKPAD_LOGFILE_KEY_PREFIX,
979                                   logFileCounter];
980
981  NSString *existingLogFilename = nil;
982  existingLogFilename = BreakpadKeyValue(ref, logFileKey);
983  // Find the first log file key that we can use by testing for existence
984  while (existingLogFilename) {
985    if ([existingLogFilename isEqualToString:logPathname]) {
986      return;
987    }
988    logFileCounter++;
989    logFileKey = [NSString stringWithFormat:@"%@%d",
990                           @BREAKPAD_LOGFILE_KEY_PREFIX,
991                           logFileCounter];
992    existingLogFilename = BreakpadKeyValue(ref, logFileKey);
993  }
994
995  BreakpadSetKeyValue(ref, logFileKey, logPathname);
996}
997