diff mbox series

fs/ceph/file: fix memory leaks in __ceph_sync_read()

Message ID 20241127212027.2704515-1-max.kellermann@ionos.com (mailing list archive)
State New
Headers show
Series fs/ceph/file: fix memory leaks in __ceph_sync_read() | expand

Commit Message

Max Kellermann Nov. 27, 2024, 9:20 p.m. UTC
In two `break` statements, the call to ceph_release_page_vector() was
missing, leaking the allocation from ceph_alloc_page_vector().

Cc: stable@vger.kernel.org
Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
---
 fs/ceph/file.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

Alex Markuze Nov. 28, 2024, 12:17 p.m. UTC | #1
Pages are freed in `ceph_osdc_put_request`, trying to release them
this way will end badly.

On Wed, Nov 27, 2024 at 11:20 PM Max Kellermann
<max.kellermann@ionos.com> wrote:
>
> In two `break` statements, the call to ceph_release_page_vector() was
> missing, leaking the allocation from ceph_alloc_page_vector().
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
> ---
>  fs/ceph/file.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> index 4b8d59ebda00..24d0f1cc9aac 100644
> --- a/fs/ceph/file.c
> +++ b/fs/ceph/file.c
> @@ -1134,6 +1134,7 @@ ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos,
>                         extent_cnt = __ceph_sparse_read_ext_count(inode, read_len);
>                         ret = ceph_alloc_sparse_ext_map(op, extent_cnt);
>                         if (ret) {
> +                               ceph_release_page_vector(pages, num_pages);
>                                 ceph_osdc_put_request(req);
>                                 break;
>                         }
> @@ -1168,6 +1169,7 @@ ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos,
>                                         op->extent.sparse_ext_cnt);
>                         if (fret < 0) {
>                                 ret = fret;
> +                               ceph_release_page_vector(pages, num_pages);
>                                 ceph_osdc_put_request(req);
>                                 break;
>                         }
> --
> 2.45.2
>
Max Kellermann Nov. 28, 2024, 12:28 p.m. UTC | #2
On Thu, Nov 28, 2024 at 1:18 PM Alex Markuze <amarkuze@redhat.com> wrote:
> Pages are freed in `ceph_osdc_put_request`, trying to release them
> this way will end badly.

I don't get it. If this ends badly, why does the other
ceph_release_page_vector() call after ceph_osdc_put_request() in that
function not end badly?
Max Kellermann Nov. 28, 2024, 12:31 p.m. UTC | #3
On Thu, Nov 28, 2024 at 1:28 PM Max Kellermann <max.kellermann@ionos.com> wrote:
>
> On Thu, Nov 28, 2024 at 1:18 PM Alex Markuze <amarkuze@redhat.com> wrote:
> > Pages are freed in `ceph_osdc_put_request`, trying to release them
> > this way will end badly.
>
> I don't get it. If this ends badly, why does the other
> ceph_release_page_vector() call after ceph_osdc_put_request() in that
> function not end badly?

Look at this piece:

        osd_req_op_extent_osd_data_pages(req, 0, pages, read_len,
                         offset_in_page(read_off),
                         false, false);

The last parameter is "own_pages". Ownership of these pages is NOT
transferred to the osdc request, therefore ceph_osdc_put_request()
will NOT free them, and this is really a leak bug and my patch fixes
it.

I just saw this piece of code for the first time, I have no idea. What
am I missing?
Alex Markuze Nov. 28, 2024, 12:48 p.m. UTC | #4
The ergonomics and error handling on this function seem awkward. I'll
take a closer look on how to arrange it better.

On Thu, Nov 28, 2024 at 2:31 PM Max Kellermann <max.kellermann@ionos.com> wrote:
>
> On Thu, Nov 28, 2024 at 1:28 PM Max Kellermann <max.kellermann@ionos.com> wrote:
> >
> > On Thu, Nov 28, 2024 at 1:18 PM Alex Markuze <amarkuze@redhat.com> wrote:
> > > Pages are freed in `ceph_osdc_put_request`, trying to release them
> > > this way will end badly.
> >
> > I don't get it. If this ends badly, why does the other
> > ceph_release_page_vector() call after ceph_osdc_put_request() in that
> > function not end badly?
>
> Look at this piece:
>
>         osd_req_op_extent_osd_data_pages(req, 0, pages, read_len,
>                          offset_in_page(read_off),
>                          false, false);
>
> The last parameter is "own_pages". Ownership of these pages is NOT
> transferred to the osdc request, therefore ceph_osdc_put_request()
> will NOT free them, and this is really a leak bug and my patch fixes
> it.
>
> I just saw this piece of code for the first time, I have no idea. What
> am I missing?
>
Max Kellermann Nov. 28, 2024, 1:11 p.m. UTC | #5
On Thu, Nov 28, 2024 at 1:49 PM Alex Markuze <amarkuze@redhat.com> wrote:
> The ergonomics and error handling on this function seem awkward. I'll
> take a closer look on how to arrange it better.

Does that mean you misunderstood page ownership, or do you still
believe my patch is wrong?
To me, "pages must not be freed manually" and "pages must be freed
manually" can't both be right at the same time.
Max Kellermann Nov. 29, 2024, 8:06 a.m. UTC | #6
On Thu, Nov 28, 2024 at 1:18 PM Alex Markuze <amarkuze@redhat.com> wrote:
> Pages are freed in `ceph_osdc_put_request`, trying to release them
> this way will end badly.

Is there anybody else who can explain this to me?
I believe Alex is wrong and my patch is correct, but maybe I'm missing
something.
diff mbox series

Patch

diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 4b8d59ebda00..24d0f1cc9aac 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -1134,6 +1134,7 @@  ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos,
 			extent_cnt = __ceph_sparse_read_ext_count(inode, read_len);
 			ret = ceph_alloc_sparse_ext_map(op, extent_cnt);
 			if (ret) {
+				ceph_release_page_vector(pages, num_pages);
 				ceph_osdc_put_request(req);
 				break;
 			}
@@ -1168,6 +1169,7 @@  ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos,
 					op->extent.sparse_ext_cnt);
 			if (fret < 0) {
 				ret = fret;
+				ceph_release_page_vector(pages, num_pages);
 				ceph_osdc_put_request(req);
 				break;
 			}