Skip to content

Commit 72ad8fb

Browse files
authored
Fix three high-impact Coverity defects (#13030)
* CID 1644226: Fix use-after-free in slice plugin — move TSfree() after last use of urlstr in DEBUG_LOG. * CID 1644298: Fix OOB write in IpAllow — break after MAX_SUBJECTS exceeded instead of continuing. * CID 1644219: Fix missing null terminator in slice plugin — replace strncpy() with memcpy() + explicit null terminator. * Add gold tests for IpAllow subjects overflow and slice long ETag edge cases.
1 parent d3ef523 commit 72ad8fb

8 files changed

Lines changed: 326 additions & 8 deletions

File tree

plugins/slice/server.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "ts/apidefs.h"
2727
#include "util.h"
2828

29+
#include <algorithm>
2930
#include <cinttypes>
3031

3132
namespace
@@ -463,14 +464,19 @@ handleNextServerHeader(Data *const data)
463464
data->m_blockstate = BlockState::PendingRef;
464465

465466
// interior headers for new identifier reference
467+
etaglen = std::min(etaglen, static_cast<int>(sizeof(data->m_etag) - 1));
466468
data->m_etaglen = etaglen;
467469
if (0 < etaglen) {
468-
strncpy(data->m_etag, etag, etaglen);
470+
memcpy(data->m_etag, etag, etaglen);
469471
}
472+
data->m_etag[etaglen] = '\0';
473+
474+
lastmodifiedlen = std::min(lastmodifiedlen, static_cast<int>(sizeof(data->m_lastmodified) - 1));
470475
data->m_lastmodifiedlen = lastmodifiedlen;
471476
if (0 < lastmodifiedlen) {
472-
strncpy(data->m_lastmodified, lastmodified, lastmodifiedlen);
477+
memcpy(data->m_lastmodified, lastmodified, lastmodifiedlen);
473478
}
479+
data->m_lastmodified[lastmodifiedlen] = '\0';
474480

475481
// potentially new content length
476482
data->m_contentlen = blockcr.m_length;

plugins/slice/slice.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "ts/ts.h"
2929

3030
#include <charconv>
31+
#include <memory>
3132
#include <netinet/in.h>
3233
#include <array>
3334
#include <string_view>
@@ -50,14 +51,13 @@ TSCont global_read_resp_hdr_contp;
5051
static bool
5152
should_skip_this_obj(TSHttpTxn txnp, Config *const config)
5253
{
53-
int len = 0;
54-
char *const urlstr = TSHttpTxnEffectiveUrlStringGet(txnp, &len);
54+
int len = 0;
55+
std::unique_ptr<char, decltype(&TSfree)> urlstr(TSHttpTxnEffectiveUrlStringGet(txnp, &len), TSfree);
5556

56-
bool const known_large = config->isKnownLargeObj({urlstr, static_cast<size_t>(len)});
57-
TSfree(urlstr);
57+
bool const known_large = config->isKnownLargeObj({urlstr.get(), static_cast<size_t>(len)});
5858

5959
if (!known_large) {
60-
DEBUG_LOG("Not a known large object, not slicing: %.*s", len, urlstr);
60+
DEBUG_LOG("Not a known large object, not slicing: %.*s", len, urlstr.get());
6161
return true;
6262
}
6363

src/proxy/IPAllow.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,10 @@ IpAllow::IpAllow(const char *ip_allow_config_var, const char *ip_categories_conf
240240
std::string_view::size_type s, e;
241241
for (s = 0, e = 0; s < subjects_sv.size() && e != subjects_sv.npos; s = e + 1) {
242242
e = subjects_sv.find(",", s);
243-
std::string_view subject_sv = subjects_sv.substr(s, e);
243+
std::string_view subject_sv = subjects_sv.substr(s, e == subjects_sv.npos ? subjects_sv.npos : e - s);
244244
if (i >= MAX_SUBJECTS) {
245245
Error("Too many ACL subjects were provided");
246+
break;
246247
}
247248
if (subject_sv == "PEER") {
248249
subjects[i] = Subject::PEER;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'''
2+
Verify that exceeding MAX_SUBJECTS for proxy.config.acl.subjects logs an error and does not crash.
3+
'''
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
# See the License for the specific language governing permissions and
18+
# limitations under the License.
19+
20+
Test.Summary = '''
21+
Verify that exceeding MAX_SUBJECTS for proxy.config.acl.subjects logs an error and does not crash.
22+
'''
23+
24+
# Scenario 1: Configuring 4 ACL subjects (more than MAX_SUBJECTS=3) should log
25+
# an error but not crash. Request should still succeed with 200 OK.
26+
Test.ATSReplayTest(replay_file="replay/ip_allow_subjects_overflow.replay.yaml")
27+
28+
# Scenario 2: Configuring exactly 3 ACL subjects (equal to MAX_SUBJECTS) should
29+
# work without error. Request should succeed with 200 OK.
30+
Test.ATSReplayTest(replay_file="replay/ip_allow_subjects_valid.replay.yaml")
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
meta:
18+
version: "1.0"
19+
20+
autest:
21+
description: 'Verify that exceeding MAX_SUBJECTS (4 subjects) logs an error but does not crash'
22+
23+
server:
24+
name: 'server-overflow'
25+
26+
client:
27+
name: 'client-overflow'
28+
29+
ats:
30+
name: 'ts-overflow'
31+
32+
process_config:
33+
enable_cache: false
34+
disable_log_checks: true
35+
36+
records_config:
37+
proxy.config.acl.subjects: 'PEER,PROXY,PLUGIN,PEER'
38+
39+
remap_config:
40+
- from: "http://www.example.com/"
41+
to: "http://127.0.0.1:{SERVER_HTTP_PORT}/"
42+
43+
log_validation:
44+
diags_log:
45+
contains:
46+
- expression: "Too many ACL subjects were provided"
47+
description: "Should log error when more than MAX_SUBJECTS are configured"
48+
49+
sessions:
50+
- transactions:
51+
52+
- client-request:
53+
method: GET
54+
url: /get
55+
version: '1.1'
56+
headers:
57+
fields:
58+
- [Host, www.example.com]
59+
- [uuid, overflow-subjects-request]
60+
61+
server-response:
62+
status: 200
63+
reason: OK
64+
headers:
65+
fields:
66+
- [Content-Length, "3"]
67+
- [Connection, close]
68+
content:
69+
data: "xxx"
70+
71+
proxy-response:
72+
status: 200
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
meta:
18+
version: "1.0"
19+
20+
autest:
21+
description: 'Verify that exactly MAX_SUBJECTS (3 subjects) works without error'
22+
23+
server:
24+
name: 'server-valid'
25+
26+
client:
27+
name: 'client-valid'
28+
29+
ats:
30+
name: 'ts-valid'
31+
32+
process_config:
33+
enable_cache: false
34+
35+
records_config:
36+
proxy.config.acl.subjects: 'PEER,PROXY,PLUGIN'
37+
38+
remap_config:
39+
- from: "http://www.example.com/"
40+
to: "http://127.0.0.1:{SERVER_HTTP_PORT}/"
41+
42+
log_validation:
43+
diags_log:
44+
excludes:
45+
- expression: "Too many ACL subjects were provided"
46+
description: "Should not log error when exactly MAX_SUBJECTS are configured"
47+
48+
sessions:
49+
- transactions:
50+
51+
- client-request:
52+
method: GET
53+
url: /get
54+
version: '1.1'
55+
headers:
56+
fields:
57+
- [Host, www.example.com]
58+
- [uuid, valid-subjects-request]
59+
60+
server-response:
61+
status: 200
62+
reason: OK
63+
headers:
64+
fields:
65+
- [Content-Length, "3"]
66+
- [Connection, close]
67+
content:
68+
data: "xxx"
69+
70+
proxy-response:
71+
status: 200
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
meta:
18+
version: "1.0"
19+
20+
autest:
21+
description: 'Verify slice plugin handles long ETag values without crashing'
22+
23+
server:
24+
name: 'server-long-etag'
25+
process_config:
26+
other_args: '-f "{url}"'
27+
28+
client:
29+
name: 'client-long-etag'
30+
31+
ats:
32+
name: 'ts-long-etag'
33+
34+
process_config:
35+
enable_cache: true
36+
37+
records_config:
38+
proxy.config.diags.debug.enabled: 1
39+
proxy.config.diags.debug.tags: 'slice'
40+
41+
remap_config:
42+
- from: "http://preload/"
43+
to: "http://127.0.0.1:{SERVER_HTTP_PORT}/"
44+
45+
- from: "http://slice/"
46+
to: "http://127.0.0.1:{SERVER_HTTP_PORT}/"
47+
plugins:
48+
- name: "slice.so"
49+
args:
50+
- "--blockbytes-test=11"
51+
52+
# Body is 28 bytes ("lets go surfin now everybody"), block size is 11.
53+
# This means 3 blocks: bytes 0-10, 11-21, 22-27.
54+
# The slice plugin copies the etag from block 0 into data->m_etag
55+
# for validation on blocks 1 and 2. A 4000-char etag exercises the
56+
# null-termination and bounds-checking code path.
57+
58+
sessions:
59+
60+
# Session 1: Preload the asset into cache (bypassing slice plugin)
61+
- transactions:
62+
63+
- client-request:
64+
method: GET
65+
url: /long_etag
66+
version: '1.1'
67+
headers:
68+
fields:
69+
- [Host, preload]
70+
- [uuid, preload-long-etag]
71+
72+
server-response:
73+
status: 200
74+
reason: OK
75+
headers:
76+
fields:
77+
- [Content-Length, "28"]
78+
- [Connection, close]
79+
- [Cache-Control, "max-age=500"]
80+
- [Etag, '"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"']
81+
- [Last-Modified, "Fri, 07 Mar 2025 18:06:58 GMT"]
82+
content:
83+
data: "lets go surfin now everybody"
84+
85+
proxy-response:
86+
status: 200
87+
88+
# Session 2: Fetch through slice plugin (should serve from cache in blocks)
89+
- transactions:
90+
91+
- client-request:
92+
delay: 100ms
93+
method: GET
94+
url: /long_etag
95+
version: '1.1'
96+
headers:
97+
fields:
98+
- [Host, slice]
99+
- [uuid, slice-long-etag]
100+
101+
server-response:
102+
status: 200
103+
reason: OK
104+
headers:
105+
fields:
106+
- [Content-Length, "28"]
107+
content:
108+
data: "lets go surfin now everybody"
109+
110+
proxy-response:
111+
status: 200
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'''
2+
Verify slice plugin handles long ETag values without crashing.
3+
'''
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
# See the License for the specific language governing permissions and
18+
# limitations under the License.
19+
20+
Test.Summary = '''
21+
Verify the slice plugin handles long ETag values (4000 chars) without crashing
22+
when copying between internal buffers during multi-block range requests.
23+
'''
24+
25+
Test.SkipUnless(Condition.PluginExists('slice.so'),)
26+
27+
Test.ATSReplayTest(replay_file="replay/slice_long_etag.replay.yaml")

0 commit comments

Comments
 (0)