diff mbox series

[v1] can: etas_es58x: es58x_rx_err_msg: fix memory leak in error path

Message ID 20211026180740.1953265-1-mailhol.vincent@wanadoo.fr (mailing list archive)
State Awaiting Upstream
Delegated to: Netdev Maintainers
Headers show
Series [v1] can: etas_es58x: es58x_rx_err_msg: fix memory leak in error path | expand

Checks

Context Check Description
netdev/tree_selection success Series ignored based on subject

Commit Message

Vincent Mailhol Oct. 26, 2021, 6:07 p.m. UTC
In es58x_rx_err_msg(), if can->do_set_mode() fails, the function
directly returns without calling netif_rx(skb). This means that the
skb previously allocated by alloc_can_err_skb() is not freed. In other
terms, this is a memory leak.

This patch simply removes the return statement in the error branch and
let the function continue.

* Appendix: how the issue was found *

This issue was found using GCC's static analysis tool: -fanalyzer:
https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html

The step to reproduce are:

  1. Install GCC 11.

  2. Hack the kernel's Makefile to add the -fanalyzer flag (we leave
  it as an exercise for the reader to figure out the details of how to
  do so).

  3. Decorate the function alloc_can_err_skb() with
  __attribute__((__malloc__ (dealloc, netif_rx))). This step helps the
  static analyzer to figure out the constructor/destructor pairs (not
  something it can deduce by himself).

  4. Compile.

The compiler then throws below warning:

