diff mbox series

[net,v2] sctp: fix memory leak in sctp_stream_outq_migrate()

Message ID 20221124131100.369106-1-shaozhengchao@huawei.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net,v2] sctp: fix memory leak in sctp_stream_outq_migrate() | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 31 this patch: 31
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 6 this patch: 6
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes fail Problems with Fixes tag: 1
netdev/build_allmodconfig_warn success Errors and warnings before: 29 this patch: 29
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 67 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

shaozhengchao Nov. 24, 2022, 1:11 p.m. UTC
When sctp_stream_outq_migrate() is called to release stream out resources,
the memory pointed to by prio_head in stream out is not released.

The memory leak information is as follows:
 unreferenced object 0xffff88801fe79f80 (size 64):
   comm "sctp_repo", pid 7957, jiffies 4294951704 (age 36.480s)
   hex dump (first 32 bytes):
     80 9f e7 1f 80 88 ff ff 80 9f e7 1f 80 88 ff ff  ................
     90 9f e7 1f 80 88 ff ff 90 9f e7 1f 80 88 ff ff  ................
   backtrace:
     [<ffffffff81b215c6>] kmalloc_trace+0x26/0x60
     [<ffffffff88ae517c>] sctp_sched_prio_set+0x4cc/0x770
     [<ffffffff88ad64f2>] sctp_stream_init_ext+0xd2/0x1b0
     [<ffffffff88aa2604>] sctp_sendmsg_to_asoc+0x1614/0x1a30
     [<ffffffff88ab7ff1>] sctp_sendmsg+0xda1/0x1ef0
     [<ffffffff87f765ed>] inet_sendmsg+0x9d/0xe0
     [<ffffffff8754b5b3>] sock_sendmsg+0xd3/0x120
     [<ffffffff8755446a>] __sys_sendto+0x23a/0x340
     [<ffffffff87554651>] __x64_sys_sendto+0xe1/0x1b0
     [<ffffffff89978b49>] do_syscall_64+0x39/0xb0
     [<ffffffff89a0008b>] entry_SYSCALL_64_after_hwframe+0x63/0xcd

Link: https://syzkaller.appspot.com/bug?exrid=29c402e56c4760763cc0
Fixes: Fixes: 637784ade221 ("sctp: introduce priority based stream scheduler")
Signed-off-by: Zhengchao Shao <shaozhengchao@huawei.com>
---
v2: add .free_sid hook function and use it to free a stream
---
 include/net/sctp/stream_sched.h |  2 ++
 net/sctp/stream.c               | 15 +++++++++++----
 net/sctp/stream_sched_prio.c    | 20 ++++++++++++++++++++
 3 files changed, 33 insertions(+), 4 deletions(-)

Comments

