drivers/firewire: fix use/leak of uninitialized stack memory in dispatch_ioctl()
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): 20141111010340.3329bbd7@kant
A message that is not in the archive (sent off-list or not yet received): 20141111130143.2ff3d42e@kant
Adding Cc: linux-api and lkml, quoting parent message in full
Quoted 50 lines · On Nov 11 Stefan Richter wrote:
Quoted 5 lines · On Nov 11 Stefan Richter wrote:
Quoted 2 lines · 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__u64closure field that many of the ioctl argument structures contain.Quoted 20 lines
[...]
Quoted 4 lines
If a user carefully crafts an ioctl command such that
_IOC_DIR(cmd)== 0, 'buffer' is left uninitialized. Each of theioctl_handlersthen 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.
Quoted 1 line
This patch checks for an invalid cmd and rejects it with
-ENOTTY.This is not exactly what your patch does. Rather...
Quoted 6 lines
drivers/firewire/core-cdev.c +0
-0@@ -1632,7 +1632,8 @@ static int dispatch_ioctl(struct client *client,if (fw_device_is_shutdown (client->device))return -ENODEV ;Quoted 11 lines
drivers/firewire/core-cdev.c static int dispatch_ioctl(struct client *client, · line 1639
Quoted 6 lines
drivers/firewire/core-cdev.c +2
-1-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_NONEioctls with-ENOTTY, not just invalid ones. If you have a look at the ABI definition ininclude/uapi/linux/firewire-cdev.h, you will notice that this approach is insufficient.I suppose we should add checks
1. for_IOC_DIRmatching the expected direction depending on_IOC_NR(or at least for presence of_IOC_WRITEwhen expected), and
2. for_IOC_SIZEbeing at least as big as the minimum unaligned argument size depending on_IOC_NR(at least in case of_IOC_DIR(cmd)&_IOC_WRITE).
Quoted 4 lines
BTW, I don't think it is worthwhile to check for upper bounds of
_IOC_SIZEbesides 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.
Signature · 10 lines
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
RFC PATCH 1/1 firewire: cdev: prevent kernel stack leaking into ioctl arguments
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.
--- drivers/firewire/core-cdev.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
drivers/firewire/core-cdev.c +33 -0
@@ -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; }
Signature · 10 lines
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
RFC PATCH 1/1 firewire: cdev: prevent kernel stack leaking into ioctl arguments
In reply to Stefan RichterFound 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.
--- drivers/firewire/core-cdev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/firewire/core-cdev.c +1 -2
@@ -1625,32 +1625,31 @@ static int (* const ioctl_handlers[])(ststatic 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; }
Signature · 10 lines
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
Re: [PATCH RFC v1b] firewire: cdev: prevent kernel stack leaking into ioctl arguments
Quoted 4 lines · Stefan Richter
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-cdevioctl()call.
This part of the stack is most likely to be already in the cache.
Regards,
Clemens