php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71659 segmentation fault in pcre running twig tests
Submitted: 2016-02-24 20:57 UTC Modified: 2016-03-09 22:17 UTC
From: nish dot aravamudan at canonical dot com Assigned: nikic (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.0.3 OS: Ubuntu 16.04
Private report: No CVE-ID: None
 [2016-02-24 20:57 UTC] nish dot aravamudan at canonical dot com
Description:
------------
Ubuntu 16.04 is moving to PHP7.0 only, and we are hitting the following failures with the twig test-suite:

1) Twig_Tests_IntegrationTest::testIntegration with data set #214 ('filters/last.test', '"last" filter', '', array('\n{{ [1, 2, 3, 4]|last }}\n{{ {...ast }}'), false, array(array('--DATA--\nreturn array('arr'
+=...4\n4\né', '\nreturn array('arr' => new Ar... 4)))\n', '', '\n4\n4\n4\n4\né')))
"last" filter (in filters/last.test)
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 '4
 4
 4
 4
-é'
+�'

/build/twig-0SRlZk/twig-1.23.1/lib/Twig/Test/IntegrationTestCase.php:219
/build/twig-0SRlZk/twig-1.23.1/lib/Twig/Test/IntegrationTestCase.php:62

2) Twig_Tests_IntegrationTest::testIntegration with data set #240 ('filters/first.test', '"first" filter', '', array('\n{{ [1, 2, 3, 4]|first }}\n{{ ...rst }}'), false, array(array('--DATA--\nreturn array('arr'
+=...1\n1\nÄ', '\nreturn array('arr' => new Ar... 4)))\n', '', '\n1\n1\n1\n1\nÄ')))
"first" filter (in filters/first.test)
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 '1
 1
 1
 1
-Ä'
+�'

/build/twig-0SRlZk/twig-1.23.1/lib/Twig/Test/IntegrationTestCase.php:219
/build/twig-0SRlZk/twig-1.23.1/lib/Twig/Test/IntegrationTestCase.php:62

I *believe* the differing character is the result of SUBSTITUTE on the string. That is neither here nor there for this bug report, necessarily, but just context. Ondřej Surý recommended we try and install php-mbstring, but that led to segmentation faults:

#0  __memcpy_avx_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:273
#1  0x00005555556798d8 in memcpy (__len=18446744073709551614,
    __src=0x7fffed43e1fc, __dest=0x7fffed49e390)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:53
#2  zend_string_init (persistent=0, len=18446744073709551614,
    str=0x7fffed43e1fc "\303\237\343\201\224a")
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_string.h:159
#3  php_pcre_split_impl (pce=pce@entry=0x555555d4aea0,
    subject=0x7fffed43e1f8 "\303\251\303\204\303\237\343\201\224a",
    subject_len=10, return_value=return_value@entry=0x7ffff381b240,
    limit_val=-1, flags=<optimized out>)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/ext/pcre/php_pcre.c:1808
#4  0x000055555567a1eb in zif_preg_split (execute_data=<optimized out>,
    return_value=0x7ffff381b240)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/ext/pcre/php_pcre.c:1721
#5  0x000055555579b58a in dtrace_execute_internal (
    execute_data=<optimized out>, return_value=<optimized out>)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:107
#6  0x000055555582f5f0 in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:844
#7  0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff381b070)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#8  0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff381b070)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#9  0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#10 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3819ff0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#11 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3819ff0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#12 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#13 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3819e80)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#14 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3819e80)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#15 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#16 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3819db0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#17 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3819db0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#18 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#19 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3819ca0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#20 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3819ca0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#21 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#22 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff38192e0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#23 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff38192e0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#24 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#25 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3819210)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#26 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3819210)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#27 0x000055555579d03c in zend_call_function (fci=fci@entry=0x7fffffff9ae0,
    fci_cache=fci_cache@entry=0x7fffffff9ab0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_execute_API.c:860
#28 0x000055555569e042 in zim_reflection_method_invokeArgs (
    execute_data=<optimized out>, return_value=0x7ffff3818e60)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/ext/reflection/php_reflection.c:3348
#29 0x000055555579b58a in dtrace_execute_internal (
    execute_data=<optimized out>, return_value=<optimized out>)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:107