| In function 'es58x_rx_err_msg':
| drivers/net/can/usb/etas_es58x/es58x_core.c:826:28: warning: leak of 'skb' [CWE-401] [-Wanalyzer-malloc-leak]
|   826 |                         if (ret)
|       |                            ^
|   'es58x_rx_err_msg': events 1-9
|     |
|     |  659 | int es58x_rx_err_msg(struct net_device *netdev, enum es58x_err error,
|     |      |     ^~~~~~~~~~~~~~~~
|     |      |     |
|     |      |     (1) entry to 'es58x_rx_err_msg'
|     |......
|     |  669 |         if (!netif_running(netdev)) {
|     |      |            ~
|     |      |            |
|     |      |            (2) following 'true' branch...
|     |......
|     |  677 |         if (error == ES58X_ERR_OK && event == ES58X_EVENT_OK) {
|     |      |         ~~ ~
|     |      |         |  |
|     |      |         |  (4) following 'false' branch...
|     |      |         (3) ...to here
|     |......
|     |  683 |         skb = alloc_can_err_skb(netdev, &cf);
|     |      |         ~~~
|     |      |         |
|     |      |         (5) ...to here
|     |......
|     |  861 |         if (cf) {
|     |      |            ~
|     |      |            |
|     |      |            (6) following 'false' branch...
|     |......
|     |  875 |         if ((event & ES58X_EVENT_CRTL_PASSIVE) &&
|     |      |         ~~ ~
|     |      |         |  |
|     |      |         |  (8) following 'true' branch...
|     |      |         (7) ...to here
|     |  876 |             priv->err_passive_before_rtx_success == ES58X_CONSECUTIVE_ERR_PASSIVE_MAX) {
|     |      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|     |      |                 |
|     |      |                 (9) ...to here
|     |
|   'es58x_rx_err_msg': events 10-12
|     |
|     |  875 |         if ((event & ES58X_EVENT_CRTL_PASSIVE) &&
|     |  876 |             priv->err_passive_before_rtx_success == ES58X_CONSECUTIVE_ERR_PASSIVE_MAX) {
|     |  877 |                 netdev_info(netdev,
|     |      |                 ~~~~~~~~~~~
|     |      |                 |
|     |      |                 (11) ...to here
|     |......
|     |  880 |                 return es58x_rx_err_msg(netdev, ES58X_ERR_OK,
|     |      |                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|     |      |                        |
|     |      |                        (12) calling 'es58x_rx_err_msg' from 'es58x_rx_err_msg'
|     |  881 |                                         ES58X_EVENT_BUSOFF, timestamp);
|     |      |                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|     |
|     +--> 'es58x_rx_err_msg': events 13-23
|            |
|            |  659 | int es58x_rx_err_msg(struct net_device *netdev, enum es58x_err error,
|            |      |     ^~~~~~~~~~~~~~~~
|            |      |     |
|            |      |     (13) entry to 'es58x_rx_err_msg'
|            |......
|            |  669 |         if (!netif_running(netdev)) {
|            |      |            ~
|            |      |            |
|            |      |            (14) following 'true' branch...
|            |......
|            |  677 |         if (error == ES58X_ERR_OK && event == ES58X_EVENT_OK) {
|            |      |         ~~ ~
|            |      |         |  |
|            |      |         |  (16) following 'false' branch...
|            |      |         (15) ...to here
|            |......
|            |  683 |         skb = alloc_can_err_skb(netdev, &cf);
|            |      |         ~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|            |      |         |     |
|            |      |         |     (18) allocated here
|            |      |         (17) ...to here
|            |  684 |
|            |  685 |         switch (error) {
|            |      |         ~~~~~~
|            |      |         |
|            |      |         (19) following 'case 0:' branch...
|            |......
|            |  764 |         switch (event) {
|            |      |         ~~~~~~
|            |      |         |
|            |      |         (20) ...to here
|            |      |         (21) following 'case 8:' branch...
|            |......
|            |  815 |         case ES58X_EVENT_BUSOFF:
|            |      |         ~~~~
|            |      |         |
|            |      |         (22) ...to here
|            |......
|            |  826 |                         if (ret)
|            |      |                            ~
|            |      |                            |
|            |      |                            (23) 'skb' leaks here; was allocated at (18)
|            |


Fixes: 8537257874e9 ("can: etas_es58x: add core support for ETAS ES58X CAN USB interfaces")
Signed-off-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
---
 drivers/net/can/usb/etas_es58x/es58x_core.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

Comments

Marc Kleine-Budde Oct. 27, 2021, 7:39 a.m. UTC | #1
On 27.10.2021 03:07:40, Vincent Mailhol wrote:
> In es58x_rx_err_msg(), if can->do_set_mode() fails, the function
> directly returns without calling netif_rx(skb). This means that the
> skb previously allocated by alloc_can_err_skb() is not freed. In other
> terms, this is a memory leak.
> 
> This patch simply removes the return statement in the error branch and
> let the function continue.
> 
> * Appendix: how the issue was found *

Thanks for the explanation, but I think I'll remove the appendix while
applying.

Thanks,
Marc
Vincent Mailhol Oct. 27, 2021, 8:48 a.m. UTC | #2
On Wed. 27 Oct 2021 at 16:39, Marc Kleine-Budde <mkl@pengutronix.de> wrote:
> On 27.10.2021 03:07:40, Vincent Mailhol wrote:
> > In es58x_rx_err_msg(), if can->do_set_mode() fails, the function
> > directly returns without calling netif_rx(skb). This means that the
> > skb previously allocated by alloc_can_err_skb() is not freed. In other
> > terms, this is a memory leak.
> >
> > This patch simply removes the return statement in the error branch and
> > let the function continue.
> >
> > * Appendix: how the issue was found *
>
> Thanks for the explanation, but I think I'll remove the appendix while
> applying.

The commit will have a link to this thread so if you don't mind,
I suggest to replace the full appendix with:

Issue was found with GCC -fanalyzer, please follow the link below
for details.

You can of course do the same for the m_can patch.


Yours sincerely,
Vincent Mailhol
Marc Kleine-Budde Oct. 29, 2021, 11:20 a.m. UTC | #3
On 27.10.2021 17:48:21, Vincent MAILHOL wrote:
> On Wed. 27 Oct 2021 at 16:39, Marc Kleine-Budde <mkl@pengutronix.de> wrote:
> > On 27.10.2021 03:07:40, Vincent Mailhol wrote:
> > > In es58x_rx_err_msg(), if can->do_set_mode() fails, the function
> > > directly returns without calling netif_rx(skb). This means that the
> > > skb previously allocated by alloc_can_err_skb() is not freed. In other
> > > terms, this is a memory leak.
> > >
> > > This patch simply removes the return statement in the error branch and
> > > let the function continue.
> > >
> > > * Appendix: how the issue was found *
> >
> > Thanks for the explanation, but I think I'll remove the appendix while
> > applying.
> 
> The commit will have a link to this thread so if you don't mind,
> I suggest to replace the full appendix with:
> 
> Issue was found with GCC -fanalyzer, please follow the link below
> for details.

Makes sense, good idea.

> You can of course do the same for the m_can patch.

ACK

Added to linux-can/testing, will send it out once v5.15 is out.

Thanks,
Marc
diff mbox series

Patch

diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c
index 403de7e9d084..8508a73d648e 100644
--- a/drivers/net/can/usb/etas_es58x/es58x_core.c
+++ b/drivers/net/can/usb/etas_es58x/es58x_core.c
@@ -664,7 +664,7 @@  int es58x_rx_err_msg(struct net_device *netdev, enum es58x_err error,
 	struct can_device_stats *can_stats = &can->can_stats;
 	struct can_frame *cf = NULL;
 	struct sk_buff *skb;
-	int ret;
+	int ret = 0;
 
 	if (!netif_running(netdev)) {
 		if (net_ratelimit())
@@ -823,8 +823,6 @@  int es58x_rx_err_msg(struct net_device *netdev, enum es58x_err error,
 			can->state = CAN_STATE_BUS_OFF;
 			can_bus_off(netdev);
 			ret = can->do_set_mode(netdev, CAN_MODE_STOP);
-			if (ret)
-				return ret;
 		}
 		break;
 
@@ -881,7 +879,7 @@  int es58x_rx_err_msg(struct net_device *netdev, enum es58x_err error,
 					ES58X_EVENT_BUSOFF, timestamp);
 	}
 
-	return 0;
+	return ret;
 }
 
 /**