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

moonchen 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 bdb24cfd93 Fix set-status crash inside if/endif at remap time (#13052)
bdb24cfd93 is described below

commit bdb24cfd93e1c6e9a27a4d03b126121d085596da
Author: Mo Chen <[email protected]>
AuthorDate: Tue Jun 23 16:59:35 2026 -0500

    Fix set-status crash inside if/endif at remap time (#13052)
---
 include/proxy/hdrs/HTTP.h                          |  4 +--
 plugins/header_rewrite/operators.cc                | 18 +++++--------
 plugins/header_rewrite/ruleset.cc                  |  4 +--
 plugins/header_rewrite/statement.cc                | 10 ++-----
 plugins/header_rewrite/statement.h                 | 10 ++-----
 src/proxy/hdrs/HTTP.cc                             |  4 +--
 .../header_rewrite_bundle.replay.yaml              | 31 ++++++++++++++++++++++
 .../header_rewrite/rules/set_status_in_if.conf     | 27 +++++++++++++++++++
 8 files changed, 74 insertions(+), 34 deletions(-)

diff --git a/include/proxy/hdrs/HTTP.h b/include/proxy/hdrs/HTTP.h
index 104743e593..571bd0fdc5 100644
--- a/include/proxy/hdrs/HTTP.h
+++ b/include/proxy/hdrs/HTTP.h
@@ -1097,7 +1097,7 @@ inline void
 HTTPHdr::status_set(HTTPStatus status)
 {
   ink_assert(valid());
-  ink_assert(m_http->m_polarity == HTTPType::RESPONSE);
+  ink_release_assert(m_http->m_polarity == HTTPType::RESPONSE);
 
   http_hdr_status_set(m_http, status);
 }
@@ -1121,7 +1121,7 @@ inline void
 HTTPHdr::reason_set(std::string_view value)
 {
   ink_assert(valid());
-  ink_assert(m_http->m_polarity == HTTPType::RESPONSE);
+  ink_release_assert(m_http->m_polarity == HTTPType::RESPONSE);
 
   http_hdr_reason_set(m_heap, m_http, value, true);
 }
diff --git a/plugins/header_rewrite/operators.cc 
b/plugins/header_rewrite/operators.cc
index e82b834468..133991228c 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -205,19 +205,13 @@ OperatorSetStatus::initialize_hooks()
 bool
 OperatorSetStatus::exec(const Resources &res) const
 {
-  switch (get_hook()) {
-  case TS_HTTP_READ_RESPONSE_HDR_HOOK:
-  case TS_HTTP_SEND_RESPONSE_HDR_HOOK:
-    if (res.bufp && res.hdr_loc) {
-      TSHttpHdrStatusSet(res.bufp, res.hdr_loc, 
static_cast<TSHttpStatus>(_status.get_int_value()), res.state.txnp, 
PLUGIN_NAME);
-      if (_reason && _reason_len > 0) {
-        TSHttpHdrReasonSet(res.bufp, res.hdr_loc, _reason, _reason_len);
-      }
+  if (res.bufp && res.hdr_loc && TSHttpHdrTypeGet(res.bufp, res.hdr_loc) == 
TS_HTTP_TYPE_RESPONSE) {
+    TSHttpHdrStatusSet(res.bufp, res.hdr_loc, 
static_cast<TSHttpStatus>(_status.get_int_value()), res.state.txnp, 
PLUGIN_NAME);
+    if (_reason && _reason_len > 0) {
+      TSHttpHdrReasonSet(res.bufp, res.hdr_loc, _reason, _reason_len);
     }
-    break;
-  default:
+  } else {
     TSHttpTxnStatusSet(res.state.txnp, 
static_cast<TSHttpStatus>(_status.get_int_value()), PLUGIN_NAME);
-    break;
   }
 
   Dbg(pi_dbg_ctl, "OperatorSetStatus::exec() invoked with status=%d", 
_status.get_int_value());
@@ -608,7 +602,7 @@ OperatorSetRedirect::exec(const Resources &res) const
       const_cast<Resources &>(res).changed_url = true;
       res._rri->redirect                       = 1;
     } else {
-      Dbg(pi_dbg_ctl, "OperatorSetRedirect::exec() hook=%d", int(get_hook()));
+      Dbg(pi_dbg_ctl, "OperatorSetRedirect::exec() redirect to %s", 
value.c_str());
       // Set the new status code and reason.
       TSHttpStatus status = static_cast<TSHttpStatus>(_status.get_int_value());
       TSHttpHdrStatusSet(res.bufp, res.hdr_loc, status, res.state.txnp, 
PLUGIN_NAME);
diff --git a/plugins/header_rewrite/ruleset.cc 
b/plugins/header_rewrite/ruleset.cc
index 39964fc68e..acbeafc72f 100644
--- a/plugins/header_rewrite/ruleset.cc
+++ b/plugins/header_rewrite/ruleset.cc
@@ -67,7 +67,7 @@ RuleSet::make_condition(Parser &p, const char *filename, int 
lineno)
   Dbg(pi_dbg_ctl, "    Creating condition: %%{%s} with arg: %s", 
p.get_op().c_str(), p.get_arg().c_str());
   c->set_config_location(filename, lineno);
   c->initialize(p);
-  if (!c->set_hook(_hook)) {
+  if (!c->is_hook_valid(_hook)) {
     delete c;
     TSError("[%s] in %s:%d: can't use this condition in hook=%s: %%{%s} with 
arg: %s", PLUGIN_NAME, filename, lineno,
             TSHttpHookNameLookup(_hook), p.get_op().c_str(), 
p.get_arg().c_str());
@@ -95,7 +95,7 @@ RuleSet::add_operator(Parser &p, const char *filename, int 
lineno)
     Dbg(pi_dbg_ctl, "    Adding operator: %s(%s)=\"%s\"", p.get_op().c_str(), 
p.get_arg().c_str(), p.get_value().c_str());
     op->set_config_location(filename, lineno);
     op->initialize(p);
-    if (!op->set_hook(_hook)) {
+    if (!op->is_hook_valid(_hook)) {
       delete op;
       Dbg(pi_dbg_ctl, "in %s:%d: can't use this operator in hook=%s:  %s(%s)", 
filename, lineno, TSHttpHookNameLookup(_hook),
           p.get_op().c_str(), p.get_arg().c_str());
diff --git a/plugins/header_rewrite/statement.cc 
b/plugins/header_rewrite/statement.cc
index 8bce404a2d..351f5d572e 100644
--- a/plugins/header_rewrite/statement.cc
+++ b/plugins/header_rewrite/statement.cc
@@ -66,15 +66,9 @@ Statement::get_resource_ids() const
 }
 
 bool
-Statement::set_hook(TSHttpHookID hook)
+Statement::is_hook_valid(TSHttpHookID hook) const
 {
-  bool ret = std::find(_allowed_hooks.begin(), _allowed_hooks.end(), hook) != 
_allowed_hooks.end();
-
-  if (ret) {
-    _hook = hook;
-  }
-
-  return ret;
+  return std::find(_allowed_hooks.begin(), _allowed_hooks.end(), hook) != 
_allowed_hooks.end();
 }
 
 // This should be overridden for any Statement which only supports some hooks
diff --git a/plugins/header_rewrite/statement.h 
b/plugins/header_rewrite/statement.h
index 4ed73ec681..0a354fc694 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -152,13 +152,8 @@ public:
   Statement(const Statement &)      = delete;
   void operator=(const Statement &) = delete;
 
-  // Which hook are we adding this statement to?
-  bool set_hook(TSHttpHookID hook);
-  TSHttpHookID
-  get_hook() const
-  {
-    return _hook;
-  }
+  // Validate that this statement is allowed on the given hook. Used during 
parsing only.
+  bool is_hook_valid(TSHttpHookID hook) const;
 
   // Which hooks are this "statement" applicable for? Used during parsing only.
   void
@@ -300,7 +295,6 @@ protected:
 
 private:
   ResourceIDs               _rsrc = RSRC_NONE;
-  TSHttpHookID              _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
   std::vector<TSHttpHookID> _allowed_hooks;
   std::string               _config_filename;
   int                       _config_lineno = 0;
diff --git a/src/proxy/hdrs/HTTP.cc b/src/proxy/hdrs/HTTP.cc
index f7f80d7117..e00c9d13e8 100644
--- a/src/proxy/hdrs/HTTP.cc
+++ b/src/proxy/hdrs/HTTP.cc
@@ -693,7 +693,7 @@ http_hdr_url_set(HdrHeap *heap, HTTPHdrImpl *hh, URLImpl 
*url)
 void
 http_hdr_status_set(HTTPHdrImpl *hh, HTTPStatus status)
 {
-  ink_assert(hh->m_polarity == HTTPType::RESPONSE);
+  ink_release_assert(hh->m_polarity == HTTPType::RESPONSE);
   hh->u.resp.m_status = static_cast<int16_t>(status);
 }
 
@@ -713,7 +713,7 @@ http_hdr_reason_get(HTTPHdrImpl *hh)
 void
 http_hdr_reason_set(HdrHeap *heap, HTTPHdrImpl *hh, std::string_view value, 
bool must_copy)
 {
-  ink_assert(hh->m_polarity == HTTPType::RESPONSE);
+  ink_release_assert(hh->m_polarity == HTTPType::RESPONSE);
   mime_str_u16_set(heap, value, &(hh->u.resp.m_ptr_reason), 
&(hh->u.resp.m_len_reason), must_copy);
 }
 
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
index b444acaae8..af07d37972 100644
--- 
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
+++ 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
@@ -176,6 +176,13 @@ autest:
             args:
               - "rules/rule_session_vars.conf"
 
+      - from: "http://www.example.com/from_18/";
+        to: "http://backend.ex:{SERVER_HTTP_PORT}/to_18/";
+        plugins:
+          - name: "header_rewrite.so"
+            args:
+              - "rules/set_status_in_if.conf"
+
     metric_checks:
       - metric: "proxy.process.plugin.header_rewrite.operators"
         min: 1
@@ -1942,3 +1949,27 @@ sessions:
       headers:
         fields:
         - [ X-Session-Seen, { value: "yes", as: equal } ]
+
+# Test 67: set-status inside if/endif at REMAP_PSEUDO_HOOK time.
+# Before the fix, the set-status operator inside an if/endif block retained
+# its default _hook (TS_HTTP_READ_RESPONSE_HDR_HOOK), so exec() called
+# TSHttpHdrStatusSet on a request buffer, corrupting the union and crashing.
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_18/test
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ uuid, 67 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 403
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/rules/set_status_in_if.conf 
b/tests/gold_tests/pluginTest/header_rewrite/rules/set_status_in_if.conf
new file mode 100644
index 0000000000..f8b2b56cc0
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/rules/set_status_in_if.conf
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+# Minimal reproduction for set-status inside if/endif at remap time.
+# Before the fix, the operator's _hook defaulted to
+# TS_HTTP_READ_RESPONSE_HDR_HOOK, so exec() called TSHttpHdrStatusSet
+# on a request buffer, corrupting the header union and crashing.
+#
+cond %{REMAP_PSEUDO_HOOK}
+    if
+        cond %{CLIENT-URL:PATH} /from_18/
+            set-status 403
+    endif

Reply via email to