NetBSD Problem Report #60669
From riastradh@netbsd.org Sat Aug 29 15:58:20 2026
Return-Path: <riastradh@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits)
client-signature RSA-PSS (2048 bits))
(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id 56C5A1A923C
for <gnats-bugs@gnats.NetBSD.org>; Sat, 29 Aug 2026 15:58:20 +0000 (UTC)
Message-Id: <20260829155819.249131A923E@mollari.NetBSD.org>
Date: Sat, 29 Aug 2026 15:58:19 +0000 (UTC)
From: riastradh@netbsd.org
Reply-To: riastradh@netbsd.org
To: gnats-bugs@NetBSD.org
Subject: netipsec key_sp2msg buffer overrun
X-Send-Pr-Version: 3.95
X-From4GNATS: "riastradh@netbsd.org via gnats" <gnats-admin@NetBSD.org>
>Number: 60669
>Notify-List: yamaguchi, knakahara, ozaki-r, ohishi@iij.ad.jp
>Category: kern
>Synopsis: netipsec key_sp2msg buffer overrun
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: needs-pullups
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Aug 29 16:00:00 +0000 2026
>Closed-Date:
>Last-Modified: Fri Oct 02 16:15:02 +0000 2026
>Originator: Taylor R Campbell
>Release: current, 11, 10, 9, ...
>Organization:
The NetIPSEC Foundanoverrun, Inc.
>Environment:
>Description:
key_sp2msg advances the output pointer by the wrong length:
2115 p += sizeof(*xisr);
2116 memcpy(p, &isr->saidx.src, isr->saidx.src.sa.sa_len);
2117 p += isr->saidx.src.sa.sa_len;
2118 memcpy(p, &isr->saidx.dst, isr->saidx.dst.sa.sa_len);
2119 p += isr->saidx.src.sa.sa_len;
https://nxr.netbsd.org/xref/src/sys/netipsec/key.c?r=1.286#2115
First memcpy and += use src.sa.sa_len.
Second memcpy uses dst.sa.sa_len, but second += uses
src.sa.sa_len instead of dst.sa.sa_len.
Fixed in FreeBSD revision 308917:
https://svnweb.freebsd.org/base?view=revision&revision=308917
Forwarded to us by Franz Bettag.
Also: It looks like the struct sadb_x_ipsecrequest field
alignment within the message buffer is not maintained except
possibly by accident.
>How-To-Repeat:
1. Set an IPv6 IPsec policy with setsockopt(IPV6_IPSEC_POLICY)
having source and destination socket addresses with
different lengths.
2. Get the IPv6 IPsec policy with
getsockopt2(IPV6_IPSEC_POLICY), using getsockopt2 so that
you can specify inbound/outbound and it won't just fail
with EINVAL like getsockopt would.
>Fix:
s/src/dst/ on the right line.
>Release-Note:
>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: security-team@NetBSD.org, ozaki-r@NetBSD.org, knakahara@NetBSD.org,
yamaguchi@NetBSD.org
Subject: Re: kern/60669: netipsec key_sp2msg buffer overrun
Date: Mon, 31 Aug 2026 16:26:20 +0000
This is a multi-part message in MIME format.
--=_cEFiPfdk98xHDMxf5dg4ZFdv6SEdn8RA
Patch attached to fix and add tests for setsockopt/getsockopt2
IPV6_IPSEC_POLICY buffer overrun. Review welcome!
Questions: Does it make sense to have mismatched src/dst address
families in a single IPsec policy? Does it make sense to have a
number of addresses in a policy other than zero or two? If not, we
can block those various cases earlier and update the tests.
Should maybe do similarly for IPv4 but there's currently no getsockopt
or getsockopt2 path for it anyway -- it's under #if 0:
1392 #if 0 /* defined(IPSEC) */
1393 case IP_IPSEC_POLICY:
1394 {
1395 struct mbuf *m = NULL;
1396
1397 /* XXX this will return EINVAL as sopt is empty */
1398 error = ipsec_get_policy(inp, sopt->sopt_data,
1399 sopt->sopt_size, &m);
1400 if (error == 0)
1401 error = sockopt_setmbuf(sopt, m);
1402 break;
1403 }
1404 #endif /*IPSEC*/
https://nxr.NetBSD.org/xref/src/sys/netinet/ip_output.c?r=1.330#1392
The IPv6 version has the same comment but it is wrong because
getsockopt2 does allow the caller to pass input through sopt:
1934 #if defined(IPSEC)
1935 case IPV6_IPSEC_POLICY:
1936 if (ipsec_used) {
1937 struct mbuf *m = NULL;
1938
1939 /*
1940 * XXX: this will return EINVAL as sopt is
1941 * empty
1942 */
1943 error = ipsec_get_policy(inp, sopt->sopt_data,
1944 sopt->sopt_size, &m);
1945 if (!error)
1946 error = sockopt_setmbuf(sopt, m);
1947 } else
1948 error = ENOPROTOOPT;
1949 break;
1950 #endif /* IPSEC */
https://nxr.NetBSD.org/xref/src/sys/netinet6/ip6_output.c?r=1.235#1934
--=_cEFiPfdk98xHDMxf5dg4ZFdv6SEdn8RA
Content-Type: text/plain; charset="ISO-8859-1"; name="pr60669-sp2msgoverrun"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="pr60669-sp2msgoverrun.patch"
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1788120878 0
# Sun Aug 30 20:14:38 2026 +0000
# Branch trunk
# Node ID f1ab0b6f9d43f3e6660ad717571d357c6c195b6e
# Parent 4fbd288ac1da9e7d9592ddecf04f7513e244152f
# EXP-Topic riastradh-pr60669-sp2msgoverrun
ipsec: Fix sizing and alignment in IPsec policy import and export.
Note: I'm not sure whether it makes sense to have mismatched src and
dst address families -- I doubt it, since userland libipsec rejects
it in ipsec_set_policy(3). I'm also not sure whether it makes sense
to have numbers of addresses other than 0 or 2. But those can be
addressed in a separate commit.
PR kern/60669: netipsec key_sp2msg buffer overrun
diff -r 4fbd288ac1da -r f1ab0b6f9d43 sys/netipsec/key.c
--- a/sys/netipsec/key.c Mon Aug 10 13:36:10 2026 +0000
+++ b/sys/netipsec/key.c Sun Aug 30 20:14:38 2026 +0000
@@ -1873,7 +1873,7 @@ static struct secpolicy *
struct ipsecrequest **p_isr =3D &newsp->req;
=20
/* validity check */
- if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0)) {
+ if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0) + sizeof(*xisr)) {
IPSECLOG(LOG_DEBUG, "Invalid msg length.\n");
*error =3D EINVAL;
goto free_exit;
@@ -1882,9 +1882,11 @@ static struct secpolicy *
tlen =3D PFKEY_EXTLEN(xpl0) - sizeof(*xpl0);
xisr =3D (const struct sadb_x_ipsecrequest *)(xpl0 + 1);
=20
- while (tlen > 0) {
+ while (tlen > sizeof(*xisr)) {
/* length check */
- if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
+ if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr) ||
+ xisr->sadb_x_ipsecrequest_len > tlen ||
+ xisr->sadb_x_ipsecrequest_len % 8 !=3D 0) {
IPSECLOG(LOG_DEBUG, "invalid ipsecrequest length.\n");
*error =3D EINVAL;
goto free_exit;
@@ -1991,12 +1993,21 @@ static struct secpolicy *
* This behavior is used by NAT-T enabled ipsecif(4).
*/
if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
+ size_t resid =3D xisr->sadb_x_ipsecrequest_len;
const struct sockaddr *paddr;
=20
+ if (sizeof(*paddr) > resid) {
+ IPSECLOG(LOG_DEBUG, "invalid request "
+ "address length.\n");
+ *error =3D EINVAL;
+ goto free_exit;
+ }
paddr =3D (const struct sockaddr *)(xisr + 1);
=20
/* validity check */
- if (paddr->sa_len > sizeof((*p_isr)->saidx.src)) {
+ if (paddr->sa_len < sizeof(*paddr) ||
+ paddr->sa_len > resid ||
+ paddr->sa_len > sizeof((*p_isr)->saidx.src)) {
IPSECLOG(LOG_DEBUG, "invalid request "
"address length.\n");
*error =3D EINVAL;
@@ -2004,11 +2015,20 @@ static struct secpolicy *
}
memcpy(&(*p_isr)->saidx.src, paddr, paddr->sa_len);
=20
+ resid -=3D paddr->sa_len;
+ if (sizeof(*paddr) > resid) {
+ IPSECLOG(LOG_DEBUG, "invalid request "
+ "address length 2, %zu > %zu.\n", sizeof(*paddr), resid);
+ *error =3D EINVAL;
+ goto free_exit;
+ }
paddr =3D (const struct sockaddr *)((const char *)paddr
+ paddr->sa_len);
=20
/* validity check */
- if (paddr->sa_len > sizeof((*p_isr)->saidx.dst)) {
+ if (paddr->sa_len < sizeof(*paddr) ||
+ paddr->sa_len > resid ||
+ paddr->sa_len > sizeof((*p_isr)->saidx.dst)) {
IPSECLOG(LOG_DEBUG, "invalid request "
"address length.\n");
*error =3D EINVAL;
@@ -2024,11 +2044,7 @@ static struct secpolicy *
tlen -=3D xisr->sadb_x_ipsecrequest_len;
=20
/* validity check */
- if (tlen < 0) {
- IPSECLOG(LOG_DEBUG, "becoming tlen < 0.\n");
- *error =3D EINVAL;
- goto free_exit;
- }
+ KASSERT(tlen >=3D 0);
=20
xisr =3D (const struct sadb_x_ipsecrequest *)((const char *)xisr +
xisr->sadb_x_ipsecrequest_len);
@@ -2071,7 +2087,7 @@ key_sp2msg(const struct secpolicy *sp, i
{
struct sadb_x_policy *xpl;
int tlen;
- char *p;
+ char *p0, *p;
struct mbuf *m;
=20
KASSERT(sp !=3D NULL);
@@ -2086,6 +2102,9 @@ key_sp2msg(const struct secpolicy *sp, i
=20
m->m_len =3D tlen;
m->m_next =3D NULL;
+ p0 =3D m->m_data;
+ KASSERT(sizeof(struct sadb_x_policy) <=3D tlen);
+ KASSERT((uintptr_t)p0 % _Alignof(struct sadb_x_policy) =3D=3D 0);
xpl =3D mtod(m, struct sadb_x_policy *);
memset(xpl, 0, tlen);
=20
@@ -2096,7 +2115,9 @@ key_sp2msg(const struct secpolicy *sp, i
xpl->sadb_x_policy_id =3D sp->id;
if (sp->origin =3D=3D IPSEC_SPORIGIN_KERNEL)
xpl->sadb_x_policy_flags |=3D IPSEC_POLICY_FLAG_ORIGIN_KERNEL;
+ CTASSERT(sizeof(*xpl) % 8 =3D=3D 0);
p =3D (char *)xpl + sizeof(*xpl);
+ KASSERT((p - p0) % 8 =3D=3D 0);
=20
/* if is the policy for ipsec ? */
if (sp->policy =3D=3D IPSEC_POLICY_IPSEC) {
@@ -2104,24 +2125,40 @@ key_sp2msg(const struct secpolicy *sp, i
struct ipsecrequest *isr;
=20
for (isr =3D sp->req; isr !=3D NULL; isr =3D isr->next) {
-
+ const unsigned len =3D sizeof(*xisr)
+ + isr->saidx.src.sa.sa_len
+ + isr->saidx.dst.sa.sa_len;
+ const unsigned pad =3D PFKEY_ALIGN8(len) - len;
+
+ KASSERT((p - p0) % 8 =3D=3D 0);
+ KASSERT(PFKEY_ALIGN8(len) <=3D tlen - (p - p0));
+
+ KASSERT(sizeof(*xisr) <=3D tlen - (p - p0));
xisr =3D (struct sadb_x_ipsecrequest *)p;
=20
xisr->sadb_x_ipsecrequest_proto =3D isr->saidx.proto;
xisr->sadb_x_ipsecrequest_mode =3D isr->saidx.mode;
xisr->sadb_x_ipsecrequest_level =3D isr->level;
xisr->sadb_x_ipsecrequest_reqid =3D isr->saidx.reqid;
-
p +=3D sizeof(*xisr);
+
+ KASSERT(isr->saidx.src.sa.sa_len <=3D tlen - (p - p0));
memcpy(p, &isr->saidx.src, isr->saidx.src.sa.sa_len);
p +=3D isr->saidx.src.sa.sa_len;
+
+ KASSERT(isr->saidx.dst.sa.sa_len <=3D tlen - (p - p0));
memcpy(p, &isr->saidx.dst, isr->saidx.dst.sa.sa_len);
- p +=3D isr->saidx.src.sa.sa_len;
-
- xisr->sadb_x_ipsecrequest_len =3D
- PFKEY_ALIGN8(sizeof(*xisr)
- + isr->saidx.src.sa.sa_len
- + isr->saidx.dst.sa.sa_len);
+ p +=3D isr->saidx.dst.sa.sa_len;
+
+ KASSERT(pad <=3D tlen - (p - p0));
+ memset(p, 0, pad);
+ p +=3D pad;
+
+ KASSERT(p =3D=3D (char *)xisr + PFKEY_ALIGN8(len));
+ KASSERT((p - p0) % 8 =3D=3D 0);
+ xisr->sadb_x_ipsecrequest_len =3D PFKEY_ALIGN8(len);
+ KASSERT(p =3D=3D (char *)xisr +
+ xisr->sadb_x_ipsecrequest_len);
}
}
=20
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1788121188 0
# Sun Aug 30 20:19:48 2026 +0000
# Branch trunk
# Node ID e10c5a00f1259f9573a35186ed44c4693099f97f
# Parent f1ab0b6f9d43f3e6660ad717571d357c6c195b6e
# EXP-Topic riastradh-pr60669-sp2msgoverrun
ipsec: Test edge cases of setting and getting IPsec policies.
PR kern/60669: netipsec key_sp2msg buffer overrun
diff -r f1ab0b6f9d43 -r e10c5a00f125 distrib/sets/lists/debug/mi
--- a/distrib/sets/lists/debug/mi Sun Aug 30 20:14:38 2026 +0000
+++ b/distrib/sets/lists/debug/mi Sun Aug 30 20:19:48 2026 +0000
@@ -2528,6 +2528,7 @@
./usr/libdata/debug/usr/tests/net/inpcb/broadcast_bind.debug tests-net-deb=
ug debug,atf,rump,compattestfile
./usr/libdata/debug/usr/tests/net/inpcb/inpcb_bind.debug tests-net-debug =
debug,atf,rump,compattestfile
./usr/libdata/debug/usr/tests/net/ipsec/natt_terminator.debug tests-net-de=
bug debug,atf,rump
+./usr/libdata/debug/usr/tests/net/ipsec/t_ipsec_policy.debug tests-net-deb=
ug debug,atf,rump
./usr/libdata/debug/usr/tests/net/mcast/mcast.debug tests-net-debug debu=
g,atf,rump
./usr/libdata/debug/usr/tests/net/mcast/t_mcast.debug tests-obsolete deb=
ug,atf,rump,obsolete
./usr/libdata/debug/usr/tests/net/net/t_bind.debug tests-net-debug debug=
,atf,compattestfile
diff -r f1ab0b6f9d43 -r e10c5a00f125 distrib/sets/lists/tests/mi
--- a/distrib/sets/lists/tests/mi Sun Aug 30 20:14:38 2026 +0000
+++ b/distrib/sets/lists/tests/mi Sun Aug 30 20:19:48 2026 +0000
@@ -4607,6 +4607,7 @@
./usr/tests/net/ipsec/t_ipsec_l2tp tests-net-tests atf,rump
./usr/tests/net/ipsec/t_ipsec_misc tests-net-tests atf,rump
./usr/tests/net/ipsec/t_ipsec_natt tests-net-tests atf,rump
+./usr/tests/net/ipsec/t_ipsec_policy tests-net-tests atf,rump
./usr/tests/net/ipsec/t_ipsec_sockopt tests-net-tests atf,rump
./usr/tests/net/ipsec/t_ipsec_spflags tests-net-tests atf,rump
./usr/tests/net/ipsec/t_ipsec_sysctl tests-net-tests atf,rump
diff -r f1ab0b6f9d43 -r e10c5a00f125 tests/net/ipsec/Makefile
--- a/tests/net/ipsec/Makefile Sun Aug 30 20:14:38 2026 +0000
+++ b/tests/net/ipsec/Makefile Sun Aug 30 20:19:48 2026 +0000
@@ -13,6 +13,8 @@ TESTS_SH_SRC_t_${name}=3D ../net_common.sh
t_${name}.sh
.endfor
=20
+TESTS_C+=3D t_ipsec_policy
+
PROGS=3D natt_terminator
MAN.natt_terminator=3D # empty
BINDIR.natt_terminator=3D ${TESTSDIR}
diff -r f1ab0b6f9d43 -r e10c5a00f125 tests/net/ipsec/t_ipsec_policy.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/net/ipsec/t_ipsec_policy.c Sun Aug 30 20:19:48 2026 +0000
@@ -0,0 +1,354 @@
+/* $NetBSD$ */
+
+/*
+ * Copyright (c) 2026 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTO=
RS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIM=
ITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICU=
LAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTO=
RS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF =
THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <arpa/inet.h>
+#include <net/pfkeyv2.h>
+#include <netinet/in.h>
+#include <netipsec/ipsec.h>
+
+#include <atf-c.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "h_macros.h"
+
+static void
+hexdump(const char *title, const void *buf, size_t len)
+{
+ const uint8_t *p =3D buf;
+ size_t i;
+
+ printf("# %s (%zu bytes)\n", title, len);
+ for (i =3D 0; i < len; i++) {
+ if ((i % 8) =3D=3D 0)
+ printf(" ");
+ printf(" %02hhx", p[i]);
+ if ((i % 16) =3D=3D 15)
+ printf("\n");
+ }
+ if (i % 16)
+ printf("\n");
+}
+
+static void *
+append(void **bufp, size_t *buflenp, size_t addlen)
+{
+ void *p;
+
+ if (addlen =3D=3D 0)
+ return NULL;
+
+ ATF_REQUIRE(addlen < SIZE_MAX - *buflenp);
+ RZ(reallocarr(bufp, *buflenp + addlen, 1));
+ p =3D (char *)*bufp + *buflenp;
+ memset(p, 0, addlen);
+ *buflenp +=3D addlen;
+ return p;
+}
+
+static void
+test_ipsec_policy_roundtrip(int exp_error, uint8_t dir,
+ const struct sockaddr *src, const struct sockaddr *dst)
+{
+ enum { guardlen =3D 8 };
+ void *buf =3D NULL;
+ size_t len =3D 0;
+ struct sadb_x_policy *xpl, *xpl1;
+ size_t xpl_start, xpl1_start;
+ size_t reqlen;
+ void *copybuf =3D NULL;
+ size_t copylen =3D 0;
+ uint8_t guardbyte[2] =3D {0x5a, 0x3c};
+ size_t guard_start[2];
+ socklen_t optlen;
+ unsigned i;
+ int s;
+
+ xpl_start =3D len;
+ xpl =3D append(&buf, &len, sizeof(*xpl));
+ xpl->sadb_x_policy_exttype =3D SADB_X_EXT_POLICY;
+ xpl->sadb_x_policy_type =3D IPSEC_POLICY_IPSEC;
+ xpl->sadb_x_policy_dir =3D dir;
+
+ for (i =3D 0; i < 2; i++) {
+ const size_t xisr_start =3D len;
+ struct sadb_x_ipsecrequest *xisr;
+
+ xisr =3D append(&buf, &len, sizeof(*xisr));
+ xisr->sadb_x_ipsecrequest_proto =3D IPPROTO_ESP;
+ xisr->sadb_x_ipsecrequest_mode =3D IPSEC_MODE_TUNNEL;
+ xisr->sadb_x_ipsecrequest_level =3D IPSEC_LEVEL_REQUIRE;
+ xisr->sadb_x_ipsecrequest_reqid =3D 0;
+
+ memcpy(append(&buf, &len, src->sa_len), src, src->sa_len);
+ memcpy(append(&buf, &len, dst->sa_len), dst, dst->sa_len);
+
+ /* pad */
+ const size_t npad =3D PFKEY_ALIGN8(len - xisr_start) -
+ (len - xisr_start);
+ (void)append(&buf, &len, npad);
+
+ ATF_REQUIRE(PFKEY_ALIGN8(len - xisr_start) =3D=3D len - xisr_start);
+ xisr =3D (void *)((char *)buf + xisr_start);
+ xisr->sadb_x_ipsecrequest_len =3D len - xisr_start;
+ }
+
+ ATF_REQUIRE(PFKEY_ALIGN8(len - xpl_start) =3D=3D len - xpl_start);
+ xpl =3D (void *)((char *)buf + xpl_start);
+ xpl->sadb_x_policy_len =3D PFKEY_UNIT64(len - xpl_start);
+
+ reqlen =3D len - xpl_start;
+
+ RL(s =3D socket(AF_INET6, SOCK_DGRAM, 0));
+ if (exp_error) {
+ ATF_CHECK_ERRNO(exp_error,
+ setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl, reqlen)
+ =3D=3D -1);
+ goto out;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl, reqlen)
+ =3D=3D -1) {
+ int error =3D errno;
+
+ atf_tc_fail_nonfatal("setsockopt(IPV6_IPSEC_POLICY): %d (%s)",
+ error, strerror(errno));
+ goto out;
+ }
+
+ guard_start[0] =3D copylen;
+ memset(append(©buf, ©len, guardlen), guardbyte[0], guardlen);
+
+ xpl1_start =3D copylen;
+ (void *)append(©buf, ©len, reqlen);
+
+ guard_start[1] =3D copylen;
+ memset(append(©buf, ©len, guardlen), guardbyte[1], guardlen);
+
+ xpl =3D (void *)((char *)buf + xpl_start);
+ xpl1 =3D (void *)((char *)copybuf + xpl1_start);
+ memcpy(xpl1, xpl, reqlen);
+
+ optlen =3D reqlen;
+ ATF_CHECK_ERRNO(EINVAL,
+ getsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl1, &optlen));
+
+ optlen =3D reqlen;
+ if (getsockopt2(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl1, &optlen)
+ =3D=3D -1) {
+ int error =3D errno;
+
+ atf_tc_fail_nonfatal("getsockopt2(IPV6_IPSEC_POLICY): %d (%s)",
+ error, strerror(errno));
+ goto out;
+ }
+ ATF_CHECK_EQ_MSG(reqlen, optlen, "reqlen=3D%zu optlen=3D%zu",
+ (size_t)reqlen, (size_t)optlen);
+
+ if (reqlen !=3D optlen || memcmp(xpl, xpl1, len) !=3D 0) {
+ hexdump("before", xpl, reqlen);
+ hexdump("after", xpl1, optlen);
+ fflush(stdout);
+ atf_tc_fail_nonfatal("mismatch");
+ }
+
+ for (i =3D 0; i < __arraycount(guard_start); i++) {
+ const char *guard =3D (char *)copybuf + guard_start[i];
+ size_t j;
+
+ for (j =3D 0; j < guardlen; j++) {
+ if (guard[j] !=3D guardbyte[i]) {
+ char title[8];
+
+ snprintf(title, sizeof(title), "guard %u", i);
+ hexdump(title, guard, guardlen);
+ fflush(stdout);
+ atf_tc_fail_nonfatal("guard %u overwritten",
+ i);
+ break;
+ }
+ }
+ }
+
+out: RL(close(s));
+}
+
+ATF_TC(pr60669);
+ATF_TC_HEAD(pr60669, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test IPV6_IPSEC_POLICY round-trip with"
+ " v4 src and v4 dst");
+}
+ATF_TC_BODY(pr60669, tc)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } v4[2] =3D {
+ [0] =3D {
+ .sin =3D {
+ .sin_len =3D sizeof(v4[0].sin),
+ .sin_family =3D AF_INET,
+ .sin_port =3D 0,
+ }
+ },
+ [1] =3D {
+ .sin =3D {
+ .sin_len =3D sizeof(v4[1].sin),
+ .sin_family =3D AF_INET,
+ .sin_port =3D 0,
+ }
+ },
+ };
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in6 sin6;
+ } v6[2] =3D {
+ [0] =3D {
+ .sin6 =3D {
+ .sin6_len =3D sizeof(v6[0].sin6),
+ .sin6_family =3D AF_INET6,
+ .sin6_port =3D 0,
+ }
+ },
+ [1] =3D {
+ .sin6 =3D {
+ .sin6_len =3D sizeof(v6[1].sin6),
+ .sin6_family =3D AF_INET6,
+ .sin6_port =3D 0,
+ }
+ },
+ };
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } un[2] =3D {
+ [0] =3D {
+ .sun =3D {
+ .sun_len =3D -1,
+ .sun_family =3D AF_LOCAL,
+ .sun_path =3D "/socket0",
+ }
+ },
+ [1] =3D {
+ .sun =3D {
+ .sun_len =3D -1,
+ .sun_family =3D AF_LOCAL,
+ .sun_path =3D "/socket1",
+ }
+ },
+ };
+ struct sockaddr sa[2] =3D {
+ [0] =3D { .sa_len =3D sizeof(sa[0]), .sa_family =3D AF_UNSPEC },
+ [1] =3D { .sa_len =3D sizeof(sa[1]), .sa_family =3D AF_UNSPEC },
+ };
+ struct sockaddr sa0[2] =3D {
+ [0] =3D { .sa_len =3D 0, .sa_family =3D AF_UNSPEC },
+ [1] =3D { .sa_len =3D 0, .sa_family =3D AF_UNSPEC },
+ };
+ const struct {
+ struct sockaddr *src;
+ struct sockaddr *dst;
+ int expected_error;
+ const char *xfail;
+ } C[] =3D {
+ [0] =3D { &v4[0].sa, &v4[1].sa, 0, NULL },
+ [1] =3D { &v4[0].sa, &v6[1].sa, 0, NULL },
+ [2] =3D { &v4[0].sa, &un[1].sa, EINVAL, NULL },
+ [3] =3D { &v4[0].sa, &sa[1], 0, NULL },
+ [4] =3D { &v4[0].sa, &sa0[1], EINVAL, NULL },
+
+ [5] =3D { &v6[0].sa, &v4[1].sa, 0, NULL },
+ [6] =3D { &v6[0].sa, &v6[1].sa, 0, NULL },
+ [7] =3D { &v6[0].sa, &un[1].sa, EINVAL, NULL },
+ [8] =3D { &v6[0].sa, &sa[1], 0, NULL },
+ [9] =3D { &v6[0].sa, &sa0[1], EINVAL, NULL },
+
+ [10] =3D { &un[0].sa, &v4[1].sa, EINVAL, NULL },
+ [11] =3D { &un[0].sa, &v6[1].sa, EINVAL, NULL },
+ [12] =3D { &un[0].sa, &un[1].sa, EINVAL, NULL },
+ [13] =3D { &un[0].sa, &sa[1], EINVAL, NULL },
+ [14] =3D { &un[0].sa, &sa0[1], EINVAL, NULL },
+
+ [15] =3D { &sa[0], &v4[1].sa, 0, NULL },
+ [16] =3D { &sa[0], &v6[1].sa, 0, NULL },
+ [17] =3D { &sa[0], &un[1].sa, EINVAL, NULL },
+ [18] =3D { &sa[0], &sa[1], 0, NULL },
+ [19] =3D { &sa[0], &sa0[1], EINVAL, NULL },
+
+ /*
+ * sa0 as the src one doesn't even make sense to test,
+ * because sa_len won't even advance past it, so it
+ * will be the same as testing a nonempty src and sa0
+ * dst above.
+ */
+ };
+ unsigned i;
+
+ RL(inet_pton(AF_INET, "192.0.2.42", &v4[0].sin.sin_addr));
+ RL(inet_pton(AF_INET, "192.51.100.54", &v4[1].sin.sin_addr));
+
+ RL(inet_pton(AF_INET6, "2001:db8:1::1", &v6[0].sin6.sin6_addr));
+ RL(inet_pton(AF_INET6, "2001:db8:2::1", &v6[1].sin6.sin6_addr));
+
+ un[0].sun.sun_len =3D SUN_LEN(&un[0].sun);
+ un[1].sun.sun_len =3D SUN_LEN(&un[1].sun);
+
+ for (i =3D 0; i < __arraycount(C); i++) {
+ const char *const dirname[2] =3D { "inbound", "outbound" };
+ const unsigned dir[2] =3D
+ { IPSEC_DIR_INBOUND, IPSEC_DIR_OUTBOUND };
+ unsigned j;
+
+ for (j =3D 0; j < 2; j++) {
+ printf("# case %u %s\n", i, dirname[j]);
+ fflush(stdout);
+ if (C[i].xfail)
+ atf_tc_expect_fail("%s", C[i].xfail);
+ test_ipsec_policy_roundtrip(C[i].expected_error,
+ dir[j], C[i].src, C[i].dst);
+ if (C[i].xfail)
+ atf_tc_expect_pass();
+ }
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, pr60669);
+ return atf_no_error();
+}
+
--=_cEFiPfdk98xHDMxf5dg4ZFdv6SEdn8RA--
From: Kengo Nakahara <k-nakahara@iij.ad.jp>
To: Taylor R Campbell <riastradh@NetBSD.org>, gnats-bugs@NetBSD.org,
netbsd-bugs@NetBSD.org
Cc: security-team@NetBSD.org, ozaki-r@NetBSD.org, knakahara@NetBSD.org,
yamaguchi@NetBSD.org, ohishi@iij.ad.jp
Subject: Re: kern/60669: netipsec key_sp2msg buffer overrun
Date: Mon, 7 Sep 2026 12:16:19 +0900
# Add CC to ohishi who is IIJ IPsec co-worker
On 2026/09/01 1:26, Taylor R Campbell wrote:
> Patch attached to fix and add tests for setsockopt/getsockopt2
> IPV6_IPSEC_POLICY buffer overrun. Review welcome!
>
> Questions: Does it make sense to have mismatched src/dst address
> families in a single IPsec policy? Does it make sense to have a
> number of addresses in a policy other than zero or two? If not, we
> can block those various cases earlier and update the tests.
It does not make sense neither of them. So, I think those cases
can be blocked.
> Should maybe do similarly for IPv4 but there's currently no getsockopt
> or getsockopt2 path for it anyway -- it's under #if 0:
>
> 1392 #if 0 /* defined(IPSEC) */
> 1393 case IP_IPSEC_POLICY:
> 1394 {
> 1395 struct mbuf *m = NULL;
> 1396
> 1397 /* XXX this will return EINVAL as sopt is empty */
> 1398 error = ipsec_get_policy(inp, sopt->sopt_data,
> 1399 sopt->sopt_size, &m);
> 1400 if (error == 0)
> 1401 error = sockopt_setmbuf(sopt, m);
> 1402 break;
> 1403 }
> 1404 #endif /*IPSEC*/
>
> https://nxr.NetBSD.org/xref/src/sys/netinet/ip_output.c?r=1.330#1392
>
> The IPv6 version has the same comment but it is wrong because
> getsockopt2 does allow the caller to pass input through sopt:
>
> 1934 #if defined(IPSEC)
> 1935 case IPV6_IPSEC_POLICY:
> 1936 if (ipsec_used) {
> 1937 struct mbuf *m = NULL;
> 1938
> 1939 /*
> 1940 * XXX: this will return EINVAL as sopt is
> 1941 * empty
> 1942 */
> 1943 error = ipsec_get_policy(inp, sopt->sopt_data,
> 1944 sopt->sopt_size, &m);
> 1945 if (!error)
> 1946 error = sockopt_setmbuf(sopt, m);
> 1947 } else
> 1948 error = ENOPROTOOPT;
> 1949 break;
> 1950 #endif /* IPSEC */
>
> https://nxr.NetBSD.org/xref/src/sys/netinet6/ip6_output.c?r=1.235#1934
I think that codes would be changed as they are old codes originated
from FAST_IPSEC,
At a glance of patch,
- It seems IS_PFKEY_ALIGNED (or more appropriate name macro)
should be introduced instead by (foo % 8) != 0 directly
- The following diff should be used KASSERTMSG instead of a comment
and KASSERT
====================
@@ -2024,11 +2044,7 @@ static struct secpolicy *
tlen -= xisr->sadb_x_ipsecrequest_len;
/* validity check */
- if (tlen < 0) {
- IPSECLOG(LOG_DEBUG, "becoming tlen < 0.\n");
- *error = EINVAL;
- goto free_exit;
- }
+ KASSERT(tlen >= 0);
====================
The other parts look good to me.
--
//////////////////////////////////////////////////////////////////////
Internet Initiative Japan Inc.
Device Engineering Section,
Core Product Development Department,
Network Division,
Network Services Business Unit
Kengo NAKAHARA <k-nakahara@iij.ad.jp>
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Kengo Nakahara <k-nakahara@iij.ad.jp>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
security-team@NetBSD.org, ozaki-r@NetBSD.org,
knakahara@NetBSD.org, yamaguchi@NetBSD.org, ohishi@iij.ad.jp
Subject: Re: kern/60669: netipsec key_sp2msg buffer overrun
Date: Mon, 7 Sep 2026 10:55:33 +0000
Great, thanks for the review, I will incorporate it into an updated
patch shortly!
> Date: Mon, 7 Sep 2026 12:16:19 +0900
> From: Kengo Nakahara <k-nakahara@iij.ad.jp>
>
> # Add CC to ohishi who is IIJ IPsec co-worker
>
> On 2026/09/01 1:26, Taylor R Campbell wrote:
> > Patch attached to fix and add tests for setsockopt/getsockopt2
> > IPV6_IPSEC_POLICY buffer overrun. Review welcome!
> >
> > Questions: Does it make sense to have mismatched src/dst address
> > families in a single IPsec policy? Does it make sense to have a
> > number of addresses in a policy other than zero or two? If not, we
> > can block those various cases earlier and update the tests.
>
> It does not make sense neither of them. So, I think those cases
> can be blocked.
>
>
> > Should maybe do similarly for IPv4 but there's currently no getsockopt
> > or getsockopt2 path for it anyway -- it's under #if 0:
> >
> > 1392 #if 0 /* defined(IPSEC) */
> > 1393 case IP_IPSEC_POLICY:
> > 1394 {
> > 1395 struct mbuf *m = NULL;
> > 1396
> > 1397 /* XXX this will return EINVAL as sopt is empty */
> > 1398 error = ipsec_get_policy(inp, sopt->sopt_data,
> > 1399 sopt->sopt_size, &m);
> > 1400 if (error == 0)
> > 1401 error = sockopt_setmbuf(sopt, m);
> > 1402 break;
> > 1403 }
> > 1404 #endif /*IPSEC*/
> >
> > https://nxr.NetBSD.org/xref/src/sys/netinet/ip_output.c?r=1.330#1392
> >
> > The IPv6 version has the same comment but it is wrong because
> > getsockopt2 does allow the caller to pass input through sopt:
> >
> > 1934 #if defined(IPSEC)
> > 1935 case IPV6_IPSEC_POLICY:
> > 1936 if (ipsec_used) {
> > 1937 struct mbuf *m = NULL;
> > 1938
> > 1939 /*
> > 1940 * XXX: this will return EINVAL as sopt is
> > 1941 * empty
> > 1942 */
> > 1943 error = ipsec_get_policy(inp, sopt->sopt_data,
> > 1944 sopt->sopt_size, &m);
> > 1945 if (!error)
> > 1946 error = sockopt_setmbuf(sopt, m);
> > 1947 } else
> > 1948 error = ENOPROTOOPT;
> > 1949 break;
> > 1950 #endif /* IPSEC */
> >
> > https://nxr.NetBSD.org/xref/src/sys/netinet6/ip6_output.c?r=1.235#1934
>
> I think that codes would be changed as they are old codes originated
> from FAST_IPSEC,
>
>
> At a glance of patch,
> - It seems IS_PFKEY_ALIGNED (or more appropriate name macro)
> should be introduced instead by (foo % 8) != 0 directly
> - The following diff should be used KASSERTMSG instead of a comment
> and KASSERT
> ====================
> @@ -2024,11 +2044,7 @@ static struct secpolicy *
> tlen -= xisr->sadb_x_ipsecrequest_len;
>
> /* validity check */
> - if (tlen < 0) {
> - IPSECLOG(LOG_DEBUG, "becoming tlen < 0.\n");
> - *error = EINVAL;
> - goto free_exit;
> - }
> + KASSERT(tlen >= 0);
> ====================
>
> The other parts look good to me.
>
>
> --
> //////////////////////////////////////////////////////////////////////
> Internet Initiative Japan Inc.
>
> Device Engineering Section,
> Core Product Development Department,
> Network Division,
> Network Services Business Unit
>
> Kengo NAKAHARA <k-nakahara@iij.ad.jp>
>
>
>
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/sys/netipsec
Date: Fri, 18 Sep 2026 13:30:09 +0000
Module Name: src
Committed By: riastradh
Date: Fri Sep 18 13:30:09 UTC 2026
Modified Files:
src/sys/netipsec: key.c
Log Message:
ipsec: Fix sizing and alignment in IPsec policy import and export.
Note: I'm not sure whether it makes sense to have mismatched src and
dst address families -- I doubt it, since userland libipsec rejects
it in ipsec_set_policy(3). I'm also not sure whether it makes sense
to have numbers of addresses other than 0 or 2. But those can be
addressed in a separate commit.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.287 -r1.288 src/sys/netipsec/key.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src
Date: Fri, 18 Sep 2026 13:32:56 +0000
Module Name: src
Committed By: riastradh
Date: Fri Sep 18 13:32:56 UTC 2026
Modified Files:
src/distrib/sets/lists/debug: mi
src/distrib/sets/lists/tests: mi
src/tests/net/ipsec: Makefile
Added Files:
src/tests/net/ipsec: t_ipsec_policy.c
Log Message:
ipsec: Test edge cases of setting and getting IPsec policies.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.518 -r1.519 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1429 -r1.1430 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.12 -r1.13 src/tests/net/ipsec/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/net/ipsec/t_ipsec_policy.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src
Date: Fri, 18 Sep 2026 13:33:46 +0000
Module Name: src
Committed By: riastradh
Date: Fri Sep 18 13:33:46 UTC 2026
Modified Files:
src/sys/netipsec: key.c
src/tests/net/ipsec: t_ipsec_policy.c
Log Message:
ipsec: Require matching src/dst addr family in IPsec policies.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.288 -r1.289 src/sys/netipsec/key.c
cvs rdiff -u -r1.1 -r1.2 src/tests/net/ipsec/t_ipsec_policy.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Fri, 18 Sep 2026 13:42:03 +0000
State-Changed-Why:
fixed in HEAD, needs pullup-11 and pullup-10 once tests have run
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/sys/netipsec
Date: Sat, 19 Sep 2026 16:49:28 +0000
Module Name: src
Committed By: riastradh
Date: Sat Sep 19 16:49:28 UTC 2026
Modified Files:
src/sys/netipsec: key.c
Log Message:
ipsec: Fix fencepost error in policy parsing.
If there are no addresses appended, the input buffer length may
rightly match sizeof(*xisr) exactly.
Should fix some of the IPsec tests I just broke!
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.289 -r1.290 src/sys/netipsec/key.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/sys/netipsec
Date: Sun, 20 Sep 2026 14:39:05 +0000
Module Name: src
Committed By: riastradh
Date: Sun Sep 20 14:39:05 UTC 2026
Modified Files:
src/sys/netipsec: key.c
Log Message:
ipsec: Nix wrong assertion added in key.c rev. 1.288.
On LP32 platforms, the xpl0 pointer may not be 64-bit aligned itself;
we just require all the _internal_ structures inside that buffer to
be 64-bit aligned.
Should fix various ipsec tests mostly on LP32 platforms.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.290 -r1.291 src/sys/netipsec/key.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/tests/net/ipsec
Date: Tue, 22 Sep 2026 15:04:22 +0000
Module Name: src
Committed By: riastradh
Date: Tue Sep 22 15:04:22 UTC 2026
Modified Files:
src/tests/net/ipsec: t_ipsec_policy.c
Log Message:
t_ipsec_policy: Send all output to stderr.
This way it doesn't get separated in atf output, so you can see which
subcases correspond to which check failures.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/tests/net/ipsec/t_ipsec_policy.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/tests/net/ipsec
Date: Tue, 22 Sep 2026 15:11:43 +0000
Module Name: src
Committed By: riastradh
Date: Tue Sep 22 15:11:43 UTC 2026
Modified Files:
src/tests/net/ipsec: t_ipsec_policy.c
Log Message:
t_ipsec_policy: Skip if IPsec is disabled in the kernel.
Currently failing on the sparc testbed with ENOPROTOOPT, which likely
indicates that IPsec is disabled on that host -- although other IPsec
tests are passing just fine? Maybe they all use rump?
XXX Should maybe do this as a rumpy test instead so we can always
ensure IPsec is enabled.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/tests/net/ipsec/t_ipsec_policy.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60669 CVS commit: src/tests/net/ipsec
Date: Fri, 2 Oct 2026 16:14:38 +0000
Module Name: src
Committed By: riastradh
Date: Fri Oct 2 16:14:38 UTC 2026
Modified Files:
src/tests/net/ipsec: t_ipsec_policy.c
Log Message:
t_ipsec_policy: Skip if net.inet.ipsec.enabled doesn't exist too.
Presumably this means the kernel was built without IPsec support.
PR kern/60669: netipsec key_sp2msg buffer overrun
To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/tests/net/ipsec/t_ipsec_policy.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.