All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
@ 2021-08-02 21:00 Pavel Skripkin
  2021-08-03 15:08 ` Dan Carpenter
  2021-08-04 14:35 ` Dan Carpenter
  0 siblings, 2 replies; 8+ messages in thread
From: Pavel Skripkin @ 2021-08-02 21:00 UTC (permalink / raw)
  To: smatch; +Cc: Pavel Skripkin

After manual code review I found a lot of bugs, when code uses
netdev_priv() pointer after free_{netdev,candev} call. Example:

	struct some_dev *dev = netdev_priv(net_dev);

	free_netdev(net_dev);
	do_clean_up(dev);

Obviously, that above code snippet will trigger UAF bug, since dev
pointer goes away with net_dev. Since there isn't any check for this
type of bug in other static analysis tools, let's add it to smatch.

Side note: this code requires further work to be able to find complex
situtations like

	struct some_dev *dev = get_dev_from_smth(smth);

	free_netdev(dev->netdev);
	do_clean_up(dev);

In this case dev is dev->netdev private data and it's a bit difficult to
to figure out if ->netdev is actual dev _parent_ or not.

Signed-off-by: Pavel Skripkin <paskripkin@gmail.com>
---
 check_list.h        |   1 +
 check_netdev_priv.c | 117 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 118 insertions(+)
 create mode 100644 check_netdev_priv.c

diff --git a/check_list.h b/check_list.h
index fd205269..972b2795 100644
--- a/check_list.h
+++ b/check_list.h
@@ -203,6 +203,7 @@ CK(check_list_add)
 CK(check_list_add_late)
 CK(check_sscanf_return)
 CK(check_kvmalloc_NOFS)
+CK(check_uaf_netdev_priv)
 
 /* wine specific stuff */
 CK(check_wine_filehandles)