Xin Long Nov. 24, 2022, 3:37 p.m. UTC | #1
On Thu, Nov 24, 2022 at 8:22 AM Zhengchao Shao <shaozhengchao@huawei.com> wrote:
>
> When sctp_stream_outq_migrate() is called to release stream out resources,
> the memory pointed to by prio_head in stream out is not released.
>
> The memory leak information is as follows:
>  unreferenced object 0xffff88801fe79f80 (size 64):
>    comm "sctp_repo", pid 7957, jiffies 4294951704 (age 36.480s)
>    hex dump (first 32 bytes):
>      80 9f e7 1f 80 88 ff ff 80 9f e7 1f 80 88 ff ff  ................
>      90 9f e7 1f 80 88 ff ff 90 9f e7 1f 80 88 ff ff  ................
>    backtrace:
>      [<ffffffff81b215c6>] kmalloc_trace+0x26/0x60
>      [<ffffffff88ae517c>] sctp_sched_prio_set+0x4cc/0x770
>      [<ffffffff88ad64f2>] sctp_stream_init_ext+0xd2/0x1b0
>      [<ffffffff88aa2604>] sctp_sendmsg_to_asoc+0x1614/0x1a30
>      [<ffffffff88ab7ff1>] sctp_sendmsg+0xda1/0x1ef0
>      [<ffffffff87f765ed>] inet_sendmsg+0x9d/0xe0
>      [<ffffffff8754b5b3>] sock_sendmsg+0xd3/0x120
>      [<ffffffff8755446a>] __sys_sendto+0x23a/0x340
>      [<ffffffff87554651>] __x64_sys_sendto+0xe1/0x1b0
>      [<ffffffff89978b49>] do_syscall_64+0x39/0xb0
>      [<ffffffff89a0008b>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
>
> Link: https://syzkaller.appspot.com/bug?exrid=29c402e56c4760763cc0
> Fixes: Fixes: 637784ade221 ("sctp: introduce priority based stream scheduler")
> Signed-off-by: Zhengchao Shao <shaozhengchao@huawei.com>
> ---
> v2: add .free_sid hook function and use it to free a stream
> ---
>  include/net/sctp/stream_sched.h |  2 ++
>  net/sctp/stream.c               | 15 +++++++++++----
>  net/sctp/stream_sched_prio.c    | 20 ++++++++++++++++++++
>  3 files changed, 33 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h
> index 01a70b27e026..65058faea4db 100644
> --- a/include/net/sctp/stream_sched.h
> +++ b/include/net/sctp/stream_sched.h
> @@ -26,6 +26,8 @@ struct sctp_sched_ops {
>         int (*init)(struct sctp_stream *stream);
>         /* Init a stream */
>         int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
> +       /* free a stream */
> +       void (*free_sid)(struct sctp_stream *stream, __u16 sid);
>         /* Frees the entire thing */
>         void (*free)(struct sctp_stream *stream);
>
> diff --git a/net/sctp/stream.c b/net/sctp/stream.c
> index ef9fceadef8d..0a704bbf1155 100644
> --- a/net/sctp/stream.c
> +++ b/net/sctp/stream.c
> @@ -52,6 +52,15 @@ static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
>         }
>  }
>
> +static void sctp_stream_free_ext(struct sctp_stream *stream, __u16 sid)
> +{
> +       struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
> +
> +       sched->free_sid(stream, sid);
> +       kfree(SCTP_SO(stream, sid)->ext);
> +       SCTP_SO(stream, sid)->ext = NULL;
> +}
> +
>  /* Migrates chunks from stream queues to new stream queues if needed,
>   * but not across associations. Also, removes those chunks to streams
>   * higher than the new max.
> @@ -76,10 +85,8 @@ static void sctp_stream_outq_migrate(struct sctp_stream *stream,
>                 }
>         }
>
> -       for (i = outcnt; i < stream->outcnt; i++) {
> -               kfree(SCTP_SO(stream, i)->ext);
> -               SCTP_SO(stream, i)->ext = NULL;
> -       }
> +       for (i = outcnt; i < stream->outcnt; i++)
> +               sctp_stream_free_ext(stream, i);
>  }
>
>  static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
> diff --git a/net/sctp/stream_sched_prio.c b/net/sctp/stream_sched_prio.c
> index 80b5a2c4cbc7..e9cc71b93e90 100644
> --- a/net/sctp/stream_sched_prio.c
> +++ b/net/sctp/stream_sched_prio.c
> @@ -204,6 +204,25 @@ static int sctp_sched_prio_init_sid(struct sctp_stream *stream, __u16 sid,
>         return sctp_sched_prio_set(stream, sid, 0, gfp);
>  }
>
> +static void sctp_sched_prio_free_sid(struct sctp_stream *stream, __u16 sid)
> +{
> +       struct sctp_stream_priorities *prio;
> +       int i;
> +
> +       if (!SCTP_SO(stream, sid)->ext || !SCTP_SO(stream, sid)->ext->prio_head)
> +               return;
It will be better to move "!SCTP_SO(stream, sid)->ext" check to
sctp_stream_free_ext().

> +
> +       prio = SCTP_SO(stream, sid)->ext->prio_head;
> +       SCTP_SO(stream, sid)->ext->prio_head = NULL;
> +       for (i = 0; i < stream->outcnt; i++) {
> +               if (SCTP_SO(stream, i)->ext &&
> +                   SCTP_SO(stream, i)->ext->prio_head == prio)
> +                       return;
> +       }
> +
> +       kfree(prio);
> +}
> +
>  static void sctp_sched_prio_free(struct sctp_stream *stream)
>  {
>         struct sctp_stream_priorities *prio, *n;
> @@ -323,6 +342,7 @@ static struct sctp_sched_ops sctp_sched_prio = {
>         .get = sctp_sched_prio_get,
>         .init = sctp_sched_prio_init,
>         .init_sid = sctp_sched_prio_init_sid,
> +       .free_sid = sctp_sched_prio_free_sid,
Please also add .free_sid for sctp_sched_fcfs/rr,
although they could be empty functions.

Thanks.

>         .free = sctp_sched_prio_free,
>         .enqueue = sctp_sched_prio_enqueue,
>         .dequeue = sctp_sched_prio_dequeue,
> --
> 2.17.1
>
Marcelo Ricardo Leitner Nov. 24, 2022, 5:18 p.m. UTC | #2
On Thu, Nov 24, 2022 at 09:11:00PM +0800, Zhengchao Shao wrote:
> When sctp_stream_outq_migrate() is called to release stream out resources,
> the memory pointed to by prio_head in stream out is not released.
> 
> The memory leak information is as follows:
>  unreferenced object 0xffff88801fe79f80 (size 64):
>    comm "sctp_repo", pid 7957, jiffies 4294951704 (age 36.480s)
>    hex dump (first 32 bytes):
>      80 9f e7 1f 80 88 ff ff 80 9f e7 1f 80 88 ff ff  ................
>      90 9f e7 1f 80 88 ff ff 90 9f e7 1f 80 88 ff ff  ................
>    backtrace:
>      [<ffffffff81b215c6>] kmalloc_trace+0x26/0x60
>      [<ffffffff88ae517c>] sctp_sched_prio_set+0x4cc/0x770
>      [<ffffffff88ad64f2>] sctp_stream_init_ext+0xd2/0x1b0
>      [<ffffffff88aa2604>] sctp_sendmsg_to_asoc+0x1614/0x1a30
>      [<ffffffff88ab7ff1>] sctp_sendmsg+0xda1/0x1ef0
>      [<ffffffff87f765ed>] inet_sendmsg+0x9d/0xe0
>      [<ffffffff8754b5b3>] sock_sendmsg+0xd3/0x120
>      [<ffffffff8755446a>] __sys_sendto+0x23a/0x340
>      [<ffffffff87554651>] __x64_sys_sendto+0xe1/0x1b0
>      [<ffffffff89978b49>] do_syscall_64+0x39/0xb0
>      [<ffffffff89a0008b>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
> 
> Link: https://syzkaller.appspot.com/bug?exrid=29c402e56c4760763cc0
> Fixes: Fixes: 637784ade221 ("sctp: introduce priority based stream scheduler")
> Signed-off-by: Zhengchao Shao <shaozhengchao@huawei.com>
> ---
> v2: add .free_sid hook function and use it to free a stream

It's missing the change to sctp_stream_free as well. Please lets try
to avoid having multiple paths freeing it differently as much as
possible.

Thanks.
Xin Long Nov. 24, 2022, 6:17 p.m. UTC | #3
On Thu, Nov 24, 2022 at 12:26 PM Marcelo Ricardo Leitner
<marcelo.leitner@gmail.com> wrote:
>
> On Thu, Nov 24, 2022 at 09:11:00PM +0800, Zhengchao Shao wrote:
> > When sctp_stream_outq_migrate() is called to release stream out resources,
> > the memory pointed to by prio_head in stream out is not released.
> >
> > The memory leak information is as follows:
> >  unreferenced object 0xffff88801fe79f80 (size 64):
> >    comm "sctp_repo", pid 7957, jiffies 4294951704 (age 36.480s)
> >    hex dump (first 32 bytes):
> >      80 9f e7 1f 80 88 ff ff 80 9f e7 1f 80 88 ff ff  ................
> >      90 9f e7 1f 80 88 ff ff 90 9f e7 1f 80 88 ff ff  ................
> >    backtrace:
> >      [<ffffffff81b215c6>] kmalloc_trace+0x26/0x60
> >      [<ffffffff88ae517c>] sctp_sched_prio_set+0x4cc/0x770
> >      [<ffffffff88ad64f2>] sctp_stream_init_ext+0xd2/0x1b0
> >      [<ffffffff88aa2604>] sctp_sendmsg_to_asoc+0x1614/0x1a30
> >      [<ffffffff88ab7ff1>] sctp_sendmsg+0xda1/0x1ef0
> >      [<ffffffff87f765ed>] inet_sendmsg+0x9d/0xe0
> >      [<ffffffff8754b5b3>] sock_sendmsg+0xd3/0x120
> >      [<ffffffff8755446a>] __sys_sendto+0x23a/0x340
> >      [<ffffffff87554651>] __x64_sys_sendto+0xe1/0x1b0
> >      [<ffffffff89978b49>] do_syscall_64+0x39/0xb0
> >      [<ffffffff89a0008b>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
> >
> > Link: https://syzkaller.appspot.com/bug?exrid=29c402e56c4760763cc0
> > Fixes: Fixes: 637784ade221 ("sctp: introduce priority based stream scheduler")
> > Signed-off-by: Zhengchao Shao <shaozhengchao@huawei.com>
> > ---
> > v2: add .free_sid hook function and use it to free a stream
>
> It's missing the change to sctp_stream_free as well. Please lets try
> to avoid having multiple paths freeing it differently as much as
> possible.
>
Right, something like:

@@ -181,9 +181,9 @@ void sctp_stream_free(struct sctp_stream *stream)
        struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
        int i;

-       sched->free(stream);
+       sched->unsched_all(stream)
        for (i = 0; i < stream->outcnt; i++)
-               kfree(SCTP_SO(stream, i)->ext);
+               sctp_stream_free_ext(stream, i);
        genradix_free(&stream->out);
        genradix_free(&stream->in);
 }

Thanks.
diff mbox series

Patch

diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h
index 01a70b27e026..65058faea4db 100644
--- a/include/net/sctp/stream_sched.h
+++ b/include/net/sctp/stream_sched.h
@@ -26,6 +26,8 @@  struct sctp_sched_ops {
 	int (*init)(struct sctp_stream *stream);
 	/* Init a stream */
 	int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
