Skip to content

Commit d1857f8

Browse files
UNC1739gregkh
authored andcommitted
gpib: fix use-after-free in IO ioctl handlers
The IBRD, IBWRT, IBCMD, and IBWAIT ioctl handlers use a gpib_descriptor pointer after board->big_gpib_mutex has been released. A concurrent IBCLOSEDEV ioctl can free the descriptor via close_dev_ioctl() during this window, causing a use-after-free. The IO handlers (read_ioctl, write_ioctl, command_ioctl) explicitly release big_gpib_mutex before calling their handler. wait_ioctl() is called with big_gpib_mutex held, but ibwait() releases it internally when wait_mask is non-zero. In all four cases, the descriptor pointer obtained from handle_to_descriptor() becomes unprotected. Fix this by introducing a kernel-only descriptor_busy reference count in struct gpib_descriptor. Each handler atomically increments descriptor_busy under file_priv->descriptors_mutex before releasing the lock, and decrements it when done. close_dev_ioctl() checks descriptor_busy under the same lock and rejects the close with -EBUSY if the count is non-zero. A reference count rather than a simple flag is necessary because multiple handlers can operate on the same descriptor concurrently (e.g. IBRD and IBWAIT on the same handle from different threads). A separate counter is needed because io_in_progress can be cleared from unprivileged userspace via the IBWAIT ioctl (through general_ibstatus() with set_mask containing CMPL), which would allow an attacker to bypass a check based solely on io_in_progress. The new descriptor_busy counter is only modified by the kernel IO paths. The lock ordering is consistent (big_gpib_mutex -> descriptors_mutex) and the handlers only hold descriptors_mutex briefly during the lookup, so there is no deadlock risk and no impact on IO throughput. Signed-off-by: Adam Crosser <adam.crosser@praetorian.com> Cc: stable <stable@kernel.org> Reviewed-by: Dave Penkler <dpenkler@gmail.com> Tested-by: Dave Penkler <dpenkler@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5cefb52 commit d1857f8

2 files changed

Lines changed: 81 additions & 23 deletions

File tree

drivers/gpib/common/gpib_os.c

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -888,10 +888,6 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
888888
if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count)
889889
return -EINVAL;
890890

891-
desc = handle_to_descriptor(file_priv, read_cmd.handle);
892-
if (!desc)
893-
return -EINVAL;
894-
895891
if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr)))
896892
return -EFAULT;
897893

@@ -904,6 +900,17 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
904900
if (!access_ok(userbuf, remain))
905901
return -EFAULT;
906902

903+
/* Lock descriptors to prevent concurrent close from freeing descriptor */
904+
if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
905+
return -ERESTARTSYS;
906+
desc = handle_to_descriptor(file_priv, read_cmd.handle);
907+
if (!desc) {
908+
mutex_unlock(&file_priv->descriptors_mutex);
909+
return -EINVAL;
910+
}
911+
atomic_inc(&desc->descriptor_busy);
912+
mutex_unlock(&file_priv->descriptors_mutex);
913+
907914
atomic_set(&desc->io_in_progress, 1);
908915

909916
/* Read buffer loads till we fill the user supplied buffer */
@@ -937,6 +944,7 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
937944
retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd));
938945

939946
atomic_set(&desc->io_in_progress, 0);
947+
atomic_dec(&desc->descriptor_busy);
940948

941949
wake_up_interruptible(&board->wait);
942950
if (retval)
@@ -964,10 +972,6 @@ static int command_ioctl(struct gpib_file_private *file_priv,
964972
if (cmd.completed_transfer_count > cmd.requested_transfer_count)
965973
return -EINVAL;
966974

967-
desc = handle_to_descriptor(file_priv, cmd.handle);
968-
if (!desc)
969-
return -EINVAL;
970-
971975
userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr;
972976
userbuf += cmd.completed_transfer_count;
973977

@@ -980,6 +984,17 @@ static int command_ioctl(struct gpib_file_private *file_priv,
980984
if (!access_ok(userbuf, remain))
981985
return -EFAULT;
982986

987+
/* Lock descriptors to prevent concurrent close from freeing descriptor */
988+
if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
989+
return -ERESTARTSYS;
990+
desc = handle_to_descriptor(file_priv, cmd.handle);
991+
if (!desc) {
992+
mutex_unlock(&file_priv->descriptors_mutex);
993+
return -EINVAL;
994+
}
995+
atomic_inc(&desc->descriptor_busy);
996+
mutex_unlock(&file_priv->descriptors_mutex);
997+
983998
/*
984999
* Write buffer loads till we empty the user supplied buffer.
9851000
* Call drivers at least once, even if remain is zero, in
@@ -1003,6 +1018,7 @@ static int command_ioctl(struct gpib_file_private *file_priv,
10031018
userbuf += bytes_written;
10041019
if (retval < 0) {
10051020
atomic_set(&desc->io_in_progress, 0);
1021+
atomic_dec(&desc->descriptor_busy);
10061022

10071023
wake_up_interruptible(&board->wait);
10081024
break;
@@ -1022,6 +1038,7 @@ static int command_ioctl(struct gpib_file_private *file_priv,
10221038
*/
10231039
if (!no_clear_io_in_prog || fault)
10241040
atomic_set(&desc->io_in_progress, 0);
1041+
atomic_dec(&desc->descriptor_busy);
10251042

10261043
wake_up_interruptible(&board->wait);
10271044
if (fault)
@@ -1047,10 +1064,6 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
10471064
if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count)
10481065
return -EINVAL;
10491066

1050-
desc = handle_to_descriptor(file_priv, write_cmd.handle);
1051-
if (!desc)
1052-
return -EINVAL;
1053-
10541067
userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr;
10551068
userbuf += write_cmd.completed_transfer_count;
10561069

@@ -1060,6 +1073,17 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
10601073
if (!access_ok(userbuf, remain))
10611074
return -EFAULT;
10621075

1076+
/* Lock descriptors to prevent concurrent close from freeing descriptor */
1077+
if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
1078+
return -ERESTARTSYS;
1079+
desc = handle_to_descriptor(file_priv, write_cmd.handle);
1080+
if (!desc) {
1081+
mutex_unlock(&file_priv->descriptors_mutex);
1082+
return -EINVAL;
1083+
}
1084+
atomic_inc(&desc->descriptor_busy);
1085+
mutex_unlock(&file_priv->descriptors_mutex);
1086+
10631087
atomic_set(&desc->io_in_progress, 1);
10641088

10651089
/* Write buffer loads till we empty the user supplied buffer */
@@ -1094,6 +1118,7 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
10941118
fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd));
10951119

10961120
atomic_set(&desc->io_in_progress, 0);
1121+
atomic_dec(&desc->descriptor_busy);
10971122

10981123
wake_up_interruptible(&board->wait);
10991124
if (fault)
@@ -1276,6 +1301,9 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne
12761301
{
12771302
struct gpib_close_dev_ioctl cmd;
12781303
struct gpib_file_private *file_priv = filep->private_data;
1304+
struct gpib_descriptor *desc;
1305+
unsigned int pad;
1306+
int sad;
12791307
int retval;
12801308

12811309
retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
@@ -1284,19 +1312,27 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne
12841312

12851313
if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS)
12861314
return -EINVAL;
1287-
if (!file_priv->descriptors[cmd.handle])
1288-
return -EINVAL;
12891315

1290-
retval = decrement_open_device_count(board, &board->device_list,
1291-
file_priv->descriptors[cmd.handle]->pad,
1292-
file_priv->descriptors[cmd.handle]->sad);
1293-
if (retval < 0)
1294-
return retval;
1295-
1296-
kfree(file_priv->descriptors[cmd.handle]);
1316+
mutex_lock(&file_priv->descriptors_mutex);
1317+
desc = file_priv->descriptors[cmd.handle];
1318+
if (!desc) {
1319+
mutex_unlock(&file_priv->descriptors_mutex);
1320+
return -EINVAL;
1321+
}
1322+
if (atomic_read(&desc->descriptor_busy)) {
1323+
mutex_unlock(&file_priv->descriptors_mutex);
1324+
return -EBUSY;
1325+
}
1326+
/* Remove from table while holding lock to prevent new IO from starting */
12971327
file_priv->descriptors[cmd.handle] = NULL;
1328+
pad = desc->pad;
1329+
sad = desc->sad;
1330+
mutex_unlock(&file_priv->descriptors_mutex);
12981331

1299-
return 0;
1332+
retval = decrement_open_device_count(board, &board->device_list, pad, sad);
1333+
1334+
kfree(desc);
1335+
return retval;
13001336
}
13011337

13021338
static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg)
@@ -1331,12 +1367,25 @@ static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
13311367
if (retval)
13321368
return -EFAULT;
13331369

1370+
/*
1371+
* Lock descriptors to prevent concurrent close from freeing
1372+
* descriptor. ibwait() releases big_gpib_mutex when wait_mask
1373+
* is non-zero, so desc must be pinned with descriptor_busy.
1374+
*/
1375+
mutex_lock(&file_priv->descriptors_mutex);
13341376
desc = handle_to_descriptor(file_priv, wait_cmd.handle);
1335-
if (!desc)
1377+
if (!desc) {
1378+
mutex_unlock(&file_priv->descriptors_mutex);
13361379
return -EINVAL;
1380+
}
1381+
atomic_inc(&desc->descriptor_busy);
1382+
mutex_unlock(&file_priv->descriptors_mutex);
13371383

13381384
retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask,
13391385
wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc);
1386+
1387+
atomic_dec(&desc->descriptor_busy);
1388+
13401389
if (retval < 0)
13411390
return retval;
13421391

@@ -2035,6 +2084,7 @@ void init_gpib_descriptor(struct gpib_descriptor *desc)
20352084
desc->is_board = 0;
20362085
desc->autopoll_enabled = 0;
20372086
atomic_set(&desc->io_in_progress, 0);
2087+
atomic_set(&desc->descriptor_busy, 0);
20382088
}
20392089

20402090
int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module)

drivers/gpib/include/gpib_types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,14 @@ struct gpib_descriptor {
364364
unsigned int pad; /* primary gpib address */
365365
int sad; /* secondary gpib address (negative means disabled) */
366366
atomic_t io_in_progress;
367+
/*
368+
* Kernel-only reference count to prevent descriptor from being
369+
* freed while IO handlers hold a pointer to it. Incremented
370+
* before each IO operation, decremented when done. Unlike
371+
* io_in_progress, this cannot be modified from userspace via
372+
* general_ibstatus().
373+
*/
374+
atomic_t descriptor_busy;
367375
unsigned is_board : 1;
368376
unsigned autopoll_enabled : 1;
369377
};

0 commit comments

Comments
 (0)