Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Adam Crosser and committed by
Greg Kroah-Hartman
d1857f82 5cefb52c

+81 -23
+73 -23
drivers/gpib/common/gpib_os.c
··· 888 888 if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) 889 889 return -EINVAL; 890 890 891 - desc = handle_to_descriptor(file_priv, read_cmd.handle); 892 - if (!desc) 893 - return -EINVAL; 894 - 895 891 if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) 896 892 return -EFAULT; 897 893 ··· 899 903 /* Check write access to buffer */ 900 904 if (!access_ok(userbuf, remain)) 901 905 return -EFAULT; 906 + 907 + /* Lock descriptors to prevent concurrent close from freeing descriptor */ 908 + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) 909 + return -ERESTARTSYS; 910 + desc = handle_to_descriptor(file_priv, read_cmd.handle); 911 + if (!desc) { 912 + mutex_unlock(&file_priv->descriptors_mutex); 913 + return -EINVAL; 914 + } 915 + atomic_inc(&desc->descriptor_busy); 916 + mutex_unlock(&file_priv->descriptors_mutex); 902 917 903 918 atomic_set(&desc->io_in_progress, 1); 904 919 ··· 944 937 retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); 945 938 946 939 atomic_set(&desc->io_in_progress, 0); 940 + atomic_dec(&desc->descriptor_busy); 947 941 948 942 wake_up_interruptible(&board->wait); 949 943 if (retval) ··· 972 964 if (cmd.completed_transfer_count > cmd.requested_transfer_count) 973 965 return -EINVAL; 974 966 975 - desc = handle_to_descriptor(file_priv, cmd.handle); 976 - if (!desc) 977 - return -EINVAL; 978 - 979 967 userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; 980 968 userbuf += cmd.completed_transfer_count; 981 969 ··· 983 979 /* Check read access to buffer */ 984 980 if (!access_ok(userbuf, remain)) 985 981 return -EFAULT; 982 + 983 + /* Lock descriptors to prevent concurrent close from freeing descriptor */ 984 + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) 985 + return -ERESTARTSYS; 986 + desc = handle_to_descriptor(file_priv, cmd.handle); 987 + if (!desc) { 988 + mutex_unlock(&file_priv->descriptors_mutex); 989 + return -EINVAL; 990 + } 991 + atomic_inc(&desc->descriptor_busy); 992 + mutex_unlock(&file_priv->descriptors_mutex); 986 993 987 994 /* 988 995 * Write buffer loads till we empty the user supplied buffer. ··· 1018 1003 userbuf += bytes_written; 1019 1004 if (retval < 0) { 1020 1005 atomic_set(&desc->io_in_progress, 0); 1006 + atomic_dec(&desc->descriptor_busy); 1021 1007 1022 1008 wake_up_interruptible(&board->wait); 1023 1009 break; ··· 1038 1022 */ 1039 1023 if (!no_clear_io_in_prog || fault) 1040 1024 atomic_set(&desc->io_in_progress, 0); 1025 + atomic_dec(&desc->descriptor_busy); 1041 1026 1042 1027 wake_up_interruptible(&board->wait); 1043 1028 if (fault) ··· 1064 1047 if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) 1065 1048 return -EINVAL; 1066 1049 1067 - desc = handle_to_descriptor(file_priv, write_cmd.handle); 1068 - if (!desc) 1069 - return -EINVAL; 1070 - 1071 1050 userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; 1072 1051 userbuf += write_cmd.completed_transfer_count; 1073 1052 ··· 1072 1059 /* Check read access to buffer */ 1073 1060 if (!access_ok(userbuf, remain)) 1074 1061 return -EFAULT; 1062 + 1063 + /* Lock descriptors to prevent concurrent close from freeing descriptor */ 1064 + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) 1065 + return -ERESTARTSYS; 1066 + desc = handle_to_descriptor(file_priv, write_cmd.handle); 1067 + if (!desc) { 1068 + mutex_unlock(&file_priv->descriptors_mutex); 1069 + return -EINVAL; 1070 + } 1071 + atomic_inc(&desc->descriptor_busy); 1072 + mutex_unlock(&file_priv->descriptors_mutex); 1075 1073 1076 1074 atomic_set(&desc->io_in_progress, 1); 1077 1075 ··· 1118 1094 fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); 1119 1095 1120 1096 atomic_set(&desc->io_in_progress, 0); 1097 + atomic_dec(&desc->descriptor_busy); 1121 1098 1122 1099 wake_up_interruptible(&board->wait); 1123 1100 if (fault) ··· 1301 1276 { 1302 1277 struct gpib_close_dev_ioctl cmd; 1303 1278 struct gpib_file_private *file_priv = filep->private_data; 1279 + struct gpib_descriptor *desc; 1280 + unsigned int pad; 1281 + int sad; 1304 1282 int retval; 1305 1283 1306 1284 retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); ··· 1312 1284 1313 1285 if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) 1314 1286 return -EINVAL; 1315 - if (!file_priv->descriptors[cmd.handle]) 1287 + 1288 + mutex_lock(&file_priv->descriptors_mutex); 1289 + desc = file_priv->descriptors[cmd.handle]; 1290 + if (!desc) { 1291 + mutex_unlock(&file_priv->descriptors_mutex); 1316 1292 return -EINVAL; 1317 - 1318 - retval = decrement_open_device_count(board, &board->device_list, 1319 - file_priv->descriptors[cmd.handle]->pad, 1320 - file_priv->descriptors[cmd.handle]->sad); 1321 - if (retval < 0) 1322 - return retval; 1323 - 1324 - kfree(file_priv->descriptors[cmd.handle]); 1293 + } 1294 + if (atomic_read(&desc->descriptor_busy)) { 1295 + mutex_unlock(&file_priv->descriptors_mutex); 1296 + return -EBUSY; 1297 + } 1298 + /* Remove from table while holding lock to prevent new IO from starting */ 1325 1299 file_priv->descriptors[cmd.handle] = NULL; 1300 + pad = desc->pad; 1301 + sad = desc->sad; 1302 + mutex_unlock(&file_priv->descriptors_mutex); 1326 1303 1327 - return 0; 1304 + retval = decrement_open_device_count(board, &board->device_list, pad, sad); 1305 + 1306 + kfree(desc); 1307 + return retval; 1328 1308 } 1329 1309 1330 1310 static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) ··· 1367 1331 if (retval) 1368 1332 return -EFAULT; 1369 1333 1334 + /* 1335 + * Lock descriptors to prevent concurrent close from freeing 1336 + * descriptor. ibwait() releases big_gpib_mutex when wait_mask 1337 + * is non-zero, so desc must be pinned with descriptor_busy. 1338 + */ 1339 + mutex_lock(&file_priv->descriptors_mutex); 1370 1340 desc = handle_to_descriptor(file_priv, wait_cmd.handle); 1371 - if (!desc) 1341 + if (!desc) { 1342 + mutex_unlock(&file_priv->descriptors_mutex); 1372 1343 return -EINVAL; 1344 + } 1345 + atomic_inc(&desc->descriptor_busy); 1346 + mutex_unlock(&file_priv->descriptors_mutex); 1373 1347 1374 1348 retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, 1375 1349 wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); 1350 + 1351 + atomic_dec(&desc->descriptor_busy); 1352 + 1376 1353 if (retval < 0) 1377 1354 return retval; 1378 1355 ··· 2084 2035 desc->is_board = 0; 2085 2036 desc->autopoll_enabled = 0; 2086 2037 atomic_set(&desc->io_in_progress, 0); 2038 + atomic_set(&desc->descriptor_busy, 0); 2087 2039 } 2088 2040 2089 2041 int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module)
+8
drivers/gpib/include/gpib_types.h
··· 364 364 unsigned int pad; /* primary gpib address */ 365 365 int sad; /* secondary gpib address (negative means disabled) */ 366 366 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; 367 375 unsigned is_board : 1; 368 376 unsigned autopoll_enabled : 1; 369 377 };