Bug 410596 - Crash when importing alarms
Summary: Crash when importing alarms
Status: RESOLVED FIXED
Alias: None
Product: kalarm
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: Other Linux
: NOR normal
Target Milestone: ---
Assignee: David Jarvie
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-08-05 00:23 UTC by Jiri Palecek
Modified: 2019-08-14 23:07 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In: 19.08.1


Attachments
proposed patch (929 bytes, patch)
2019-08-09 02:07 UTC, Jiri Palecek
Details
This patch looks better (928 bytes, patch)
2019-08-09 02:47 UTC, Jiri Palecek
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jiri Palecek 2019-08-05 00:23:29 UTC
By some (to me unknown) action probably connected to copying my disk drive to another one I lost several of my alarms in KAlarm. When I clicked some of the items in the left pane of the main window (calendars), the alarms came back, but I got a crash. Backtrace is:

(gdb) bt
#0  0xb7f1a881 in __kernel_vsyscall ()
#1  0xb4c129eb in __GI___nanosleep (remaining=0xbfa8ee64, requested_time=0xbfa8ee64) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
#2  __GI___nanosleep (requested_time=0xbfa8ee64, remaining=0xbfa8ee64) at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#3  0xb4c128fd in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#4  0xb68f93ea in startProcessInternal (argc=argc@entry=21, argv=argv@entry=0xbfa8ef38, waitAndExit=waitAndExit@entry=true, directly=false) at ./src/kcrash.cpp:672
#5  0xb68f9bf3 in KCrash::startProcess (argc=21, argv=0xbfa8ef38, waitAndExit=true) at ./src/kcrash.cpp:637
#6  0xb68fa0fb in KCrash::defaultCrashHandler (sig=11) at ./src/kcrash.cpp:540
#7  <signal handler called>
#8  0xb7b1a136 in KAlarmCal::KAEvent::category() const () from /usr/lib/i386-linux-gnu/libKF5AlarmCalendar.so.5abi1
#9  0x004c5c32 in AlarmCalendar::events (this=0x17ef5a0, collection=..., type=...) at /usr/include/i386-linux-gnu/qt5/QtCore/qarraydata.h:209
#10 0x004c5eab in AlarmCalendar::events (s=..., this=0x17ef5a0) at ./src/alarmcalendar.h:68
#11 AlarmCalendar::checkForDisabledAlarms (this=0x17ef5a0) at ./src/alarmcalendar.cpp:1439
#12 0x004c8a9e in AlarmCalendar::slotEventChanged (this=<optimized out>, event=...) at ./src/alarmcalendar.cpp:567
#13 0x004c8caf in AlarmCalendar::slotEventsAdded (this=0x17ef5a0, events=...) at /usr/include/i386-linux-gnu/qt5/QtCore/qlist.h:544
#14 0xb51ae484 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#15 0xb51ae90d in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#16 0x005b7bf2 in AkonadiModel::eventsAdded (this=0x1796c80, _t1=...) at ./obj-i686-linux-gnu/src/kalarm_bin_autogen/EWIEGA46WW/moc_akonadimodel.cpp:423
#17 0x0057dd5d in AkonadiModel::slotRowsInserted (this=<optimized out>, parent=..., start=<optimized out>, end=<optimized out>) at ./src/akonadimodel.cpp:1634
#18 0xb51ae484 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#19 0xb51ae90d in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#20 0xb51325c9 in QAbstractItemModel::rowsInserted(QModelIndex const&, int, int, QAbstractItemModel::QPrivateSignal) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#21 0xb513a139 in QAbstractItemModel::endInsertRows() () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#22 0xb731390b in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#23 0xb73013b3 in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#24 0xb51ae33b in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#25 0xb51ae90d in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#26 0xb7224b56 in Akonadi::Monitor::itemAdded(Akonadi::Item const&, Akonadi::Collection const&) () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#27 0xb7232a70 in Akonadi::MonitorPrivate::emitItemsNotification(Akonadi::Protocol::ItemChangeNotification const&, QVector<Akonadi::Item> const&, Akonadi::Collection const&, Akonadi::Collection const&) () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#28 0xb7233198 in Akonadi::MonitorPrivate::emitNotification(QSharedPointer<Akonadi::Protocol::ChangeNotification> const&) () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#29 0xb71d8ace in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#30 0xb72351f2 in Akonadi::MonitorPrivate::flushPipeline() () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#31 0xb723556a in Akonadi::MonitorPrivate::dataAvailable() () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#32 0xb72268ad in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#33 0xb51ae33b in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#34 0xb51ae90d in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#35 0xb7207d83 in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#36 0xb71cc6ef in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#37 0xb7207dd7 in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#38 0xb51ae33b in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#39 0xb51ae90d in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#40 0xb656b041 in KJob::result(KJob*, KJob::QPrivateSignal) () from /usr/lib/i386-linux-gnu/libKF5CoreAddons.so.5
#41 0xb656bc3c in KJob::finishJob(bool) () from /usr/lib/i386-linux-gnu/libKF5CoreAddons.so.5
#42 0xb656dcea in KJob::emitResult() () from /usr/lib/i386-linux-gnu/libKF5CoreAddons.so.5
#43 0xb72b7d98 in ?? () from /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5abi2
#44 0xb51bad44 in ?? () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#45 0xb51aeb8e in QObject::event(QEvent*) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#46 0xb5bd50c6 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#47 0xb5bdcd79 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#48 0xb518260a in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#49 0xb51d8d9e in QTimerInfoList::activateTimers() () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#50 0xb51d985c in ?? () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#51 0xb346310d in g_main_context_dispatch () from /usr/lib/i386-linux-gnu/libglib-2.0.so.0
#52 0xb34633c9 in ?? () from /usr/lib/i386-linux-gnu/libglib-2.0.so.0
#53 0xb3463474 in g_main_context_iteration () from /usr/lib/i386-linux-gnu/libglib-2.0.so.0
#54 0xb51d9c1d in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#55 0xaf9e3ca3 in ?? () from /usr/lib/i386-linux-gnu/libQt5XcbQpa.so.5
#56 0xb5181007 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#57 0xb51897ee in QCoreApplication::exec() () from /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5
#58 0xb55cdda1 in QGuiApplication::exec() () from /usr/lib/i386-linux-gnu/sse2/libQt5Gui.so.5
#59 0xb5bd5034 in QApplication::exec() () from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#60 0x00489aa6 in main (argc=<optimized out>, argv=<optimized out>) at ./src/main.cpp:78

