Thread (15 messages) 15 messages, 5 authors, 2024-05-29

Re: [PATCH v4] ax25: Fix refcount imbalance on inbound connections

From: Dan Cross <hidden>
Date: 2024-05-23 20:40:05
Also in: linux-hams

On Thu, May 23, 2024 at 2:23 PM Dan Carpenter [off-list ref] wrote:
On Thu, May 23, 2024 at 11:22:43AM -0400, Dan Cross wrote:
quoted
On Thu, May 23, 2024 at 11:05 AM Dan Carpenter [off-list ref] wrote:
quoted
quoted
[snip]
I've already said that I don't think the patch is correct and offered
an alternative which takes a reference in accept() but also adds a
matching put()...  But I can't really test my patch so if we're going to
do something that we know is wrong, I'd prefer to just revert Duoming's
patch.
Dan, may I ask how you determined that Lars's patch is incorrect?
The problem is that accept() and ax25_release() are not mirrored pairs.
I'm having a hard time understanding why. Here's my reasoning; please
correct me if I'm wrong?

Taking a step back, the semantics of `accept` are that, on successful
completion, it creates a new socket associated with the accepted
connection. It makes sense that such a new socket would take a
reference on the underlying device, since the socket is inherently
tied to that device; this is what Lars's patch does. Indeed, consider
the case that a connection was accepted, and then the bound listening
socket was immediately closed, thus dropping the reference on the
device: it seems that adding a reference onto the device in the
`accept` path is necessary.

So how does `ax25_release` get called? That ends up getting invoked
from `close`; I traced this through the kernel from `sys_close` until
the invocation of the `.release` function from the `proto_ops` vector.
The call graph looks something like this:

sys_close (fs/open.c)
 -> file_close_fd (fs/file.c)
  -> file_closed_fd_locked(same)
  <- returns struct file to file_close_fd
 <- returns struct file to sys_close
 -> filp_flush (fs/open.c)
  -> ops vec `.flush` (nop for socket)
 -> __fput_sync (fs/file_table.c):  decref(f_count) => __fput
  -> __fput (fs/file_table.c)
   -> fsnotify_close(file) (irrelevant)
   -> f_op->release
    -> sock_close (net/socket.c)
     -> __sock_release (net/socket.c)
      -> proto_ops vec `.release`
       -> ax25_release (net/ax25/af_ax25.c)

There may be other ways it's invoked, but that's likely the main one.
It seems clear that this will happen for sockets that have a ref on
the device either via `bind` or via `accept`.
We're just taking the reference and never dropping it. Which fixes the
use after free but introduces a leak.
I'm not sure that's true. It looks to me like the ref is dropped when
the accepted socket is eventually closed. What am I missing?
quoted
Testing so far indicates that it works as expected. On the other hand,
Lars tested your patch and found that it did not address the
underlying issue
(https://marc.info/?l=linux-hams&m=171646940902757&w=2).
Yeah.  I've said a couple times that my patch wasn't complete.  I keep
hoping that Duoming will chime in here...
+1!
quoted
If I may suggest a path forward, given that observed results show that
Lars's patch works as expected, perhaps we can commit that and then
work to incorporate a more robust ref counting strategy a la your
patch?
The argument for this patch is that it works in testing even though we
think it's not totally correct.  That's not really a good argument.
Like we can revert patches that clearly don't work so we could revert
Duoming's patch, but when we're adding code then that should work.
I agree that absence of evidence is not evidence of absence, but I'm
not sure I follow the reasoning behind their being a leak. It's not
clear to me that Lars's patch is obviously wrong. I _do_ think this
code hasn't been shown much love in a long time, and I am totally
prepared to admit that I'm wrong, but right now, I don't see it?

        - Dan C.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help