#30 0x000055555582f5f0 in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:844
#31 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3818c60)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#32 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3818c60)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#33 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#34 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3818470)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#35 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3818470)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#36 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#37 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3817880)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#38 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3817880)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#39 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#40 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3816e20)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#41 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3816e20)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#42 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#43 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3816840)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#44 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3816840)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#45 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#46 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3816260)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#47 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3816260)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#48 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#49 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3815c80)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#50 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3815c80)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#51 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#52 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3814640)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#53 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3814640)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#54 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#55 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3814220)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#56 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3814220)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#57 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#58 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3814130)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#59 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3814130)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#60 0x000055555582f72d in ZEND_DO_FCALL_SPEC_HANDLER ()
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:800
#61 0x00005555557eaedb in execute_ex (ex=ex@entry=0x7ffff3814030)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:414
#62 0x000055555579b421 in dtrace_execute_ex (execute_data=0x7ffff3814030)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_dtrace.c:83
#63 0x000055555583e2b7 in zend_execute (
    op_array=op_array@entry=0x7ffff3883000,
    return_value=return_value@entry=0x0)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend_vm_execute.h:458
#64 0x00005555557ab6b3 in zend_execute_scripts (type=type@entry=8,
    retval=retval@entry=0x0, file_count=file_count@entry=3)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/Zend/zend.c:1427
#65 0x000055555574c0c0 in php_execute_script (primary_file=0x7fffffffcb10)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/main/main.c:2484
#66 0x000055555583ff84 in do_cli (argc=4, argv=0x555555bab130)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/sapi/cli/php_cli.c:974
#67 0x00005555556364e4 in main (argc=4, argv=0x555555bab130)
    at /build/php7.0-Y7XHJx/php7.0-7.0.3/sapi/cli/php_cli.c:1345

From a live gdb session:

(gdb) print subject
$3 = 0x7fffed43e1f8 "\303\251\303\204\303\237\343\201\224a"
(gdb) print offsets
$4 = (int *) 0x7fffffff9150
(gdb) print offsets[0]
$5 = 2
(gdb) print last_match
$6 = 0x7fffed43e1fc "\303\237\343\201\224a"
(gdb) print &subject[offsets[0]]-last_match
$7 = -2

This line (ext/pcre/php_pcre.c:1808):

ZVAL_STRINGL(&tmp, last_match, &subject[offsets[0]]-last_match);

is the culprit, but I don't know why. The last value is -2, as indicated above?

This code seems relatively unchanged compared to the PHP5 version I have handy, so I'm unsure of what's happening. 


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-02-24 21:18 UTC] ngompa13 at gmail dot com
I've been able to reproduce the issue on CentOS 7.2 with Remi's php70 SCL with PHP package version 7.0.3-1.el7.remi.
 [2016-02-24 21:39 UTC] nish dot aravamudan at canonical dot com
FYI, also reproduced with 7.0.4~rc1.
 [2016-02-25 07:24 UTC] laruence@php.net
-Status: Open +Status: Feedback
 [2016-02-25 07:24 UTC] laruence@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves. 

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external 
resources such as databases, etc. If the script requires a 
database to demonstrate the issue, please make sure it creates 
all necessary tables, stored procedures etc.

Please avoid embedding huge scripts into the report.


 [2016-02-26 02:41 UTC] nish dot aravamudan at canonical dot com
I apologize, but I'm not a PHP developer. The twig testsuite is publicly available from github.
 [2016-02-26 21:34 UTC] nish dot aravamudan at canonical dot com
Adding pcre.jit=0 to the invocation of the tests (-d parameter to phpunit) allows the test to succeed.
 [2016-02-28 08:38 UTC] pajoye@php.net
One of the JIT bugs in pcre. It should be reported upstream.
 [2016-03-02 16:45 UTC] nish dot aravamudan at canonical dot com
-Status: Feedback +Status: Open
 [2016-03-02 16:45 UTC] nish dot aravamudan at canonical dot com
https://bugs.exim.org/show_bug.cgi?id=1803 filed.

However, two updates from my testing last night.

1) The twig testsuite is run using phpunit. phpunit has a parameter --process-isolation. When the tests are run with that parameter, the tests pass (even with pcre.jit left on). This, I think, points to a PHP bug, but I'm not sure.

2) When I tried to just run the failing twig tests on their own (split_utf8.test and length_utf8.test are the two input files), I could not recreate the segmentation fault. This again, it feels like, points to a fault somewhere in the php logic due to some corrupt state, perhaps?
 [2016-03-02 21:22 UTC] inefedor at gmail dot com
Probably when phpunit runs tests in different process, it uses another php binary or another set of php.ini settings.
 [2016-03-08 22:48 UTC] nish dot aravamudan at canonical dot com
