Skip to content

Commit 473e054

Browse files
Walt Karasbryancall
authored andcommitted
Fix problems with "Probe" option for X-Debug MIME header field. (#6197)
Fix problems with "Probe" option for X-Debug MIME header field. - Adds exercise of "Probe" to the existing x_remap Au test. - Remove memory leaks and risk of double deletion in xdebug plugin.
1 parent 15a6890 commit 473e054

File tree

17 files changed

+577
-81
lines changed

17 files changed

+577
-81
lines changed

plugins/xdebug/Cleanup.h

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/**
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
/**
20+
* @file Cleanup.h
21+
* @brief Easy-to-use utilities to avoid resource leaks or double-releases of resources. Independent of the rest
22+
* of the CPPAPI.
23+
*/
24+
25+
#pragma once
26+
27+
#include <type_traits>
28+
#include <memory>
29+
30+
#include <ts/ts.h>
31+
32+
namespace atscppapi
33+
{
34+
// For TS API types TSXxx with a TSXxxDestroy function, define standard deleter TSXxxDeleter, and use it to
35+
// define TSXxxUniqPtr (specialization of std::unique_ptr). X() is used when the destroy function returns void,
36+
// Y() is used when the destroy function returns TSReturnCode.
37+
38+
#if defined(X)
39+
#error "X defined as preprocessor symbol"
40+
#endif
41+
42+
#define X(NAME_SEGMENT) \
43+
struct TS##NAME_SEGMENT##Deleter { \
44+
void \
45+
operator()(TS##NAME_SEGMENT ptr) \
46+
{ \
47+
TS##NAME_SEGMENT##Destroy(ptr); \
48+
} \
49+
}; \
50+
using TS##NAME_SEGMENT##UniqPtr = std::unique_ptr<std::remove_pointer_t<TS##NAME_SEGMENT>, TS##NAME_SEGMENT##Deleter>;
51+
52+
#if defined(Y)
53+
#error "Y defined as preprocessor symbol"
54+
#endif
55+
56+
#define Y(NAME_SEGMENT) \
57+
struct TS##NAME_SEGMENT##Deleter { \
58+
void \
59+
operator()(TS##NAME_SEGMENT ptr) \
60+
{ \
61+
TSAssert(TS##NAME_SEGMENT##Destroy(ptr) == TS_SUCCESS); \
62+
} \
63+
}; \
64+
using TS##NAME_SEGMENT##UniqPtr = std::unique_ptr<std::remove_pointer_t<TS##NAME_SEGMENT>, TS##NAME_SEGMENT##Deleter>;
65+
66+
Y(MBuffer) // Defines TSMBufferDeleter and TSMBufferUniqPtr.
67+
X(MimeParser) // Defines TSMimeParserDeleter and TSMimeParserUniqPtr.
68+
X(Thread) // Defines TSThreadDeleter and TSThreadUniqPtr.
69+
X(Mutex) // Defines TSMutexDeleter and TSMutexUniqPtr.
70+
Y(CacheKey) // Defines TSCacheKeyDeleter and TSCacheKeyUniqPtr.
71+
X(Cont) // Defines TSContDeleter and TSContUniqPtr.
72+
X(SslContext) // Defines TSSslContextDeleter and TSSslContextUniqPtr.
73+
X(IOBuffer) // Defines TSIOBufferDeleter and TSIOBufferUniqPtr.
74+
Y(TextLogObject) // Defines TSTextLogObjectDeleter and TSTextLogObjectUniqPtr.
75+
X(Uuid) // Defines TSUuidDeleter and TSUuidUniqPtr.
76+
77+
#undef X
78+
#undef Y
79+
80+
// Deleter and unique pointer for memory buffer returned by TSalloc(), TSrealloc(), Tstrdup(), TSsrtndup().
81+
//
82+
struct TSMemDeleter {
83+
void
84+
operator()(void *ptr)
85+
{
86+
TSfree(ptr);
87+
}
88+
};
89+
using TSMemUniqPtr = std::unique_ptr<void, TSMemDeleter>;
90+
91+
// Deleter and unique pointer for TSIOBufferReader. Care must be taken that the reader is deleted before the
92+
// TSIOBuffer to which it refers is deleted.
93+
//
94+
struct TSIOBufferReaderDeleter {
95+
void
96+
operator()(TSIOBufferReader ptr)
97+
{
98+
TSIOBufferReaderFree(ptr);
99+
}
100+
};
101+
using TSIOBufferReaderUniqPtr = std::unique_ptr<std::remove_pointer_t<TSIOBufferReader>, TSIOBufferReaderDeleter>;
102+
103+
class TxnAuxDataMgrBase
104+
{
105+
protected:
106+
struct MgrData_ {
107+
TSCont txnCloseContp = nullptr;
108+
int txnArgIndex = -1;
109+
};
110+
111+
public:
112+
class MgrData : private MgrData_
113+
{
114+
friend class TxnAuxDataMgrBase;
115+
};
116+
117+
protected:
118+
static MgrData_ &
119+
access(MgrData &md)
120+
{
121+
return md;
122+
}
123+
};
124+
125+
using TxnAuxMgrData = TxnAuxDataMgrBase::MgrData;
126+
127+
// Class to manage auxilliary data for a transaction. If an instance is created for the transaction, the instance
128+
// will be deleted on the TXN_CLOSE transaction hook (which is always triggered for all transactions).
129+
// The TxnAuxData class must have a public default constructor.
130+
//
131+
template <class TxnAuxData, TxnAuxMgrData &MDRef> class TxnAuxDataMgr : private TxnAuxDataMgrBase
132+
{
133+
public:
134+
using Data = TxnAuxData;
135+
136+
// This must be called from the plugin init function. arg_name is the name for the transaction argument used
137+
// to store the pointer to the auxiliary data class instance. Repeated calls are ignored.
138+
//
139+
static void
140+
init(char const *arg_name, char const *arg_desc = "per-transaction auxiliary data")
141+
{
142+
MgrData_ &md = access(MDRef);
143+
144+
if (md.txnArgIndex >= 0) {
145+
return;
146+
}
147+
148+
TSReleaseAssert(TSHttpTxnArgIndexReserve(arg_name, arg_desc, &md.txnArgIndex) == TS_SUCCESS);
149+
TSReleaseAssert(md.txnCloseContp = TSContCreate(_deleteAuxData, nullptr));
150+
}
151+
152+
// Get a reference to the auxiliary data for a transaction.
153+
//
154+
static TxnAuxData &
155+
data(TSHttpTxn txn)
156+
{
157+
MgrData_ &md = access(MDRef);
158+
159+
TSAssert(md.txnArgIndex >= 0);
160+
161+
auto d = static_cast<TxnAuxData *>(TSHttpTxnArgGet(txn, md.txnArgIndex));
162+
if (!d) {
163+
d = new TxnAuxData;
164+
165+
TSHttpTxnArgSet(txn, md.txnArgIndex, d);
166+
167+
TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, md.txnCloseContp);
168+
}
169+
return *d;
170+
}
171+
172+
private:
173+
static int
174+
_deleteAuxData(TSCont, TSEvent, void *edata)
175+
{
176+
MgrData_ &md = access(MDRef);
177+
178+
auto txn = static_cast<TSHttpTxn>(edata);
179+
auto data = static_cast<TxnAuxData *>(TSHttpTxnArgGet(txn, md.txnArgIndex));
180+
delete data;
181+
TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
182+
return 0;
183+
};
184+
};
185+
186+
} // end namespace atscppapi

plugins/xdebug/Makefile.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@
1515
# limitations under the License.
1616

1717
pkglib_LTLIBRARIES += xdebug/xdebug.la
18-
xdebug_xdebug_la_SOURCES = xdebug/xdebug.cc
18+
xdebug_xdebug_la_SOURCES = \
19+
xdebug/Cleanup.h \
20+
xdebug/xdebug.cc

plugins/xdebug/xdebug.cc

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <strings.h>
2323
#include <sstream>
2424
#include <cstring>
25+
#include <atomic>
26+
#include <memory>
2527
#include <getopt.h>
2628
#include <cstdint>
2729
#include <cinttypes>
@@ -32,6 +34,34 @@
3234
#include "tscore/ink_defs.h"
3335
#include "tscpp/util/PostScript.h"
3436
#include "tscpp/util/TextView.h"
37+
#include "Cleanup.h"
38+
39+
namespace
40+
{
41+
struct BodyBuilder {
42+
atscppapi::TSContUniqPtr transform_connp;
43+
atscppapi::TSIOBufferUniqPtr output_buffer;
44+
// It's important that output_reader comes after output_buffer so it will be deleted first.
45+
atscppapi::TSIOBufferReaderUniqPtr output_reader;
46+
TSVIO output_vio = nullptr;
47+
bool wrote_prebody = false;
48+
bool wrote_body = false;
49+
bool hdr_ready = false;
50+
std::atomic_flag wrote_postbody;
51+
52+
int64_t nbytes = 0;
53+
};
54+
55+
struct XDebugTxnAuxData {
56+
std::unique_ptr<BodyBuilder> body_builder;
57+
unsigned xheaders = 0;
58+
};
59+
60+
atscppapi::TxnAuxMgrData mgrData;
61+
62+
using AuxDataMgr = atscppapi::TxnAuxDataMgr<XDebugTxnAuxData, mgrData>;
63+
64+
} // end anonymous namespace
3565

3666
#include "xdebug_headers.cc"
3767
#include "xdebug_transforms.cc"
@@ -53,8 +83,6 @@ enum {
5383
XHEADER_X_PSELECT_KEY = 1u << 10,
5484
};
5585

56-
static int XArgIndex = 0;
57-
static int BodyBuilderArgIndex = 0;
5886
static TSCont XInjectHeadersCont = nullptr;
5987
static TSCont XDeleteDebugHdrCont = nullptr;
6088

@@ -382,7 +410,7 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata)
382410