+	/* free a stream */
+	void (*free_sid)(struct sctp_stream *stream, __u16 sid);
 	/* Frees the entire thing */
 	void (*free)(struct sctp_stream *stream);
 
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index ef9fceadef8d..0a704bbf1155 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -52,6 +52,15 @@  static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
 	}
 }
 
+static void sctp_stream_free_ext(struct sctp_stream *stream, __u16 sid)
+{
+	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
+
+	sched->free_sid(stream, sid);
+	kfree(SCTP_SO(stream, sid)->ext);
+	SCTP_SO(stream, sid)->ext = NULL;
+}
+
 /* Migrates chunks from stream queues to new stream queues if needed,
  * but not across associations. Also, removes those chunks to streams
  * higher than the new max.
@@ -76,10 +85,8 @@  static void sctp_stream_outq_migrate(struct sctp_stream *stream,
 		}
 	}
 
-	for (i = outcnt; i < stream->outcnt; i++) {
-		kfree(SCTP_SO(stream, i)->ext);
-		SCTP_SO(stream, i)->ext = NULL;
-	}
+	for (i = outcnt; i < stream->outcnt; i++)
+		sctp_stream_free_ext(stream, i);
 }
 
 static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
diff --git a/net/sctp/stream_sched_prio.c b/net/sctp/stream_sched_prio.c
index 80b5a2c4cbc7..e9cc71b93e90 100644
--- a/net/sctp/stream_sched_prio.c
+++ b/net/sctp/stream_sched_prio.c
@@ -204,6 +204,25 @@  static int sctp_sched_prio_init_sid(struct sctp_stream *stream, __u16 sid,
 	return sctp_sched_prio_set(stream, sid, 0, gfp);
 }
 
+static void sctp_sched_prio_free_sid(struct sctp_stream *stream, __u16 sid)
+{
+	struct sctp_stream_priorities *prio;
+	int i;
+
+	if (!SCTP_SO(stream, sid)->ext || !SCTP_SO(stream, sid)->ext->prio_head)
+		return;
+
+	prio = SCTP_SO(stream, sid)->ext->prio_head;
+	SCTP_SO(stream, sid)->ext->prio_head = NULL;
+	for (i = 0; i < stream->outcnt; i++) {
+		if (SCTP_SO(stream, i)->ext &&
+		    SCTP_SO(stream, i)->ext->prio_head == prio)
+			return;
+	}
+
+	kfree(prio);
+}
+
 static void sctp_sched_prio_free(struct sctp_stream *stream)
 {
 	struct sctp_stream_priorities *prio, *n;
@@ -323,6 +342,7 @@  static struct sctp_sched_ops sctp_sched_prio = {
 	.get = sctp_sched_prio_get,
 	.init = sctp_sched_prio_init,
 	.init_sid = sctp_sched_prio_init_sid,
+	.free_sid = sctp_sched_prio_free_sid,
 	.free = sctp_sched_prio_free,
 	.enqueue = sctp_sched_prio_enqueue,
 	.dequeue = sctp_sched_prio_dequeue,