Skip to content

Commit 84a7af1

Browse files
agrawrohphlax
authored andcommitted
network: fix crash in getAddressWithPort() when called with a scoped IPv6 address
[CVE-2026-26310](GHSA-3cw6-2j68-868p) Signed-off-by: Rohit Agrawal <rohit.agrawal@databricks.com> Signed-off-by: Ryan Northey <ryan@synca.io>
1 parent 5508210 commit 84a7af1

5 files changed

Lines changed: 95 additions & 5 deletions

File tree

changelogs/current.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ bug_fixes:
4949
change: |
5050
Fixed an off-by-one write in ``JsonEscaper::escapeString()`` that could corrupt the string null terminator
5151
when the input string ends with a control character.
52+
- area: network
53+
change: |
54+
Fixed a crash in ``Utility::getAddressWithPort`` when called with a scoped IPv6 address (e.g., ``fe80::1%eth0``).
5255
5356
removed_config_or_runtime:
5457
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

source/common/network/utility.cc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,18 @@ const std::string& Utility::getIpv6CidrCatchAllAddress() {
316316
Address::InstanceConstSharedPtr Utility::getAddressWithPort(const Address::Instance& address,
317317
uint32_t port) {
318318
switch (address.ip()->version()) {
319-
case Address::IpVersion::v4:
320-
return std::make_shared<Address::Ipv4Instance>(address.ip()->addressAsString(), port);
321-
case Address::IpVersion::v6:
322-
return std::make_shared<Address::Ipv6Instance>(address.ip()->addressAsString(), port);
319+
case Address::IpVersion::v4: {
320+
// Copy the sockaddr and update the port to preserve all address properties.
321+
sockaddr_in addr = *reinterpret_cast<const sockaddr_in*>(address.sockAddr());
322+
addr.sin_port = htons(static_cast<uint16_t>(port));
323+
return std::make_shared<Address::Ipv4Instance>(&addr);
324+
}
325+
case Address::IpVersion::v6: {
326+
// Copy the sockaddr and update the port to preserve all address properties including scope ID.
327+
sockaddr_in6 addr = *reinterpret_cast<const sockaddr_in6*>(address.sockAddr());
328+
addr.sin6_port = htons(static_cast<uint16_t>(port));
329+
return std::make_shared<Address::Ipv6Instance>(addr, address.ip()->ipv6()->v6only());
330+
}
323331
}
324332
PANIC("not handled");
325333
}

test/common/network/utility_test.cc

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,49 @@ TEST(NetworkUtility, ParseInternetAddressAndPort) {
252252
EXPECT_EQ("[::1]:0", Utility::parseInternetAddressAndPortNoThrow("[::1]:0")->asString());
253253
}
254254

255+
TEST(NetworkUtility, GetAddressWithPort) {
256+
// Test basic IPv4.
257+
auto addr_v4 = std::make_shared<Address::Ipv4Instance>("1.2.3.4", 80);
258+
auto addr_v4_new_port = Utility::getAddressWithPort(*addr_v4, 8080);
259+
EXPECT_EQ("1.2.3.4:8080", addr_v4_new_port->asString());
260+
EXPECT_EQ(Address::IpVersion::v4, addr_v4_new_port->ip()->version());
261+
262+
// Test basic IPv6.
263+
auto addr_v6 = std::make_shared<Address::Ipv6Instance>("::1", 80);
264+
auto addr_v6_new_port = Utility::getAddressWithPort(*addr_v6, 8080);
265+
EXPECT_EQ("[::1]:8080", addr_v6_new_port->asString());
266+
EXPECT_EQ(Address::IpVersion::v6, addr_v6_new_port->ip()->version());
267+
268+
// Test IPv6 with scope ID.
269+
sockaddr_in6 scoped_addr;
270+
memset(&scoped_addr, 0, sizeof(scoped_addr));
271+
scoped_addr.sin6_family = AF_INET6;
272+
EXPECT_EQ(1, inet_pton(AF_INET6, "fe80::1", &scoped_addr.sin6_addr));
273+
scoped_addr.sin6_port = htons(80);
274+
scoped_addr.sin6_scope_id = 5;
275+
276+
auto addr_v6_scoped = std::make_shared<Address::Ipv6Instance>(scoped_addr);
277+
EXPECT_EQ("[fe80::1%5]:80", addr_v6_scoped->asString());
278+
EXPECT_EQ(5u, addr_v6_scoped->ip()->ipv6()->scopeId());
279+
280+
auto addr_v6_scoped_new_port = Utility::getAddressWithPort(*addr_v6_scoped, 8080);
281+
EXPECT_EQ("[fe80::1%5]:8080", addr_v6_scoped_new_port->asString());
282+
EXPECT_EQ(Address::IpVersion::v6, addr_v6_scoped_new_port->ip()->version());
283+
EXPECT_EQ(5u, addr_v6_scoped_new_port->ip()->ipv6()->scopeId());
284+
EXPECT_EQ(8080u, addr_v6_scoped_new_port->ip()->port());
285+
286+
// Verify v6only is preserved.
287+
sockaddr_in6 v6only_addr;
288+
memset(&v6only_addr, 0, sizeof(v6only_addr));
289+
v6only_addr.sin6_family = AF_INET6;
290+
EXPECT_EQ(1, inet_pton(AF_INET6, "::1", &v6only_addr.sin6_addr));
291+
v6only_addr.sin6_port = htons(80);
292+
auto addr_v6only_false = std::make_shared<Address::Ipv6Instance>(v6only_addr, false);
293+
EXPECT_FALSE(addr_v6only_false->ip()->ipv6()->v6only());
294+
auto addr_v6only_false_new_port = Utility::getAddressWithPort(*addr_v6only_false, 8080);
295+
EXPECT_FALSE(addr_v6only_false_new_port->ip()->ipv6()->v6only());
296+
}
297+
255298
class NetworkUtilityGetLocalAddress : public testing::TestWithParam<Address::IpVersion> {};
256299

257300
INSTANTIATE_TEST_SUITE_P(IpVersions, NetworkUtilityGetLocalAddress,
@@ -706,7 +749,7 @@ TEST(ResolvedUdpSocketConfig, Warning) {
706749
ResolvedUdpSocketConfig resolved_config(envoy::config::core::v3::UdpSocketConfig(), true));
707750
}
708751

709-
#ifndef WIN32
752+
#if defined(__linux__)
710753
TEST(PacketLoss, LossTest) {
711754
class ZeroTimeSource : public TimeSource {
712755
public:

test/extensions/filters/common/original_src/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ envoy_cc_test(
1414
rbe_pool = "6gig",
1515
deps = [
1616
"//source/common/network:address_lib",
17+
"//source/common/network:utility_lib",
1718
"//source/extensions/filters/common/original_src:original_src_socket_option_lib",
19+
"//source/extensions/filters/common/original_src:socket_option_factory_lib",
1820
"//test/mocks:common_lib",
1921
"//test/mocks/network:network_mocks",
2022
"//test/test_common:printers_lib",

test/extensions/filters/common/original_src/original_src_socket_option_test.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "envoy/config/core/v3/base.pb.h"
22
#include "envoy/network/address.h"
33

4+
#include "source/common/network/address_impl.h"
45
#include "source/common/network/utility.h"
56
#include "source/extensions/filters/common/original_src/original_src_socket_option.h"
7+
#include "source/extensions/filters/common/original_src/socket_option_factory.h"
68

79
#include "test/mocks/common.h"
810
#include "test/mocks/network/mocks.h"
@@ -104,6 +106,38 @@ TEST_F(OriginalSrcSocketOptionTest, TestOptionDetailsNotSupported) {
104106
EXPECT_FALSE(details.has_value());
105107
}
106108

109+
// Test that buildOriginalSrcOptions works with scoped IPv6 addresses.
110+
TEST(SocketOptionFactoryTest, BuildOriginalSrcOptionsWithScopedIpv6) {
111+
// Create a scoped IPv6 address.
112+
sockaddr_in6 scoped_addr;
113+
memset(&scoped_addr, 0, sizeof(scoped_addr));
114+
scoped_addr.sin6_family = AF_INET6;
115+
EXPECT_EQ(1, inet_pton(AF_INET6, "fe80::1", &scoped_addr.sin6_addr));
116+
scoped_addr.sin6_port = htons(12345);
117+
scoped_addr.sin6_scope_id = 3;
118+
119+
auto source_address = std::make_shared<Network::Address::Ipv6Instance>(scoped_addr);
120+
EXPECT_EQ("[fe80::1%3]:12345", source_address->asString());
121+
EXPECT_EQ(3u, source_address->ip()->ipv6()->scopeId());
122+
123+
auto options = buildOriginalSrcOptions(source_address, 0);
124+
125+
// Verify options were created successfully.
126+
EXPECT_NE(nullptr, options);
127+
EXPECT_FALSE(options->empty());
128+
129+
// Verify the address in the socket option has port set to 0 but preserves the scope ID.
130+
// The first option should be the OriginalSrcSocketOption.
131+
NiceMock<Network::MockConnectionSocket> socket;
132+
(*options)[0]->setOption(socket, envoy::config::core::v3::SocketOption::STATE_PREBIND);
133+
134+
auto local_address = socket.connection_info_provider_->localAddress();
135+
EXPECT_EQ(Network::Address::IpVersion::v6, local_address->ip()->version());
136+
EXPECT_EQ(0u, local_address->ip()->port());
137+
EXPECT_EQ(3u, local_address->ip()->ipv6()->scopeId());
138+
EXPECT_EQ("[fe80::1%3]:0", local_address->asString());
139+
}
140+
107141
} // namespace
108142
} // namespace OriginalSrc
109143
} // namespace Common

0 commit comments

Comments
 (0)