11//
2- // KSCrashMonitor_Memory.h
2+ // KSCrashMonitor_Memory.m
33//
44// Created by Alexander Cohen on 2024-05-20.
55//
5454
5555static const int32_t KSCrash_Memory_Magic = ' kscm' ;
5656
57- static const uint8_t KSCrash_Memory_Version_1 = 1 ;
58- const uint8_t KSCrash_Memory_CurrentVersion = KSCrash_Memory_Version_1 ;
57+ const uint8_t KSCrash_Memory_Version_1_0 = 1 ;
58+ const uint8_t KSCrash_Memory_CurrentVersion = KSCrash_Memory_Version_1_0 ;
5959
6060const uint8_t KSCrash_Memory_NonFatalReportLevelNone = KSCrashAppMemoryStateTerminal + 1 ;
6161
7777static void notifyPostSystemEnable (void );
7878static void ksmemory_read (const char *path);
7979static void ksmemory_map (const char *path);
80+ static void ksmemory_unmap (void );
81+ static void ksmemory_applyNoFileProtection (NSString *path);
8082
8183// ============================================================================
8284#pragma mark - Globals -
@@ -140,12 +142,21 @@ static void _ks_memory_update(void (^block)(KSCrash_Memory *mem))
140142static void _ks_memory_set (KSCrash_Memory *mem)
141143{
142144 os_unfair_lock_lock (&g_memoryLock);
145+ void *old = g_memory;
143146 g_memory = mem;
144147 os_unfair_lock_unlock (&g_memoryLock);
148+
149+ if (old) {
150+ ksfu_munmap (old, sizeof (KSCrash_Memory));
151+ }
145152}
146153
147154static void _ks_memory_update_from_app_memory (KSCrashAppMemory *const memory)
148155{
156+ if (!memory) {
157+ return ;
158+ }
159+
149160 _ks_memory_update (^(KSCrash_Memory *mem) {
150161 *mem = (KSCrash_Memory) {
151162 .magic = KSCrash_Memory_Magic,
@@ -268,9 +279,10 @@ static void setEnabled(bool isEnabled)
268279 }];
269280
270281 } else {
271- g_memoryTracker = nil ;
272282 [KSCrashAppStateTracker.sharedInstance removeObserver: g_appStateObserver];
273283 g_appStateObserver = nil ;
284+ g_memoryTracker = nil ;
285+ ksmemory_unmap ();
274286 }
275287}
276288
@@ -291,14 +303,16 @@ static void addContextualInfoToEvent(KSCrash_MonitorContext *eventContext)
291303 // since we're in a signal or something that can only
292304 // use async safe functions, we can't lock.
293305 // It's "ok" though, since no other threads should be running.
294- g_memory->fatal = eventContext->requirements .isFatal ;
306+ if (g_memory) {
307+ g_memory->fatal = eventContext->requirements .isFatal ;
308+ }
295309 } else {
296310 _ks_memory_update (^(KSCrash_Memory *mem) {
297311 mem->fatal = eventContext->requirements .isFatal ;
298312 });
299313 }
300314
301- if (g_isEnabled) {
315+ if (g_isEnabled && g_memory ) {
302316 // same as above re: not locking when _asyncSafeOnly_ is set.
303317 KSCrash_Memory memCopy = asyncSafeOnly ? *g_memory : _ks_memory_copy ();
304318 eventContext->AppMemory .footprint = memCopy.footprint ;
@@ -337,7 +351,7 @@ static void kscm_memory_check_for_oom_in_previous_session(void)
337351 // a programming error and receiving a Mach event or signal that
338352 // indicates a crash, we should process that on startup and ignore
339353 // and indication of an OOM.
340- bool userPerceivedOOM = NO ;
354+ bool userPerceivedOOM = false ;
341355 if (g_FatalReportsEnabled && ksmemory_previous_session_was_terminated_due_to_memory (&userPerceivedOOM)) {
342356 // We only report an OOM that the user might have seen.
343357 // Ignore this check if we want to report all OOM, foreground and background.
@@ -459,10 +473,10 @@ static void ksmemory_read(const char *path)
459473
460474 // check the timestamp, let's say it's valid for the last week
461475 // do we really want crash reports older than a week anyway??
462- const uint64_t kUS_in_day = 8.64e+10 ;
476+ const uint64_t kUS_in_day = 86400000000ULL ; // 24 * 60 * 60 * 1000000
463477 const uint64_t kUS_in_week = kUS_in_day * 7 ;
464478 uint64_t now = ksdate_microseconds ();
465- if (memory.timestamp <= 0 || memory.timestamp == INT64_MAX || memory.timestamp < now - kUS_in_week ) {
479+ if (memory.timestamp == 0 || memory.timestamp > now || memory.timestamp < now - kUS_in_week ) {
466480 return ;
467481 }
468482
@@ -516,9 +530,18 @@ static void ksmemory_map(const char *path)
516530 }
517531
518532 _ks_memory_set (ptr);
519- _ks_memory_update_from_app_memory (g_memoryTracker.memory );
533+
534+ KSCrashAppMemory *currentMemory = g_memoryTracker.memory ;
535+ if (currentMemory) {
536+ _ks_memory_update_from_app_memory (currentMemory);
537+ }
520538}
521539
540+ /* *
541+ Unmaps the memory-mapped file and clears the global pointer.
542+ */
543+ static void ksmemory_unmap (void ) { _ks_memory_set (NULL ); }
544+
522545/* *
523546 What we're doing here is writing a file out that can be reused
524547 on restart if the data shows us there was a memory issue.
@@ -562,12 +585,27 @@ static void ksmemory_write_possible_oom(void)
562585 g_callbacks.handle (ctx);
563586}
564587
588+ static void ksmemory_applyNoFileProtection (NSString *path)
589+ {
590+ if (!path) {
591+ return ;
592+ }
593+
594+ NSDictionary *attrs = @ { NSFileProtectionKey : NSFileProtectionNone };
595+ [[NSFileManager defaultManager ] setAttributes: attrs ofItemAtPath: path error: nil ];
596+ }
597+
565598void ksmemory_initialize (const char *dataPath)
566599{
567- g_hasPostEnable = 0 ;
600+ g_hasPostEnable = false ;
568601 g_dataURL = [NSURL fileURLWithPath: @(dataPath)];
569602 g_memoryURL = [g_dataURL URLByAppendingPathComponent: @" memory.bin" ];
570603
604+ // Ensure files we touch stay readable while the app is locked.
605+ ksmemory_applyNoFileProtection (g_dataURL.path );
606+ ksmemory_applyNoFileProtection (g_memoryURL.path );
607+ ksmemory_applyNoFileProtection (kscm_memory_oom_breadcrumb_URL ().path );
608+
571609 // load up the old memory data
572610 ksmemory_read (g_memoryURL.path .UTF8String );
573611}
@@ -579,7 +617,7 @@ bool ksmemory_previous_session_was_terminated_due_to_memory(bool *userPerceptibl
579617 // exception/event occured that terminated/crashed the app. We don't want to report
580618 // that as an OOM.
581619 if (g_previousSessionMemory.fatal ) {
582- return NO ;
620+ return false ;
583621 }
584622
585623 // We might care if the user might have seen the OOM
0 commit comments