diff --git a/check_netdev_priv.c b/check_netdev_priv.c
new file mode 100644
index 00000000..87d373b5
--- /dev/null
+++ b/check_netdev_priv.c
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2021 Pavel Skripkin
+ */
+
+/* TODO:
+ *
+ * Try to find a way how to handle situations like:
+ *
+ *	struct some_dev *dev = get_dev_from_smth(smth);
+ *
+ *	free_netdev(dev->netdev);
+ *	do_clean_up(dev);
+ *
+ *
+ *	In this case dev is dev->netdev private data (exmpl: ems_usb_disconnect())
+ */
+
+#include "smatch.h"
+#include "smatch_extra.h"
+#include "smatch_function_hashtable.h"
+
+static int my_id;
+STATE(freed);
+STATE(ok);
+
+static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
+{
+	if (sm->state != &ok)
+		set_state(my_id, sm->name, sm->sym, &ok);
+}
+
+static inline char *get_function_name(struct expression *expr)
+{
+	if (!expr || expr->type != EXPR_CALL)
+		return NULL;
+	if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
+		return NULL;
+	return expr->fn->symbol_name->name;
+}
+
+static inline int is_netdev_priv(struct expression *call)
+{
+	char *name;
+
+	if (!call || call->type != EXPR_CALL)
+		return 0;
+
+	name = get_function_name(call);
+	if (!name)
+		return 0;
+
+	return !strcmp("netdev_priv", name);
+}
+
+static const char *get_parent_netdev_name(struct expression *expr)
+{
+	struct expression *call, *arg_expr = NULL;
+	struct symbol *sym;
+
+	call = get_assigned_expr(strip_expr(expr));
+	if (is_netdev_priv(call)) {
+		arg_expr = get_argument_from_call_expr(call->args, 0);
+		arg_expr = strip_expr(arg_expr);
+	} else {
+		return NULL;
+	}
+
+	return expr_to_var_sym(arg_expr, &sym);
+}
+
+static void match_free_netdev(const char *fn, struct expression *expr, void *_arg_no)
+{
+	struct expression *arg;
+	const char *name;
+
+	arg = get_argument_from_call_expr(expr->args, PTR_INT(_arg_no));
+	if (!arg)
+		return;
+
+	name = expr_to_var(arg);
+	if (!name)
+		return;
+
+	set_state(my_id, name, NULL, &freed);
+}
+
+static void match_symbol(struct expression *expr)
+{
+	const char *parent_netdev, *name;
+	struct smatch_state *state;
+
+	name = expr_to_var(expr);
+	if (!name)
+		return;
+
+	parent_netdev = get_parent_netdev_name(expr);
+	if (!parent_netdev)
+		return;
+	
+	state = get_state(my_id, parent_netdev, NULL);
+	if (state == &freed)
+		sm_error("Using %s after free_{netdev,candev}(%s);\n", name, parent_netdev);
+}
+
+void check_uaf_netdev_priv(int id)
+{
+	if (option_project != PROJ_KERNEL)
+		return;
+
+	my_id = id;
+
+	add_function_hook("free_netdev", &match_free_netdev, NULL);
+	add_function_hook("free_candev", &match_free_netdev, NULL);
+	add_modification_hook(my_id, &ok_to_use);
+	add_hook(&match_symbol, SYM_HOOK);
+}
-- 
2.32.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-02 21:00 [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev Pavel Skripkin
@ 2021-08-03 15:08 ` Dan Carpenter
  2021-08-03 16:03   ` Pavel Skripkin
  2021-08-04 14:35 ` Dan Carpenter
  1 sibling, 1 reply; 8+ messages in thread
From: Dan Carpenter @ 2021-08-03 15:08 UTC (permalink / raw)
  To: Pavel Skripkin; +Cc: smatch

Thanks Pavel!

It looks really nice.  I've applied it.  I'll test it tonight and push
tomorrow.

I don't see any major issues with the check at all, but I have a few
comments below.

On Tue, Aug 03, 2021 at 12:00:22AM +0300, Pavel Skripkin wrote:
> +static void match_free_netdev(const char *fn, struct expression *expr, void *_arg_no)
> +{
> +	struct expression *arg;
> +	const char *name;
> +
> +	arg = get_argument_from_call_expr(expr->args, PTR_INT(_arg_no));
> +	if (!arg)
> +		return;
> +
> +	name = expr_to_var(arg);
> +	if (!name)
> +		return;
> +
> +	set_state(my_id, name, NULL, &freed);
> +}

There is a new param_key API which would make this function shorter.

static void free_netdev(struct expression *expr, const char *name, struct symbol *sym, void *data)
{
	set_state(my_id, name, NULL, &freed);
}

Then in the register function you'd add a hooks like this:

	add_function_param_key_hook("free_netdev", &free_netdev, 0, "$", NULL);
	add_function_param_key_hook("free_candev", &free_netdev, 0, "$", NULL);

Of course, you've already written your own code which works so it's
fine.  But that param_key API is really the most awesome thing.

> +
> +static void match_symbol(struct expression *expr)
> +{
> +	const char *parent_netdev, *name;
> +	struct smatch_state *state;
> +

This hook is run for every variable so it's tempting to add a shortcut
here:

	if (!has_states(my_id))
		return;

> +	name = expr_to_var(expr);
> +	if (!name)
> +		return;
> +
> +	parent_netdev = get_parent_netdev_name(expr);
> +	if (!parent_netdev)
> +		return;
> +	
> +	state = get_state(my_id, parent_netdev, NULL);
> +	if (state == &freed)
> +		sm_error("Using %s after free_{netdev,candev}(%s);\n", name, parent_netdev);
> +}
> +
> +void check_uaf_netdev_priv(int id)
> +{
> +	if (option_project != PROJ_KERNEL)
> +		return;
> +
> +	my_id = id;
> +
> +	add_function_hook("free_netdev", &match_free_netdev, NULL);

NULL works but INT_PTR(0) is nicer.  ;)

> +	add_function_hook("free_candev", &match_free_netdev, NULL);
> +	add_modification_hook(my_id, &ok_to_use);
> +	add_hook(&match_symbol, SYM_HOOK);
> +}

Anyway, I think I will probably add the has_states() check but the
rest isn't important.

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-03 15:08 ` Dan Carpenter
@ 2021-08-03 16:03   ` Pavel Skripkin
  2021-08-04  7:05     ` Dan Carpenter
  0 siblings, 1 reply; 8+ messages in thread
From: Pavel Skripkin @ 2021-08-03 16:03 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: smatch

Hi, Dan!

On 8/3/21 6:08 PM, Dan Carpenter wrote:
> Thanks Pavel!
> 
> It looks really nice.  I've applied it.  I'll test it tonight and push
> tomorrow.
> 
> I don't see any major issues with the check at all, but I have a few
> comments below.
> 
> On Tue, Aug 03, 2021 at 12:00:22AM +0300, Pavel Skripkin wrote:
>> +static void match_free_netdev(const char *fn, struct expression *expr, void *_arg_no)
>> +{
>> +	struct expression *arg;
>> +	const char *name;
>> +
>> +	arg = get_argument_from_call_expr(expr->args, PTR_INT(_arg_no));
>> +	if (!arg)
>> +		return;
>> +
>> +	name = expr_to_var(arg);
>> +	if (!name)
>> +		return;
>> +
>> +	set_state(my_id, name, NULL, &freed);
>> +}
> 
> There is a new param_key API which would make this function shorter.
> 
> static void free_netdev(struct expression *expr, const char *name, struct symbol *sym, void *data)
> {
> 	set_state(my_id, name, NULL, &freed);
> }
> 
> Then in the register function you'd add a hooks like this:
> 
> 	add_function_param_key_hook("free_netdev", &free_netdev, 0, "$", NULL);
> 	add_function_param_key_hook("free_candev", &free_netdev, 0, "$", NULL);
> 

I guess, I missed that API, sorry :( Next time I will use it instead.

> Of course, you've already written your own code which works so it's
> fine.  But that param_key API is really the most awesome thing.
> 
>> +
>> +static void match_symbol(struct expression *expr)
>> +{
>> +	const char *parent_netdev, *name;
>> +	struct smatch_state *state;
>> +
> 
> This hook is run for every variable so it's tempting to add a shortcut
> here:
> 
> 	if (!has_states(my_id))
> 		return;
> 
>> +	name = expr_to_var(expr);
>> +	if (!name)
>> +		return;
>> +
>> +	parent_netdev = get_parent_netdev_name(expr);
>> +	if (!parent_netdev)
>> +		return;
>> +	
>> +	state = get_state(my_id, parent_netdev, NULL);
>> +	if (state == &freed)
>> +		sm_error("Using %s after free_{netdev,candev}(%s);\n", name, parent_netdev);
>> +}
>> +
>> +void check_uaf_netdev_priv(int id)
>> +{
>> +	if (option_project != PROJ_KERNEL)
>> +		return;
>> +
>> +	my_id = id;
>> +
>> +	add_function_hook("free_netdev", &match_free_netdev, NULL);
> 
> NULL works but INT_PTR(0) is nicer.  ;)
> 
>> +	add_function_hook("free_candev", &match_free_netdev, NULL);
>> +	add_modification_hook(my_id, &ok_to_use);
>> +	add_hook(&match_symbol, SYM_HOOK);
>> +}
> 
> Anyway, I think I will probably add the has_states() check but the
> rest isn't important.


Thank you for applying and your guidelines, I appreciate it :)



With regards,
Pavel Skripkin

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-03 16:03   ` Pavel Skripkin
@ 2021-08-04  7:05     ` Dan Carpenter
  0 siblings, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2021-08-04  7:05 UTC (permalink / raw)
  To: Pavel Skripkin; +Cc: smatch

On Tue, Aug 03, 2021 at 07:03:59PM +0300, Pavel Skripkin wrote:
> Hi, Dan!
> 
> On 8/3/21 6:08 PM, Dan Carpenter wrote:
> > Thanks Pavel!
> > 
> > It looks really nice.  I've applied it.  I'll test it tonight and push
> > tomorrow.
> > 
> > I don't see any major issues with the check at all, but I have a few
> > comments below.
> > 
> > On Tue, Aug 03, 2021 at 12:00:22AM +0300, Pavel Skripkin wrote:
> > > +static void match_free_netdev(const char *fn, struct expression *expr, void *_arg_no)
> > > +{
> > > +	struct expression *arg;
> > > +	const char *name;
> > > +
> > > +	arg = get_argument_from_call_expr(expr->args, PTR_INT(_arg_no));
> > > +	if (!arg)
> > > +		return;
> > > +
> > > +	name = expr_to_var(arg);
> > > +	if (!name)
> > > +		return;
> > > +
> > > +	set_state(my_id, name, NULL, &freed);
> > > +}
> > 
> > There is a new param_key API which would make this function shorter.
> > 
> > static void free_netdev(struct expression *expr, const char *name, struct symbol *sym, void *data)
> > {
> > 	set_state(my_id, name, NULL, &freed);
> > }
> > 
> > Then in the register function you'd add a hooks like this:
> > 
> > 	add_function_param_key_hook("free_netdev", &free_netdev, 0, "$", NULL);
> > 	add_function_param_key_hook("free_candev", &free_netdev, 0, "$", NULL);
> > 
> 
> I guess, I missed that API, sorry :( Next time I will use it instead.

It's new, I wouldn't have expected anyone to be aware of it yet.  But
it's really nice.

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-02 21:00 [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev Pavel Skripkin
  2021-08-03 15:08 ` Dan Carpenter
@ 2021-08-04 14:35 ` Dan Carpenter
  2021-08-04 14:40   ` Pavel Skripkin
  2021-08-04 14:44   ` Pavel Skripkin
  1 sibling, 2 replies; 8+ messages in thread
From: Dan Carpenter @ 2021-08-04 14:35 UTC (permalink / raw)
  To: Pavel Skripkin; +Cc: smatch

Pushed!

This finds two bugs in my allmodconfig.  They're both real bugs.

drivers/net/ethernet/freescale/fec_main.c:3994 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
drivers/net/ethernet/freescale/fec_main.c:3995 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);

Presumably you already sent fixes or reported these?

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-04 14:35 ` Dan Carpenter
@ 2021-08-04 14:40   ` Pavel Skripkin
  2021-08-04 14:44   ` Pavel Skripkin
  1 sibling, 0 replies; 8+ messages in thread
From: Pavel Skripkin @ 2021-08-04 14:40 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: smatch

On 8/4/21 5:35 PM, Dan Carpenter wrote:
> Pushed!
> 
> This finds two bugs in my allmodconfig.  They're both real bugs.
> 
> drivers/net/ethernet/freescale/fec_main.c:3994 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> drivers/net/ethernet/freescale/fec_main.c:3995 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> 

Wow!

I did't test it with allmodconfig, but I've tested it with my config for 
reproducing syzkaller bugs.

I've fixed few bugs while writing this checker and fixes are already 
upstreamed.

> Presumably you already sent fixes or reported these?
> 
> regards,
> dan carpenter
> 


With regards,
Pavel Skripkin

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-04 14:35 ` Dan Carpenter
  2021-08-04 14:40   ` Pavel Skripkin
@ 2021-08-04 14:44   ` Pavel Skripkin
  2021-08-04 14:55     ` Dan Carpenter
  1 sibling, 1 reply; 8+ messages in thread
From: Pavel Skripkin @ 2021-08-04 14:44 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: smatch

On 8/4/21 5:35 PM, Dan Carpenter wrote:
> Pushed!
> 
> This finds two bugs in my allmodconfig.  They're both real bugs.
> 
> drivers/net/ethernet/freescale/fec_main.c:3994 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> drivers/net/ethernet/freescale/fec_main.c:3995 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> 

I can send patches to fix these errors with Reported-by tag, or You will 
take care of it?


With regards,
Pavel Skripkin

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev
  2021-08-04 14:44   ` Pavel Skripkin
@ 2021-08-04 14:55     ` Dan Carpenter
  0 siblings, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2021-08-04 14:55 UTC (permalink / raw)
  To: Pavel Skripkin; +Cc: smatch

On Wed, Aug 04, 2021 at 05:44:09PM +0300, Pavel Skripkin wrote:
> On 8/4/21 5:35 PM, Dan Carpenter wrote:
> > Pushed!
> > 
> > This finds two bugs in my allmodconfig.  They're both real bugs.
> > 
> > drivers/net/ethernet/freescale/fec_main.c:3994 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> > drivers/net/ethernet/freescale/fec_main.c:3995 fec_drv_remove() error: Using fep after free_{netdev,candev}(ndev);
> > drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> > drivers/net/ethernet/neterion/vxge/vxge-main.c:3518 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> > drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> > drivers/net/ethernet/neterion/vxge/vxge-main.c:3520 vxge_device_unregister() error: Using vdev after free_{netdev,candev}(dev);
> > 
> 
> I can send patches to fix these errors with Reported-by tag, or You will
> take care of it?

Yes, please send the fixes.  These are your discovery.  You get first
dibs.  ;)

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-08-04 14:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-02 21:00 [PATCH] check_netdev_priv: warn about using netdev priv data after free_netdev Pavel Skripkin
2021-08-03 15:08 ` Dan Carpenter
2021-08-03 16:03   ` Pavel Skripkin
2021-08-04  7:05     ` Dan Carpenter
2021-08-04 14:35 ` Dan Carpenter
2021-08-04 14:40   ` Pavel Skripkin
2021-08-04 14:44   ` Pavel Skripkin
2021-08-04 14:55     ` Dan Carpenter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.