383411
TSReleaseAssert(event == TS_EVENT_HTTP_SEND_RESPONSE_HDR);
384412

385-
uintptr_t xheaders = reinterpret_cast<uintptr_t>(TSHttpTxnArgGet(txn, XArgIndex));
413+
unsigned xheaders = AuxDataMgr::data(txn).xheaders;
386414
if (xheaders == 0) {
387415
goto done;
388416
}
@@ -422,14 +450,14 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata)
422450
}
423451

424452
if (xheaders & XHEADER_X_PROBE_HEADERS) {
425-
BodyBuilder *data = static_cast<BodyBuilder *>(TSHttpTxnArgGet(txn, BodyBuilderArgIndex));
453+
BodyBuilder *data = AuxDataMgr::data(txn).body_builder.get();
426454
TSDebug("xdebug_transform", "XInjectResponseHeaders(): client resp header ready");
427455
if (data == nullptr) {
428456
TSHttpTxnReenable(txn, TS_EVENT_HTTP_ERROR);
429457
return TS_ERROR;
430458
}
431459
data->hdr_ready = true;
432-
writePostBody(data);
460+
writePostBody(txn, data);
433461
}
434462

435463
if (xheaders & XHEADER_X_PSELECT_KEY) {
@@ -493,9 +521,9 @@ isFwdFieldValue(std::string_view value, intmax_t &fwdCnt)
493521
static int
494522
XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
495523
{
496-
TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
497-
uintptr_t xheaders = 0;
498-
intmax_t fwdCnt = 0;
524+
TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
525+
unsigned xheaders = 0;
526+
intmax_t fwdCnt = 0;
499527
TSMLoc field, next;
500528
TSMBuffer buffer;
501529
TSMLoc hdr;
@@ -550,26 +578,17 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
550578
} else if (header_field_eq("probe", value, vsize)) {
551579
xheaders |= XHEADER_X_PROBE_HEADERS;
552580

581+
auto &auxData = AuxDataMgr::data(txn);
582+
553583
// prefix request headers and postfix response headers
554584
BodyBuilder *data = new BodyBuilder();
555-
data->txn = txn;
585+
auxData.body_builder.reset(data);
556586

557587
TSVConn connp = TSTransformCreate(body_transform, txn);
558-
TSContDataSet(connp, data);
588+
data->transform_connp.reset(connp);
589+
TSContDataSet(connp, txn);
559590
TSHttpTxnHookAdd(txn, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
560591

561-
// store data pointer in txnarg to use in global cont XInjectResponseHeaders
562-
TSHttpTxnArgSet(txn, BodyBuilderArgIndex, data);
563-
564-
// create a self-cleanup on close
565-
auto cleanupBodyBuilder = [](TSCont /* contp */, TSEvent event, void *edata) -> int {
566-
TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
567-
BodyBuilder *data = static_cast<BodyBuilder *>(TSHttpTxnArgGet(txn, BodyBuilderArgIndex));
568-
delete data;
569-
return TS_EVENT_NONE;
570-
};
571-
TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, TSContCreate(cleanupBodyBuilder, nullptr));
572-
573592
// disable writing to cache because we are injecting data into the body.
574593
TSHttpTxnReqCacheableSet(txn, 0);
575594
TSHttpTxnRespCacheableSet(txn, 0);
@@ -608,7 +627,7 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
608627
TSDebug("xdebug", "adding response hook for header mask %p and forward count %" PRIiMAX, reinterpret_cast<void *>(xheaders),
609628
fwdCnt);
610629
TSHttpTxnHookAdd(txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, XInjectHeadersCont);
611-
TSHttpTxnArgSet(txn, XArgIndex, reinterpret_cast<void *>(xheaders));
630+
AuxDataMgr::data(txn).xheaders = xheaders;
612631

613632
if (fwdCnt == 0) {
614633
// X-Debug header has to be deleted, but not too soon for other plugins to see it.
@@ -688,9 +707,9 @@ TSPluginInit(int argc, const char *argv[])
688707
}
689708
xDebugHeader.len = strlen(xDebugHeader.str);
690709

710+
AuxDataMgr::init("xdebug");
711+
691712
// Setup the global hook
692-
TSReleaseAssert(TSHttpTxnArgIndexReserve("xdebug", "xdebug header requests", &XArgIndex) == TS_SUCCESS);
693-
TSReleaseAssert(TSHttpTxnArgIndexReserve("bodyTransform", "BodyBuilder*", &XArgIndex) == TS_SUCCESS);
694713
TSReleaseAssert(XInjectHeadersCont = TSContCreate(XInjectResponseHeaders, nullptr));
695714
TSReleaseAssert(XDeleteDebugHdrCont = TSContCreate(XDeleteDebugHdr, nullptr));
696715
TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(XScanRequestHeaders, nullptr));

0 commit comments

Comments
 (0)