]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ipv6: ioam: fix type confusion of dst_entry
authorJiayuan Chen <jiayuan.chen@linux.dev>
Thu, 18 Jun 2026 10:43:35 +0000 (18:43 +0800)
committerJakub Kicinski <kuba@kernel.org>
Sun, 21 Jun 2026 22:26:40 +0000 (15:26 -0700)
IOAM uses a dummy dst_entry(null_dst) to mark that the destination should
not be changed after the transformation. This dst is stored in the IOAM lwt
state and may be passed to dst_cache_set_ip6().

However, the IPv6 dst cache path eventually calls rt6_get_cookie(), which
treats the dst_entry as part of a struct rt6_info. Since the null_dst was
embedded directly as a struct dst_entry in struct ioam6_lwt, this resulted
in an invalid cast and rt6_get_cookie() reading fields from the wrong
object.

In practice, the wrong cookie is not used while dst->obsolete is zero, but
rt6_get_cookie() may also access per-cpu value when rt->sernum is
zero. In this case, rt->sernum aliases ioam6_lwt::cache::reset_ts, which
can become zero, making this a potential invalid pointer access.

Fix this by embedding a full struct rt6_info for the dummy IPv6 route and
passing its dst member to the dst APIs.

Fixes: 47ce7c854563 ("net: ipv6: ioam6: fix double reallocation")
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Justin Iurman <justin.iurman@gmail.com>
Link: https://patch.msgid.link/20260618104336.48934-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ioam6_iptunnel.c

index b9f6d892a566c025e0d20e24d44e1570b7a9e42b..cfb2c41634a0b0813d1dd868fcb0943d3a4b80c1 100644 (file)
@@ -35,7 +35,7 @@ struct ioam6_lwt_freq {
 };
 
 struct ioam6_lwt {
-       struct dst_entry null_dst;
+       struct rt6_info null_rt;
        struct dst_cache cache;
        struct ioam6_lwt_freq freq;
        atomic_t pkt_cnt;
@@ -176,7 +176,7 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
         * it is stored in the cache. Then, +1/-1 each time we read the cache
         * and release it. Long story short, we're fine.
         */
-       dst_init(&ilwt->null_dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT);
+       dst_init(&ilwt->null_rt.dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT);
 
        atomic_set(&ilwt->pkt_cnt, 0);
        ilwt->freq.k = freq_k;
@@ -360,7 +360,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
        /* This is how we notify that the destination does not change after
         * transformation and that we need to use orig_dst instead of the cache
         */
-       if (dst == &ilwt->null_dst) {
+       if (dst == &ilwt->null_rt.dst) {
                dst_release(dst);
 
                dst = orig_dst;
@@ -429,7 +429,7 @@ do_encap:
                local_bh_disable();
                if (orig_dst->lwtstate == dst->lwtstate)
                        dst_cache_set_ip6(&ilwt->cache,
-                                         &ilwt->null_dst, &fl6.saddr);
+                                         &ilwt->null_rt.dst, &fl6.saddr);
                else
                        dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr);
                local_bh_enable();