It seems that in AlarmCalendar::events(const Akonadi::Collection& collection, CalEvent::Types type) const, events[4] seems invalid although events has length 6. Any ideas?

KAlarm version is 2.12.2

Any ideas?
Comment 1 Jiri Palecek 2019-08-05 00:43:27 UTC
BTW valgrind says this about the problem:

==23380== Invalid read of size 4
==23380==    at 0x4BF3134: KAlarmCal::KAEvent::category() const (kaevent.cpp:1769)
==23380==    by 0x1B0C31: AlarmCalendar::events(Akonadi::Collection const&, QFlags<KAlarmCal::CalEvent::Type>) const (alarmcalendar.cpp:1340)
==23380==    by 0x1B0EAA: events (alarmcalendar.h:68)
==23380==    by 0x1B0EAA: AlarmCalendar::checkForDisabledAlarms() (alarmcalendar.cpp:1439)
==23380==    by 0x1B22F0: AlarmCalendar::removeKAEvents(long long, bool, QFlags<KAlarmCal::CalEvent::Type>) (alarmcalendar.cpp:502)
==23380==    by 0x756C483: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x2A2B91: AkonadiModel::collectionStatusChanged(Akonadi::Collection const&, AkonadiModel::Change, QVariant const&, bool) (moc_akonadimodel.cpp:416)
==23380==    by 0x268546: AkonadiModel::setCollectionChanged(Akonadi::Collection const&, QSet<QByteArray> const&, bool) (akonadimodel.cpp:1707)
==23380==    by 0x2A90E5: slotCollectionChanged (akonadimodel.h:261)
==23380==    by 0x2A90E5: AkonadiModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (moc_akonadimodel.cpp:214)
==23380==    by 0x756C33A: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x5431EB5: Akonadi::Monitor::collectionChanged(Akonadi::Collection const&, QSet<QByteArray> const&) (in /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5.9.3.abi2)
==23380==  Address 0x1081c7b8 is 0 bytes inside a block of size 4 free'd
==23380==    at 0x480BD67: operator delete(void*) (in /usr/lib/i386-linux-gnu/valgrind/vgpreload_memcheck-x86-linux.so)
==23380==    by 0x1B209E: AlarmCalendar::removeKAEvents(long long, bool, QFlags<KAlarmCal::CalEvent::Type>) (alarmcalendar.cpp:485)
==23380==    by 0x756C483: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x2A2B91: AkonadiModel::collectionStatusChanged(Akonadi::Collection const&, AkonadiModel::Change, QVariant const&, bool) (moc_akonadimodel.cpp:416)
==23380==    by 0x268546: AkonadiModel::setCollectionChanged(Akonadi::Collection const&, QSet<QByteArray> const&, bool) (akonadimodel.cpp:1707)
==23380==    by 0x2A90E5: slotCollectionChanged (akonadimodel.h:261)
==23380==    by 0x2A90E5: AkonadiModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (moc_akonadimodel.cpp:214)
==23380==    by 0x756C33A: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x5431EB5: Akonadi::Monitor::collectionChanged(Akonadi::Collection const&, QSet<QByteArray> const&) (in /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5.9.3.abi2)
==23380==    by 0x543A461: Akonadi::MonitorPrivate::emitCollectionNotification(Akonadi::Protocol::CollectionChangeNotification const&, Akonadi::Collection const&, Akonadi::Collection const&, Akonadi::Collection const&) (in /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5.9.3.abi2)
==23380==    by 0x543FD3F: Akonadi::MonitorPrivate::emitNotification(QSharedPointer<Akonadi::Protocol::ChangeNotification> const&) (in /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5.9.3.abi2)
==23380==  Block was alloc'd at
==23380==    at 0x480ACAB: operator new(unsigned int) (in /usr/lib/i386-linux-gnu/valgrind/vgpreload_memcheck-x86-linux.so)
==23380==    by 0x1B3A5A: AlarmCalendar::slotEventChanged(AkonadiModel::Event const&) (alarmcalendar.cpp:564)
==23380==    by 0x1B3CAE: AlarmCalendar::slotEventsAdded(QList<AkonadiModel::Event> const&) (alarmcalendar.cpp:530)
==23380==    by 0x756C483: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x2A2BF1: AkonadiModel::eventsAdded(QList<AkonadiModel::Event> const&) (moc_akonadimodel.cpp:423)
==23380==    by 0x268D5C: AkonadiModel::slotRowsInserted(QModelIndex const&, int, int) (akonadimodel.cpp:1634)
==23380==    by 0x756C483: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x756C90C: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x74F05C8: QAbstractItemModel::rowsInserted(QModelIndex const&, int, int, QAbstractItemModel::QPrivateSignal) (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x74F8138: QAbstractItemModel::endInsertRows() (in /usr/lib/i386-linux-gnu/sse2/libQt5Core.so.5.11.3)
==23380==    by 0x5522231: ??? (in /usr/lib/i386-linux-gnu/libKF5AkonadiCore.so.5.9.3.abi2)
==23380==
Comment 2 David Jarvie 2019-08-08 23:02:44 UTC
Thank you for the good quality bug report.

Even with the information you provided, it isn't clear what went wrong. It's possible (but not certain) that if any events have UIDs which are duplicated between different active alarm calendar files, this may be able to trigger the crash. To help narrow down the cause, could you please check for duplicated event UIDs as follows:

Check all the *active* alarm calendar files which were enabled in the left pane of the KAlarm main window, and also the calendar which you were activating when the crash occurred. To do this, execute the command

grep ^UID: file1 file2 ... | sort | uniq -c | grep -v ^1

(substituting the actual file names, of course)

This should output all event UIDs which are duplicated.

If there are any duplicated UIDs, please check whether any or all of them occur in the calendar which caused the crash, by grepping within that file for the duplicated UIDs.
Comment 3 David Jarvie 2019-08-08 23:05:58 UTC
Oops, the command should be

grep ^UID: file1 file2 ... | sort | uniq -c | grep -v "^ *1 "
Comment 4 David Jarvie 2019-08-08 23:23:16 UTC
The valgrind crash is in fact different from the original crash. The valgrind crash shows that multiple calendar status changes are being received in AlarmCalendar, before the processing of the previous one has completed. The fix for this will be to ensure that these status indications are queued so that they are never processed in parallel.

The original crash only shows one calendar status change, so my previous Comment #2 and Comment #3 are still valid.
Comment 5 Jiri Palecek 2019-08-09 02:05:22 UTC
Hello,

thanks for your effort. Yes, there are duplicate items:

$ grep ^UID ~/.kde/share/apps/kalarm/*.ics | sort -k 3 -t:
/home/jirka/.kde/share/apps/kalarm/calendar.ics:UID:b355410c-07af-4ded-abf3-d887430000a1
/home/jirka/.kde/share/apps/kalarm/displaying.ics:UID:KAlarm-disp-1593075078.221
/home/jirka/.kde/share/apps/kalarm/expired.ics:UID:KAlarm-exp-959849771.278
/home/jirka/.kde/share/apps/kalarm/expired.ics:UID:KAlarm-exp-959849771.278
/home/jirka/.kde/share/apps/kalarm/expired.ics:UID:KAlarm-exp-981216950.140
/home/jirka/.kde/share/apps/kalarm/expired.ics:UID:KAlarm-exp-981216950.140
/home/jirka/.kde/share/apps/kalarm/calendar.ics:UID:KAlarm-1906626020.755
/home/jirka/.kde/share/apps/kalarm/calendar.ics:UID:KAlarm-727370443.645
/home/jirka/.kde/share/apps/kalarm/calendar.ics:UID:6e28d948-c6ba-4127-ac36-8a01b672de95
/home/jirka/.kde/share/apps/kalarm/calendar.ics:UID:9f3cd70a-9e1d-4d08-81ae-8dea00eaa34c

(In reply to David Jarvie from comment #4)
> The valgrind crash is in fact different from the original crash. The

I don't think so; yes, the backtraces (their upper frames) are different, however, I think this isn't important. The immediate place of the crash is the same, and the reason - a dangling pointer - is the same in both traces. I discovered the dangling pointer also in gdb. My hypothesis is, that while the native run goes through the same dangling pointer dereference as the valgrind run, natively it goes through because the memory hasn't been overwritten yet. It just crashes later when the freed memory gets reused.


> valgrind crash shows that multiple calendar status changes are being
> received in AlarmCalendar, before the processing of the previous one has
> completed.

What you mean? I don't see any indication of that from valgrind, IMHO it just crashed inside the same removeKAEvents call that deleted the data.

Given that, I looked at AlarmCalendar::removedKAevents and it really seems fishy. It haphazardly deletes some events, then it randomly removes the collection which might or might not contain the pointers to data which were deleted. It seems this could cause the crash I see.

Anyway, I have prepared a patch that fixes this method, by only leaving valid pointers in mResourceMap. It makes the crash go away (although I got another crash under valgrind, accompanied by the message that connection to akonadi was lost, possibly because execution under valgrind was so slow).

I will post the patch here, although I was planning to send it through phabricator.
Comment 6 Jiri Palecek 2019-08-09 02:07:29 UTC
Created attachment 122027 [details]
proposed patch

Will send it through Phabricator in a few days, just sending so you can comment on it now.
Comment 7 Jiri Palecek 2019-08-09 02:09:40 UTC
Comment on attachment 122027 [details]
proposed patch

Disregard that
Comment 8 Jiri Palecek 2019-08-09 02:47:34 UTC
Created attachment 122029 [details]
This patch looks better
Comment 9 David Jarvie 2019-08-09 11:59:38 UTC
Yes, there is a bug in removeKAEvents, and your patch looks good. I'll wait for you to submit it in Phabricator.

The valgrind trace shows that a calendar added is being processed, and that that is interrupted (while a 'new' is being executed) by the removal of a calendar, and that in turn is interrupted (while a 'delete' is being executed) by another removal of a calendar.

Obviously valgrind makes this more likely to happen since the code runs slower, but it could potentially still happen in normal use. The code in AlarmCalendar is not designed to work in such circumstances (for example, removeKAEvents deletes each KAEvent instance individually before references to them are finally all removed from mResourceMap), so a queuing mechanism is needed.
Comment 10 Jiri Palecek 2019-08-09 14:41:30 UTC
(In reply to David Jarvie from comment #9)
> Yes, there is a bug in removeKAEvents, and your patch looks good. I'll wait
> for you to submit it in Phabricator.

I've posted it.

> The valgrind trace shows that a calendar added is being processed, and that
> that is interrupted (while a 'new' is being executed) by the removal of a
> calendar, and that in turn is interrupted (while a 'delete' is being
> executed) by another removal of a calendar.

I think this is a misunderstanding, the valgrind report for a wrong access contains three traces, one at the time of the access, second one at the time of the block being freed, third one at the time of its allocation. It isn't one backtrace of new interrupted while being executed. It would be very odd if that actually could happen.

I admit it's not entirely legible here. I'll try to use attachments the next time, although it's tricky when you just copy output from terminal.
Comment 11 David Jarvie 2019-08-09 15:23:32 UTC
Ok, that makes sense. Thanks for your Phabricator patch, which I have accepted.
Comment 12 David Jarvie 2019-08-14 09:26:14 UTC
Can you please commit your Phabricator patch so that this bug report can be closed? Thanks.
Comment 13 Jiri Palecek 2019-08-14 18:22:38 UTC
(In reply to David Jarvie from comment #12)
> Can you please commit your Phabricator patch so that this bug report can be
> closed? Thanks.

Hardly, I have no access.
Comment 14 David Jarvie 2019-08-14 23:07:32 UTC
Ok, I've committed it for you. Thanks for the fix.

Commit 1cefdb07c558721dea7a90a2216b61d21425a8d5 to Applications/19.08 branch.