0

I'm getting the following crash report for my macOS launch daemon when it exits:

(An abridged version):

Process:               MyService [30028]
Path:                  /Library/PrivilegedHelperTools/*/MyService
Identifier:            MyService
Version:               ???
Code Type:             ARM-64 (Native)
Parent Process:        launchd [1]
User ID:               0

Date/Time:             2023-05-21 01:38:16.2607 +0300
OS Version:            macOS 13.3.1 (22E772610a)
Report Version:        12
Anonymous UUID:        

Sleep/Wake UUID:       

Time Awake Since Boot: 170000 seconds
Time Since Wake:       5497 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Application Specific Information:
abort() called


Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib                 0x18fb0c724 __pthread_kill + 8
1   libsystem_pthread.dylib                0x18fb43c28 pthread_kill + 288
2   libsystem_c.dylib                      0x18fa51b74 __abort + 128
3   libsystem_c.dylib                      0x18fa51af4 abort + 192
4   libclang_rt.asan_osx_dynamic.dylib         0x1049421bc __sanitizer::Abort() + 68
5   libclang_rt.asan_osx_dynamic.dylib         0x104941884 __sanitizer::Die() + 212
6   libclang_rt.asan_osx_dynamic.dylib         0x1049271c0 __asan::ScopedInErrorReport::~ScopedInErrorReport() + 1124
7   libclang_rt.asan_osx_dynamic.dylib         0x104926498 __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) + 1436
8   libclang_rt.asan_osx_dynamic.dylib         0x1048fd558 printf_common(void*, char const*, char*) + 1400
9   libclang_rt.asan_osx_dynamic.dylib         0x1048fd980 wrap_vsnprintf + 176
10  libclang_rt.asan_osx_dynamic.dylib         0x1048fe1b8 wrap_snprintf + 84
11  MyService                              0x104222ce4 P1Timer::_cancelEvents(char const*, char const*, unsigned long&) + 780 (P1Timer.h:696)
12  MyService                              0x10417edfc P1Timer::stopP1Event() + 912 (P1Timer.h:151)
13  MyService                              0x1041b1f64 MySvc::setOrRemoveP1Timer() + 368 (MySvc.cpp:8355)
14  MyService                              0x1041b0ea0 OS_P1_STATE::_beginStopIdleState(bool) + 2132 (MySvc.cpp:5104)
15  MyService                              0x1041815e8 OS_P1_STATE::restore_IdleState() + 756 (MySvc.cpp:5226)
16  MyService                              0x1041e1544 OS_P1_STATE::~OS_P1_STATE() + 96 (OSSleepState.h:34)
17  MyService                              0x10416129c OS_P1_STATE::~OS_P1_STATE() + 84 (OSSleepState.h:32)
18  MyService                              0x104161908 MySvc::~MySvc() + 228 (MySvc.cpp:160)
19  MyService                              0x104161a84 MySvc::~MySvc() + 84 (MySvc.cpp:156)
20  MyService                              0x104289504 main + 400 (main.cpp:48)
21  dyld                                   0x18f7ebf28 start + 2236

I'm checking what is at the P1Timer.h:696 line, and there's just this call:

    //const char* pstrBundleID
    //const char* pstrEventType

    std::string strDbgDesc;
    MyFormat(strDbgDesc,                              //P1Timer.h:696
             "budleID=\"%s\", EventType=\"%s\""
             ,
             pstrBundleID ? pstrBundleID : "<null>",
             pstrEventType ? pstrEventType : "<null>");

The pstrBundleID and pstrEventType come from a call like so:

_cancelEvents(_strTmrBundleID.c_str(), NULL, szCnt);

where _strTmrBundleID is of type std::string that is set only in the constructor.

And finally:

std::string& MyFormat(std::string& str, const char *pFmt, ...)
{
    va_list args;
    va_start (args, pFmt);

    int nchLen = vsnprintf(nullptr, 0, pFmt, args);
    str.resize(nchLen);

    vsnprintf(&str[0], nchLen + 1, pFmt, args);

    va_end(args);
    
    return str;
}

I am trying to understand what could've caused this crash. And namely, where does __asan::ReportGenericError report to? I want to see what exactly it didn't like.

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • To reuse a `va_list` multiple times, you need to use `va_copy`. – user17732522 May 21 '23 at 08:20
  • Asan normally just prints to the console – Alan Birtles May 21 '23 at 08:44
  • @user17732522 all that has nothing to do with it. You don't need `va_copy` to invoke `vsnprintf` twice. And overwriting last 0 in std::string with 0 won't do anything bad. It's just an array of bytes and there's nothing special about hat last 0. Overwriting it with something other than 0 is indeed bad. – c00000fd May 21 '23 at 11:09
  • @AlanBirtles: it turned out to be use-after-free case. Very cool. The class member was invoked after its destructor. I like that Address Sanitizer. I wish it put that info into the crash dump though. I was able to catch it with a debugger, but it would've been a much more difficult task if all I had was a crash dump. – c00000fd May 21 '23 at 11:11
  • @c00000fd Sorry, I'll take that second one back. I misremembered writing null not being allowed. However, reusing `va_list` without `va_copy` still only works on some implementations. – user17732522 May 21 '23 at 11:55

0 Answers0