I'm going to test a new version of PHP7.0 that has a small adjustment to php_

--- php7.0-7.0.3.orig/ext/pcre/php_pcre.c
+++ php7.0-7.0.3/ext/pcre/php_pcre.c
@@ -1761,6 +1761,7 @@ PHPAPI void php_pcre_split_impl(pcre_cac
        extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
        extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
 #ifdef PCRE_EXTRA_MARK
+       extra->mark = &mark;
        extra->flags &= ~PCRE_EXTRA_MARK;
 #endif


Commit https://github.com/php/php-src/commit/376ab3b7873ca04142185d8c08dbb4c4be152474 (and presumably others based upon the current state of the code) modified the other functions to avoid ->mark corruption. I don't know why this only shows up with JIT, but perhaps the ->mark value is not clobbered except if JIT is used.
 [2016-03-08 23:00 UTC] nish dot aravamudan at canonical dot com
If I understand this right, that should rather be:

+       extra->mark = NULL;
 [2016-03-08 23:49 UTC] nish dot aravamudan at canonical dot com
I was probably overconfident in my ability to understand the PHP code :)

But now I think the correct fix is:

Index: gitwd/ext/pcre/php_pcre.c
===================================================================
--- gitwd.orig/ext/pcre/php_pcre.c
+++ gitwd/ext/pcre/php_pcre.c
@@ -1848,6 +1848,10 @@ PHPAPI void php_pcre_split_impl(pcre_cac
                                                        RETURN_FALSE;
                                                }
                                        }
+#ifdef PCRE_EXTRA_MARK
+                                       extra_bump->mark = NULL;
+                                       extra_bump->flags &= ~PCRE_EXTRA_MARK;
+#endif
                                        count = pcre_exec(re_bump, extra_bump, subject,
                                                          subject_len, start_offset,
                                                          exoptions, offsets, size_offsets);
 [2016-03-09 18:29 UTC] nish dot aravamudan at canonical dot com
Ok, so I believe the right change is probably to just unset the PCRE_EXTRA_MARK bit in the flags of extra_bump. I just tested my previous patch (which also unset mark itself) and can confirm that the twig testsuite passes now, but I think the smaller change (to just modify extra_bump) will be functionally equivalent. Is a GitHub PR the appropriate way to suggest the patch get included?
 [2016-03-09 21:44 UTC] nikic@php.net
To clarify that I understand the issue correctly: What's happening here is that the regex /./us is already used (from the userland side) and we'll set the MARK flag and mark pointer in pcre_extra. This will then be cached. preg_split() then reuses that compiled regex from cache without clearing the MARK bit, so it will be using a dangling mark pointer. If that understand is correct your fix looks reasonable.

However I'm not sure why we have that code there at all. That seems like a very, very terrible way to advance one UTF-8 character. In other places we use http://lxr.php.net/xref/PHP_MASTER/ext/pcre/php_pcre.c#251, which should be a good sight more efficient. I'll check if we can just replace it altogether or if this is working around some weird issue.
 [2016-03-09 21:59 UTC] nish dot aravamudan at canonical dot com
I believe your understanding to be correct :)

Also, your comments are alluded to by the pcre developer who helped me debug this: https://bugs.exim.org/show_bug.cgi?id=1803#c44
 [2016-03-09 22:03 UTC] nikic@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=5a6da79fd0bd88997b3679578c7702bc74b3f61a
Log: Fix bug #71659
 [2016-03-09 22:03 UTC] nikic@php.net
-Status: Open +Status: Closed
 [2016-03-09 22:17 UTC] nikic@php.net
-Assigned To: +Assigned To: nikic
 [2016-03-09 22:17 UTC] nikic@php.net
As it did not break any tests, I've replaced the bump regex with use of calculate_unit_length(). I've verified that this does fix the segfault in the Twig testsuite.

I think there's still one potential issue left (though very unlikely): Some of the pcre_exec uses are in loops where MARK is set outside the loop. The loop could potentially trigger a "too many substrings" condition and the error handler could recursively invoke pcre_exec with a different mark value. We should probably move the code into the pcre_exec loop just to be sure.

In any case, thanks a lot to you and Zoltan Herczeg both for investigating this tricky issue!
 [2016-07-20 11:33 UTC] davey@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=5a6da79fd0bd88997b3679578c7702bc74b3f61a
Log: Fix bug #71659
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC