Bug: slice plugin fatal assertion — TSHttpTxnEffectiveUrlStringGet called with invalid txn in read_resp_hdr (related #11618)
Summary
ATS crashes with a fatal assertion when the slice plugin calls TSHttpTxnEffectiveUrlStringGet inside its server intercept response handler. The call site was introduced by the Conditional Slicing feature (#11618). At the point read_resp_hdr fires inside the intercept PluginVC, the original transaction's HTTP state machine has already advanced past the hook phase where this API is valid, causing sdk_sanity_check_txn to fail and ATS to abort.
This is a fleet-wide, reproducible crash observed on two independent hosts running the same slice.so build, confirming it is not an isolated incident.
Version
- ATS version: 10.1.3 (RPM build path references 10.1.2 sources — possible packaging issue worth investigating separately)
- OS: RHEL 8 / Linux x86_64, kernel 4.18.0-553.x.el8_10
- Build date: 2026-05-26
Plugins loaded
slice.so, cache_range_requests.so, header_rewrite.so, compress.so, regex_remap.so, cachekey.so, background_fetch.so, maxmind_acl.so, tslua.so
Steps to reproduce
- Configure ATS with the
slice plugin active on remap rules, with or without --minimum-size (conditional slicing).
- Serve traffic that triggers the slice intercept path (range requests or slice-eligible objects).
- Under production load, ATS fatally aborts.
Observed behaviour
Fatal assertion in src/api/InkAPI.cc:3940:
Fatal: /rpmbuilddir/BUILD/trafficserver-10.1.2/src/api/InkAPI.cc:3940:
failed assertion `TS_SUCCESS == sdk_sanity_check_txn(txnp)`
Crash stack (from traffic_crashlog), identical across both affected hosts:
Thread [ET_NET N]:
crash_logger_invoke
gsignal / abort
ink_abort
_ink_assert
_TSReleaseAssert
TSHttpTxnEffectiveUrlStringGet(txnp, &urllen) ← assertion fires here
handle_server_resp(tsapi_cont*, TSEvent, Data*) [slice.so +0x4eb]
intercept_hook(tsapi_cont*, TSEvent, void*) [slice.so +0x41d]
INKContInternal::handle_event
PluginVC::process_write_side
PluginVC::main_handler
Identical function offsets (handle_server_resp +0x4eb, intercept_hook +0x41d) on both hosts confirm the same slice.so binary is affected fleet-wide.
Root cause
PR #11618 (Conditional Slicing) added a new read_resp_hdr function in plugins/slice/server.cc that calls TSHttpTxnEffectiveUrlStringGet(txnp) to look up the effective URL for updating the object size cache. This function is registered as a TS_HTTP_READ_RESPONSE_HDR_HOOK and also fires from within the intercept handler chain via handle_server_resp.
When slice sets up a server intercept via TSContCreate(intercept_hook, mutex), the PluginVC drives a fake origin exchange. When handle_server_resp fires inside that intercept, the txnp is the intercept pseudo-transaction. The original HTTP SM-backed transaction has already advanced past TS_HTTP_READ_REQUEST_HDR_HOOK state, so sdk_sanity_check_txn — which validates that the pointer refers to an HttpSM in an expected hook state — returns TS_ERROR, triggering the release assert.
This is a fundamental API lifecycle violation: TSHttpTxnEffectiveUrlStringGet is only valid while the original transaction is in a hook callback, not from inside an intercept server response handler.
Suggested fix from claude.ai
The effective URL should be captured once in read_request (where txnp is fully valid) and stored in the Data struct, then read from there in read_resp_hdr instead of calling the API again.
1. Add a field to the Data struct:
struct Data {
// ... existing fields ...
std::string effective_url; // captured at read_request time, before data.release()
};
2. Capture in read_request before data.release():
int urllen = 0;
char *urlstr = TSHttpTxnEffectiveUrlStringGet(txnp, &urllen);
if (urlstr != nullptr) {
data->effective_url.assign(urlstr, urllen);
TSfree(urlstr);
}
// ... then data.release() as before
3. Use the stored URL in read_resp_hdr (intercept path):
// Remove: char *urlstr = TSHttpTxnEffectiveUrlStringGet(txnp, &urllen);
// Replace with:
Data *data = static_cast<Data *>(TSContDataGet(contp));
if (data->effective_url.empty()) {
// no URL available — update stats, reenable, return
}
std::string_view url = data->effective_url;
4. Non-intercept path (first-time large object discovery):
For the non-intercepted txn hook, allocate a small struct at hook registration time in read_request (while txnp is still valid), capture the URL there, and pass it as cont data. Destroy it in the response handler after use.
struct RespHdrData {
Config *config;
std::string effective_url;
};
// In read_request, non-intercept branch:
auto *rhdata = new RespHdrData{config, {}};
int urllen = 0;
char *urlstr = TSHttpTxnEffectiveUrlStringGet(txnp, &urllen);
if (urlstr) { rhdata->effective_url.assign(urlstr, urllen); TSfree(urlstr); }
TSCont resp_contp = TSContCreate(read_resp_hdr, nullptr);
TSContDataSet(resp_contp, rhdata);
TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, resp_contp);
// TSContDestroy + delete rhdata after use in the handler
Expected behaviour
The slice plugin should not call TSHttpTxnEffectiveUrlStringGet from within an intercept server response handler. The URL should be captured earlier in the transaction lifecycle and stored for later use.
Workaround
Disabling the slice plugin stops the crashes. No other workaround is known.
Additional context
- Crash confirmed on two independent production hosts with identical
slice.so binary (confirmed by matching intra-plugin offsets despite different ASLR load addresses and plugin sandbox UUIDs).
- The version string mismatch (binary reports 10.1.3, RPM build path shows 10.1.2) may indicate a separate packaging issue.
- Signal information and CPU registers are not present in the crashlog because the crash is triggered by
abort() rather than a hardware fault — this is expected behaviour.
- 74 ET_NET threads were active at crash time, indicating a high-concurrency production environment.
Bug: slice plugin fatal assertion —
TSHttpTxnEffectiveUrlStringGetcalled with invalid txn inread_resp_hdr(related #11618)Summary
ATS crashes with a fatal assertion when the slice plugin calls
TSHttpTxnEffectiveUrlStringGetinside its server intercept response handler. The call site was introduced by the Conditional Slicing feature (#11618). At the pointread_resp_hdrfires inside the intercept PluginVC, the original transaction's HTTP state machine has already advanced past the hook phase where this API is valid, causingsdk_sanity_check_txnto fail and ATS to abort.This is a fleet-wide, reproducible crash observed on two independent hosts running the same slice.so build, confirming it is not an isolated incident.
Version
Plugins loaded
slice.so,cache_range_requests.so,header_rewrite.so,compress.so,regex_remap.so,cachekey.so,background_fetch.so,maxmind_acl.so,tslua.soSteps to reproduce
sliceplugin active on remap rules, with or without--minimum-size(conditional slicing).Observed behaviour
Fatal assertion in
src/api/InkAPI.cc:3940:Crash stack (from
traffic_crashlog), identical across both affected hosts:Identical function offsets (
handle_server_resp +0x4eb,intercept_hook +0x41d) on both hosts confirm the same slice.so binary is affected fleet-wide.Root cause
PR #11618 (Conditional Slicing) added a new
read_resp_hdrfunction inplugins/slice/server.ccthat callsTSHttpTxnEffectiveUrlStringGet(txnp)to look up the effective URL for updating the object size cache. This function is registered as aTS_HTTP_READ_RESPONSE_HDR_HOOKand also fires from within the intercept handler chain viahandle_server_resp.When slice sets up a server intercept via
TSContCreate(intercept_hook, mutex), thePluginVCdrives a fake origin exchange. Whenhandle_server_respfires inside that intercept, thetxnpis the intercept pseudo-transaction. The original HTTP SM-backed transaction has already advanced pastTS_HTTP_READ_REQUEST_HDR_HOOKstate, sosdk_sanity_check_txn— which validates that the pointer refers to anHttpSMin an expected hook state — returnsTS_ERROR, triggering the release assert.This is a fundamental API lifecycle violation:
TSHttpTxnEffectiveUrlStringGetis only valid while the original transaction is in a hook callback, not from inside an intercept server response handler.Suggested fix from claude.ai
The effective URL should be captured once in
read_request(wheretxnpis fully valid) and stored in theDatastruct, then read from there inread_resp_hdrinstead of calling the API again.1. Add a field to the
Datastruct:2. Capture in
read_requestbeforedata.release():3. Use the stored URL in
read_resp_hdr(intercept path):4. Non-intercept path (first-time large object discovery):
For the non-intercepted txn hook, allocate a small struct at hook registration time in
read_request(whiletxnpis still valid), capture the URL there, and pass it as cont data. Destroy it in the response handler after use.Expected behaviour
The slice plugin should not call
TSHttpTxnEffectiveUrlStringGetfrom within an intercept server response handler. The URL should be captured earlier in the transaction lifecycle and stored for later use.Workaround
Disabling the
sliceplugin stops the crashes. No other workaround is known.Additional context
slice.sobinary (confirmed by matching intra-plugin offsets despite different ASLR load addresses and plugin sandbox UUIDs).abort()rather than a hardware fault — this is expected behaviour.