This is an automated email from the ASF dual-hosted git repository.

bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new b279b9f5ca Guard pre-init log wakeups (#13472)
b279b9f5ca is described below

commit b279b9f5cad5f4bdbe13fe3b86bad45cbc8109c9
Author: Brian Neradt <[email protected]>
AuthorDate: Mon Aug 3 17:39:15 2026 -0500

    Guard pre-init log wakeups (#13472)
    
    Plugins can fill a text log buffer during TSPluginInit before logging
    preprocessing threads and their notification objects exist. This causes
    a null dereference that crashes traffic_server during startup.
    
    This problem is addressed in this patch by queuing those buffers without
    signaling until the logging workers exist. Their initial queue scan then
    flushes the pending data. A regression test forces a rollover during
    plugin initialization and verifies ATS stays up and preserves the log
    entry.
    
    With the production fix removed, the regression test caused
    traffic_server to crash with SIGSEGV at address zero while signaling the
    preprocessing notification. With the fix applied, the identical test
    passes.
---
 src/proxy/logging/LogObject.cc                   |  8 +++--
 tests/gold_tests/logging/log_plugin_init.test.py | 38 ++++++++++++++++++++++++
 tests/tools/plugins/test_log_interface.cc        | 11 ++++++-
 3 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/src/proxy/logging/LogObject.cc b/src/proxy/logging/LogObject.cc
index 9269ed3136..741019140b 100644
--- a/src/proxy/logging/LogObject.cc
+++ b/src/proxy/logging/LogObject.cc
@@ -395,7 +395,9 @@ LogObject::_checkout_write(size_t *write_offset, size_t 
bytes_needed)
         int idx = m_buffer_manager_idx++ % m_flush_threads;
         Dbg(dbg_ctl_log_logbuffer, "adding buffer %d to flush list after 
checkout", buffer->get_id());
         m_buffer_manager[idx].add_to_flush_queue(buffer);
-        Log::preproc_notify[idx].signal();
+        if (Log::preproc_notify != nullptr) {
+          Log::preproc_notify[idx].signal();
+        }
         buffer = nullptr;
       }
 
@@ -574,7 +576,9 @@ LogObject::flush_buffer(LogBuffer *buffer)
   int idx = m_buffer_manager_idx++ % m_flush_threads;
   Dbg(dbg_ctl_log_logbuffer, "adding buffer %d to flush list after checkout", 
buffer->get_id());
   m_buffer_manager[idx].add_to_flush_queue(buffer);
-  Log::preproc_notify[idx].signal();
+  if (Log::preproc_notify != nullptr) {
+    Log::preproc_notify[idx].signal();
+  }
 }
 
 int
diff --git a/tests/gold_tests/logging/log_plugin_init.test.py 
b/tests/gold_tests/logging/log_plugin_init.test.py
new file mode 100644
index 0000000000..24fa59ac72
--- /dev/null
+++ b/tests/gold_tests/logging/log_plugin_init.test.py
@@ -0,0 +1,38 @@
+'''
+Verify text logging during plugin initialization.
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+
+Test.Summary = '''
+Verify plugins can fill a text log buffer before logging threads start.
+'''
+
+ts = Test.MakeATSProcess('ts')
+ts.Disk.records_config.update({'proxy.config.log.log_buffer_size': 9216})
+Test.PrepareTestPlugin(os.path.join(Test.Variables.AtsTestPluginsDir, 
'test_log_interface.so'), ts, '--write-during-init')
+
+plugin_log = Test.Disk.File(os.path.join(ts.Variables.LOGDIR, 
'test_log_interface.log'), exists=True)
+plugin_log.Content = Testers.ContainsExpression(
+    'Writing during plugin initialization', 'The pre-initialization log buffer 
should be flushed')
+
+tr = Test.AddTestRun('Start ATS with a plugin that fills a text log buffer 
during initialization')
+tr.Processes.Default.Command = 'printf "traffic_server remained running"'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.StartBefore(ts)
+tr.StillRunningAfter = ts
diff --git a/tests/tools/plugins/test_log_interface.cc 
b/tests/tools/plugins/test_log_interface.cc
index c61d54fbfb..1378acc678 100644
--- a/tests/tools/plugins/test_log_interface.cc
+++ b/tests/tools/plugins/test_log_interface.cc
@@ -73,7 +73,7 @@ global_handler(TSCont /* continuation ATS_UNUSED */, TSEvent 
event, void *data)
 }
 
 void
-TSPluginInit(int /* argc ATS_UNUSED */, const char ** /* argv ATS_UNUSED */)
+TSPluginInit(int argc, const char **argv)
 {
   TSPluginRegistrationInfo info;
 
@@ -87,5 +87,14 @@ TSPluginInit(int /* argc ATS_UNUSED */, const char ** /* 
argv ATS_UNUSED */)
   }
 
   TSAssert(TS_SUCCESS == TSTextLogObjectCreate(plugin_name, 
TS_LOG_MODE_ADD_TIMESTAMP, &pluginlog));
+
+  if (argc > 1 && strcmp(argv[1], "--write-during-init") == 0) {
+    const std::string long_line(5000, 'i');
+
+    for (int i = 0; i < 2; ++i) {
+      TSAssert(TS_SUCCESS == TSTextLogObjectWrite(pluginlog, "Writing during 
plugin initialization: %s", long_line.c_str()));
+    }
+  }
+
   TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(global_handler, 
nullptr));
 }

Reply via email to