public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Arjan van de Ven <arjan@linux.intel.com>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, edumazet@google.com, horms@kernel.org,
	jreuter@yaina.de, kuba@kernel.org, linux-hams@vger.kernel.org,
	linux-kernel@vger.kernel.org, pabeni@redhat.com,
	syzkaller-bugs@googlegroups.com,
	syzbot+9c8999af06ca7df15fc6@syzkaller.appspotmail.com
Subject: Re: [syzbot] [hams?] KASAN: slab-use-after-free Read in ax25_send_frame (3)
Date: Mon, 27 Apr 2026 08:25:26 -0700	[thread overview]
Message-ID: <20260427152555.500903-1-arjan@linux.intel.com> (raw)
In-Reply-To: <69ef2847.170a0220.11de9.001a.GAE@google.com>

This email is created by automation to help kernel developers
deal with a large volume of AI generated bug reports by decoding
oopses into more actionable information.

Decoded Backtrace

net/ax25/ax25_out.c (crash site, UAF read)

  32  ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen,
  32      const ax25_address *src, ax25_address *dest,
  32      ax25_digi *digi, struct net_device *dev)
  33  {
  34      ax25_dev *ax25_dev;
  35      ax25_cb *ax25;
       ...
  77      if (digi != NULL) {
->78          ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC);
              // <- digi = neigh->digipeat; freed ax25_digi; 66-byte UAF read
  79          if (ax25->digipeat == NULL) {
       ...
 115      return ax25;
 116  }

net/rose/rose_link.c (caller, t0timer callback)

  79  static void rose_t0timer_expiry(struct timer_list *t)
  80  {
  81      struct rose_neigh *neigh = timer_container_of(neigh, t, t0timer);
  82
->83      rose_transmit_restart_request(neigh);
         // <- inlined; calls rose_send_frame -> ax25_send_frame
         //    with neigh->digipeat as the digi argument
  84
  85      neigh->dce_mode = 0;
  86
  87      rose_start_t0timer(neigh);
  88  }

rose_send_frame() inlined at rose_link.c:106:

  95  static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
  96  {
       ...
 105      ax25s = neigh->ax25;
->106     neigh->ax25 = ax25_send_frame(skb, 260, rose_call,
             &neigh->callsign, neigh->digipeat, neigh->dev);
         // <- neigh->digipeat passed as digi; freed by rose_timer_expiry
 107      if (ax25s)
 108          ax25_cb_put(ax25s);
 109      return neigh->ax25 != NULL;
 110  }

net/rose/rose_timer.c (free site)

 164  static void rose_timer_expiry(struct timer_list *t)
 165  {
 166      struct rose_sock *rose = timer_container_of(rose, t, timer);
 167      struct sock *sk = &rose->sock;
       ...
 174      switch (rose->state) {
       ...
 182      case ROSE_STATE_2:  /* T3 */
->183          rose_neigh_put(rose->neighbour);
              // <- drops refcount to 0; frees neigh->digipeat (ax25_digi)
              //    and neigh itself; t0timer still pending
 184          rose_disconnect(sk, ETIMEDOUT, -1, -1);
 185          break;
       ...
 197  }

include/net/rose.h (rose_neigh_put, inline free function)

 160  static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
 161  {
 162      if (refcount_dec_and_test(&rose_neigh->use)) {
 163          if (rose_neigh->ax25)
 164              ax25_cb_put(rose_neigh->ax25);
->165          kfree(rose_neigh->digipeat);
              // <- frees the ax25_digi (66 bytes); t0timer not cancelled
 166          kfree(rose_neigh);
 167      }
 168  }

net/rose/rose_route.c (allocation site)

  84      if (rose_neigh == NULL) {
  85          rose_neigh = kmalloc_obj(*rose_neigh, GFP_ATOMIC);
       ...
 100          refcount_set(&rose_neigh->use, 1);
       ...
 107          if (rose_route->ndigis != 0) {
 108              rose_neigh->digipeat =
->109                  kmalloc_obj(ax25_digi, GFP_ATOMIC);
                  // <- allocates the 66-byte ax25_digi later freed and read
       ...
 124          }
 125      }


Tentative Analysis

The crash is a KASAN slab-use-after-free: rose_t0timer_expiry() reads
the freed rose_neigh->digipeat (an ax25_digi struct, 66 bytes) via
ax25_send_frame() -> kmemdup().

Commit d860d1faa6b2 ("net: rose: convert 'use' field to refcount_t")
changed rose_timer_expiry() from merely decrementing the plain 'use'
counter to calling rose_neigh_put(), which now frees rose_neigh (and
its digipeat) when the refcount hits zero. The new rose_neigh_put()
omits timer cancellation, so after it returns the t0timer embedded in
the (now-freed) rose_neigh can still fire in the same TIMER_SOFTIRQ
batch.

The race on a single-CPU machine (the syzbot scenario) is purely
sequential: rose_timer_expiry() fires first, frees the neigh; then
rose_t0timer_expiry() fires next in the same run_timer_base batch with
a dangling neigh pointer, passes neigh->digipeat to ax25_send_frame,
and kmemdup triggers the KASAN report.

Before d860d1faa6b2, rose_timer_expiry() never freed the neigh; the
free was always performed by rose_remove_neigh() which called
timer_delete_sync() on both timers before freeing. The refcount
conversion introduced a new free path that missed this cancellation.


Potential Solution

Add timer_delete() calls for both ftimer and t0timer inside
rose_neigh_put() before the kfree calls, mirroring what
rose_remove_neigh() already does via timer_delete_sync(). The
non-synchronous variant is required because rose_neigh_put() may be
called from softirq context.

   static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
   {
       if (refcount_dec_and_test(&rose_neigh->use)) {
           timer_delete(&rose_neigh->ftimer);
           timer_delete(&rose_neigh->t0timer);
           if (rose_neigh->ax25)
               ax25_cb_put(rose_neigh->ax25);
           kfree(rose_neigh->digipeat);
           kfree(rose_neigh);
       }
   }


More information

Oops-Analysis: http://oops.fenrus.org/reports/lkml/69ef2847.170a0220.11de9.001a.GAE_google.com/
Assisted-by: Copilot:claude-sonnet-4.6 linux-kernel-oops-x86.

      reply	other threads:[~2026-04-27 15:24 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-27  9:11 [syzbot] [hams?] KASAN: slab-use-after-free Read in ax25_send_frame (3) syzbot
2026-04-27 15:25 ` Arjan van de Ven [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260427152555.500903-1-arjan@linux.intel.com \
    --to=arjan@linux.intel.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=jreuter@yaina.de \
    --cc=kuba@kernel.org \
    --cc=linux-hams@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=syzbot+9c8999af06ca7df15fc6@syzkaller.appspotmail.com \
    --cc=syzkaller-bugs@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox