linux-api.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] drivers/firewire: fix use/leak of uninitialized stack memory in dispatch_ioctl()
       [not found]   ` <20141111130143.2ff3d42e@kant>
@ 2014-11-11 16:13     ` Stefan Richter
  2014-11-11 16:15       ` [PATCH RFC v1a] firewire: cdev: prevent kernel stack leaking into ioctl arguments Stefan Richter
  2014-11-11 16:16       ` [PATCH RFC v1b] " Stefan Richter
  0 siblings, 2 replies; 4+ messages in thread
From: Stefan Richter @ 2014-11-11 16:13 UTC (permalink / raw)
  To: linux1394-devel; +Cc: linux-api, linux-kernel, David Ramos

Adding Cc: linux-api and lkml, quoting parent message in full

On Nov 11 Stefan Richter wrote:
> On Nov 11 Stefan Richter wrote:
> > On Nov 10 David Ramos wrote:
> > > This patch fixes an uninitialized memory use/leak bug discovered
> > > by our UC-KLEE tool in the 3.16.3 kernel.
> > 
> > There is uninitialized memory use, but no leak.
> 
> Actually there could be leaks too.  If later fw_cdev_event_ are
> generated, they will contain the __u64 closure field that many of the
> ioctl argument structures contain.
> 
> > [...]
> > > If a user carefully crafts an ioctl command such that _IOC_DIR(cmd)
> > > == 0, 'buffer' is left uninitialized. Each of the ioctl_handlers then
> > > accesses the pre-existing stack values, which will cause unpredictable
> > > behavior.
> > 
> > Not all (but indeed almost all) ioctl handlers will use uninitialized
> > memory in that case.  The damage should be marginal though, because all of
> > these handlers must implement sufficient arguments checking anyway.
> > 
> > > This patch checks for an invalid cmd and rejects it with -ENOTTY.
> > 
> > This is not exactly what your patch does.  Rather...
> > 
> > > --- a/drivers/firewire/core-cdev.c
> > > +++ b/drivers/firewire/core-cdev.c
> > > @@ -1632,7 +1632,8 @@ static int dispatch_ioctl(struct client *client,
> > >  	if (fw_device_is_shutdown(client->device))
> > >  		return -ENODEV;
> > >  
> > > -	if (_IOC_TYPE(cmd) != '#' ||
> > > +	if (_IOC_DIR(cmd) == _IOC_NONE ||
> > > +	    _IOC_TYPE(cmd) != '#' ||
> > >  	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
> > >  	    _IOC_SIZE(cmd) > sizeof(buffer))
> > >  		return -ENOTTY;
> > 
> > ...it quits *all* _IOC_NONE ioctls with -ENOTTY, not just invalid ones.
> > If you have a look at the ABI definition in
> > include/uapi/linux/firewire-cdev.h, you will notice that this approach is
> > insufficient.
> 
> I suppose we should add checks
>   1. for _IOC_DIR matching the expected direction depending on _IOC_NR
>      (or at least for presence of _IOC_WRITE when expected), and
>   2. for _IOC_SIZE being at least as big as the minimum unaligned argument
>      size depending on _IOC_NR (at least in case of
>      _IOC_DIR(cmd) & _IOC_WRITE).
> 
> BTW, I don't think it is worthwhile to check for upper bounds of _IOC_SIZE
> besides the present check against sizeof(buffer), since the precise size of
> arguments depends on structure alignment of the user code and on ABI
> version.

I will follow up with two alternative patches for discussion; either one
should fix the issue but they differ in complexity and runtime cost:
    [PATCH RFC v1a] firewire: cdev: prevent kernel stack leaking into ioctl arguments
    [PATCH RFC v1b] firewire: cdev: prevent kernel stack leaking into ioctl arguments
If I receive no feedback to the contrary, I will probably submit patch v1b
to upstream, which is the simpler one of the two.
-- 
Stefan Richter
-=====-====- =-== -=-==
http://arcgraph.de/sr/

------------------------------------------------------------------------------
Comprehensive Server Monitoring with Site24x7.
Monitor 10 servers for $9/Month.
Get alerted through email, SMS, voice calls or mobile push notifications.
Take corrective actions from your mobile device.
http://pubads.g.doubleclick.net/gampad/clk?id=154624111&iu=/4140/ostg.clktrk

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

* [PATCH RFC v1a] firewire: cdev: prevent kernel stack leaking into ioctl arguments
  2014-11-11 16:13     ` [PATCH] drivers/firewire: fix use/leak of uninitialized stack memory in dispatch_ioctl() Stefan Richter
@ 2014-11-11 16:15       ` Stefan Richter
  2014-11-11 16:16       ` [PATCH RFC v1b] " Stefan Richter
  1 sibling, 0 replies; 4+ messages in thread
From: Stefan Richter @ 2014-11-11 16:15 UTC (permalink / raw)
  To: linux1394-devel; +Cc: linux-api, linux-kernel, David Ramos

Found by the UC-KLEE tool:  A user could supply less input to
firewire-cdev ioctls than write- or write/read-type ioctl handlers
expect.  The handlers used data from uninitialized kernel stack then.

This could partially leak back to the user if the kernel subsequently
generated fw_cdev_event_'s (to be read from the firewire-cdev fd)
which notably would contain the _u64 closure field which many of the
ioctl argument structures contain.

The fact that the handlers would act on random garbage input is a
lesser issue since all handlers must check their input anyway.

Remarks:
  - There was never any leak from kernel stack to the ioctl output
    buffer itself.  IOW, it was not possible to read kernel stack by a
    read-type or write/read-type ioctl alone; the leak could at most
    happen in combination with read()ing subsequent event data.
  - The affected character device file interface is specified in
    include/uapi/linux/firewire-cdev.h.  An overview is given in
    Documentation/ABI/stable/firewire-cdev.

This fix uses a lookup table to verify that all ioctl input fields are
indeed written by the user.  Else the ioctl fails with -EINVAL.

Reported-by: David Ramos <daramos@stanford.edu>
Cc: <stable@vger.kernel.org>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
 drivers/firewire/core-cdev.c |   33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -1609,48 +1609,81 @@ static int (* const ioctl_handlers[])(st
 	[0x0a] = ioctl_start_iso,
 	[0x0b] = ioctl_stop_iso,
 	[0x0c] = ioctl_get_cycle_timer,
 	[0x0d] = ioctl_allocate_iso_resource,
 	[0x0e] = ioctl_deallocate_iso_resource,
 	[0x0f] = ioctl_allocate_iso_resource_once,
 	[0x10] = ioctl_deallocate_iso_resource_once,
 	[0x11] = ioctl_get_speed,
 	[0x12] = ioctl_send_broadcast_request,
 	[0x13] = ioctl_send_stream_packet,
 	[0x14] = ioctl_get_cycle_timer2,
 	[0x15] = ioctl_send_phy_packet,
 	[0x16] = ioctl_receive_phy_packets,
 	[0x17] = ioctl_set_iso_channels,
 	[0x18] = ioctl_flush_iso,
 };
 
+static const char minimum_user_input[] = {
+	[0x00] = 32,	/* _IOWR fw_cdev_get_info			*/
+	[0x01] = 36,	/*  _IOW fw_cdev_send_request			*/
+	[0x02] = 20,	/* _IOWR fw_cdev_allocate			*/
+	[0x03] =  4,	/*  _IOW fw_cdev_deallocate			*/
+	[0x04] = 20,	/*  _IOW fw_cdev_send_response			*/
+	[0x05] =  4,	/*  _IOW fw_cdev_initiate_bus_reset		*/
+	[0x06] = 20,	/* _IOWR fw_cdev_add_descriptor			*/
+	[0x07] =  4,	/*  _IOW fw_cdev_remove_descriptor		*/
+	[0x08] = 24,	/* _IOWR fw_cdev_create_iso_context		*/
+	[0x09] = 24,	/* _IOWR fw_cdev_queue_iso			*/
+	[0x0a] = 16,	/*  _IOW fw_cdev_start_iso			*/
+	[0x0b] =  4,	/*  _IOW fw_cdev_stop_iso			*/
+	[0x0c] =  0,	/*  _IOR fw_cdev_get_cycle_timer		*/
+	[0x0d] = 20,	/* _IOWR fw_cdev_allocate_iso_resource		*/
+	[0x0e] =  4,	/*  _IOW fw_cdev_deallocate			*/
+	[0x0f] = 20,	/*  _IOW fw_cdev_allocate_iso_resource		*/
+	[0x10] = 20,	/*  _IOW fw_cdev_allocate_iso_resource		*/
+	[0x11] =  0,	/*   _IO					*/
+	[0x12] = 36,	/*  _IOW struct fw_cdev_send_request		*/
+	[0x13] = 40,	/*  _IOW struct fw_cdev_send_stream_packet	*/
+	[0x14] = 16,	/* _IOWR fw_cdev_get_cycle_timer2		*/
+	[0x15] = 20,	/* _IOWR fw_cdev_send_phy_packet		*/
+	[0x16] =  8,	/*  _IOW fw_cdev_receive_phy_packets		*/
+	[0x17] = 12,	/*  _IOW fw_cdev_set_iso_channels		*/
+	[0x18] =  4,	/*  _IOW struct fw_cdev_flush_iso		*/
+};
+
 static int dispatch_ioctl(struct client *client,
 			  unsigned int cmd, void __user *arg)
 {
 	union ioctl_arg buffer;
 	int ret;
 
 	if (fw_device_is_shutdown(client->device))
 		return -ENODEV;
 
 	if (_IOC_TYPE(cmd) != '#' ||
 	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
 	    _IOC_SIZE(cmd) > sizeof(buffer))
 		return -ENOTTY;
 
+	if (minimum_user_input[_IOC_NR(cmd)])
+		if (!(_IOC_DIR(cmd) & _IOC_WRITE) ||
+		    _IOC_SIZE(cmd) < minimum_user_input[_IOC_NR(cmd)])
+			return -EINVAL;
+
 	if (_IOC_DIR(cmd) == _IOC_READ)
 		memset(&buffer, 0, _IOC_SIZE(cmd));
 
 	if (_IOC_DIR(cmd) & _IOC_WRITE)
 		if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
 			return -EFAULT;
 
 	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
 	if (ret < 0)
 		return ret;
 
 	if (_IOC_DIR(cmd) & _IOC_READ)
 		if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
 			return -EFAULT;
 
 	return ret;
 }


-- 
Stefan Richter
-=====-====- =-== -=-==
http://arcgraph.de/sr/

------------------------------------------------------------------------------
Comprehensive Server Monitoring with Site24x7.
Monitor 10 servers for $9/Month.
Get alerted through email, SMS, voice calls or mobile push notifications.
Take corrective actions from your mobile device.
http://pubads.g.doubleclick.net/gampad/clk?id=154624111&iu=/4140/ostg.clktrk

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

* [PATCH RFC v1b] firewire: cdev: prevent kernel stack leaking into ioctl arguments
  2014-11-11 16:13     ` [PATCH] drivers/firewire: fix use/leak of uninitialized stack memory in dispatch_ioctl() Stefan Richter
  2014-11-11 16:15       ` [PATCH RFC v1a] firewire: cdev: prevent kernel stack leaking into ioctl arguments Stefan Richter
@ 2014-11-11 16:16       ` Stefan Richter
  2014-11-11 18:22         ` Clemens Ladisch
  1 sibling, 1 reply; 4+ messages in thread
From: Stefan Richter @ 2014-11-11 16:16 UTC (permalink / raw)
  To: linux1394-devel; +Cc: linux-api, linux-kernel, David Ramos

Found by the UC-KLEE tool:  A user could supply less input to
firewire-cdev ioctls than write- or write/read-type ioctl handlers
expect.  The handlers used data from uninitialized kernel stack then.

This could partially leak back to the user if the kernel subsequently
generated fw_cdev_event_'s (to be read from the firewire-cdev fd)
which notably would contain the _u64 closure field which many of the
ioctl argument structures contain.

The fact that the handlers would act on random garbage input is a
lesser issue since all handlers must check their input anyway.

Remarks:
  - There was never any leak from kernel stack to the ioctl output
    buffer itself.  IOW, it was not possible to read kernel stack by a
    read-type or write/read-type ioctl alone; the leak could at most
    happen in combination with read()ing subsequent event data.
  - The affected character device file interface is specified in
    include/uapi/linux/firewire-cdev.h.  An overview is given in
    Documentation/ABI/stable/firewire-cdev.

This fix simply always null-initializes the entire ioctl argument buffer
regardless of the actual length of expected user input.  That is, a
runtime overhead of memset(..., 40) is added to each firewirew-cdev
ioctl() call.

Reported-by: David Ramos <daramos@stanford.edu>
Cc: <stable@vger.kernel.org>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
 drivers/firewire/core-cdev.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -1625,32 +1625,31 @@ static int (* const ioctl_handlers[])(st
 
 static int dispatch_ioctl(struct client *client,
 			  unsigned int cmd, void __user *arg)
 {
 	union ioctl_arg buffer;
 	int ret;
 
 	if (fw_device_is_shutdown(client->device))
 		return -ENODEV;
 
 	if (_IOC_TYPE(cmd) != '#' ||
 	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
 	    _IOC_SIZE(cmd) > sizeof(buffer))
 		return -ENOTTY;
 
-	if (_IOC_DIR(cmd) == _IOC_READ)
-		memset(&buffer, 0, _IOC_SIZE(cmd));
+	memset(&buffer, 0, sizeof(buffer));
 
 	if (_IOC_DIR(cmd) & _IOC_WRITE)
 		if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
 			return -EFAULT;
 
 	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
 	if (ret < 0)
 		return ret;
 
 	if (_IOC_DIR(cmd) & _IOC_READ)
 		if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
 			return -EFAULT;
 
 	return ret;
 }


-- 
Stefan Richter
-=====-====- =-== -=-==
http://arcgraph.de/sr/

------------------------------------------------------------------------------
Comprehensive Server Monitoring with Site24x7.
Monitor 10 servers for $9/Month.
Get alerted through email, SMS, voice calls or mobile push notifications.
Take corrective actions from your mobile device.
http://pubads.g.doubleclick.net/gampad/clk?id=154624111&iu=/4140/ostg.clktrk

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

* Re: [PATCH RFC v1b] firewire: cdev: prevent kernel stack leaking into ioctl arguments
  2014-11-11 16:16       ` [PATCH RFC v1b] " Stefan Richter
@ 2014-11-11 18:22         ` Clemens Ladisch
  0 siblings, 0 replies; 4+ messages in thread
From: Clemens Ladisch @ 2014-11-11 18:22 UTC (permalink / raw)
  To: Stefan Richter, linux1394-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, David Ramos

Stefan Richter wrote:
> This fix simply always null-initializes the entire ioctl argument buffer
> regardless of the actual length of expected user input.  That is, a
> runtime overhead of memset(..., 40) is added to each firewirew-cdev
> ioctl() call.

This part of the stack is most likely to be already in the cache.


Regards,
Clemens

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

end of thread, other threads:[~2014-11-11 18:22 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <55D28623-C1C7-47D2-9638-0B8FD0733C48@stanford.edu>
     [not found] ` <20141111010340.3329bbd7@kant>
     [not found]   ` <20141111130143.2ff3d42e@kant>
2014-11-11 16:13     ` [PATCH] drivers/firewire: fix use/leak of uninitialized stack memory in dispatch_ioctl() Stefan Richter
2014-11-11 16:15       ` [PATCH RFC v1a] firewire: cdev: prevent kernel stack leaking into ioctl arguments Stefan Richter
2014-11-11 16:16       ` [PATCH RFC v1b] " Stefan Richter
2014-11-11 18:22         ` Clemens Ladisch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).