Ticket #70: 0001-Linux-Fix-70-race-condition-in-sysfs_get_device_list.patch
| File 0001-Linux-Fix-70-race-condition-in-sysfs_get_device_list.patch, 8.4 KB (added by stuge, 2 years ago) |
|---|
-
libusb/os/linux_usbfs.c
From c64e9f36b39c5b692ba48ce328dfd912d67eb2c1 Mon Sep 17 00:00:00 2001 From: Alan Ott <alan@signal11.us> Date: Wed, 5 Jan 2011 00:37:28 -0500 Subject: [PATCH] Linux: Fix #70 race condition in sysfs_get_device_list() Changed the way libusb chooses between using sysfs and usbfs for information about the attached devies. Using the old method, a race condition could occur if a device was unplugged just before (or during) the call to libusb_get_device_list(), corrupting the internal sysfs_can_relate_devices and sysfs_has_descriptors variables, and preventing libusb_get_device_list() from working in future calls. The old method was based on the assumption that if certain sysfs files (eg: busnum) were not able to be opened, that it indicated an inadequacy of sysfs (ie: the running kernel's sysfs version did not contain those files), when in reality, those files couldn't be opened because the device had been unplugged. The new method checks the adequacy of sysfs during libusb_init() (op_init()) and if a sysfs file cannot be opened, it is now assumed that it is because the device has been unplugged, not because sysfs is inadequate. Signed-off-by: Alan Ott <alan@signal11.us> [stuge: Include closedir() bugfix posted in ticket by arne] --- libusb/os/linux_usbfs.c | 126 +++++++++++++++++++++++++++++++++++------------ 1 files changed, 95 insertions(+), 31 deletions(-) diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index 72db57a..e4bdcde 100644
a b static clockid_t monotonic_clkid = -1; 95 95 96 96 /* do we have a busnum to relate devices? this also implies that we can read 97 97 * the active configuration through bConfigurationValue */ 98 static int sysfs_can_relate_devices = -1;98 static int sysfs_can_relate_devices = 0; 99 99 100 100 /* do we have a descriptors file? */ 101 static int sysfs_has_descriptors = -1;101 static int sysfs_has_descriptors = 0; 102 102 103 103 struct linux_device_priv { 104 104 char *sysfs_dir; … … static int check_flag_bulk_continuation(void) 242 242 return 1; 243 243 } 244 244 245 /* Return 1 if filename exists inside dirname in sysfs. 246 SYSFS_DEVICE_PATH is assumed to be the beginning of the path. */ 247 static int sysfs_has_file(const char *dirname, const char *filename) 248 { 249 struct stat statbuf; 250 char path[PATH_MAX]; 251 int r; 252 253 snprintf(path, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, dirname, filename); 254 r = stat(path, &statbuf); 255 if (r == 0 && S_ISREG(statbuf.st_mode)) 256 return 1; 257 258 return 0; 259 } 260 245 261 static int op_init(struct libusb_context *ctx) 246 262 { 247 263 struct stat statbuf; … … static int op_init(struct libusb_context *ctx) 269 285 270 286 r = stat(SYSFS_DEVICE_PATH, &statbuf); 271 287 if (r == 0 && S_ISDIR(statbuf.st_mode)) { 288 DIR *devices = opendir(SYSFS_DEVICE_PATH); 289 struct dirent *entry; 290 272 291 usbi_dbg("found usb devices in sysfs"); 292 293 if (!devices) { 294 usbi_err(ctx, "opendir devices failed errno=%d", errno); 295 return LIBUSB_ERROR_IO; 296 } 297 298 /* Make sure sysfs supports all the required files. If it 299 * does not, then usbfs will be used instead. Determine 300 * this by looping through the directories in 301 * SYSFS_DEVICE_PATH. With the assumption that there will 302 * always be subdirectories of the name usbN (usb1, usb2, 303 * etc) representing the root hubs, check the usbN 304 * subdirectories to see if they have all the needed files. 305 * This algorithm uses the usbN subdirectories (root hubs) 306 * because a device disconnection will cause a race 307 * condition regarding which files are available, sometimes 308 * causing an incorrect result. The root hubs are used 309 * because it is assumed that they will always be present. 310 * See the "sysfs vs usbfs" comment at the top of this file 311 * for more details. */ 312 while ((entry = readdir(devices))) { 313 int has_busnum=0, has_devnum=0, has_descriptors=0; 314 int has_configuration_value=0; 315 316 /* Only check the usbN directories. */ 317 if (strncmp(entry->d_name, "usb", 3) != 0) 318 continue; 319 320 /* Check for the files libusb needs from sysfs. */ 321 has_busnum = sysfs_has_file(entry->d_name, "busnum"); 322 has_devnum = sysfs_has_file(entry->d_name, "devnum"); 323 has_descriptors = sysfs_has_file(entry->d_name, "descriptors"); 324 has_configuration_value = sysfs_has_file(entry->d_name, "bConfigurationValue"); 325 326 if (has_busnum && has_devnum && has_configuration_value) 327 sysfs_can_relate_devices = 1; 328 if (has_descriptors) 329 sysfs_has_descriptors = 1; 330 331 /* Only need to check until we've found ONE device which 332 has all the attributes. */ 333 if (sysfs_has_descriptors && sysfs_can_relate_devices) 334 break; 335 } 336 closedir(devices); 337 338 /* Only use sysfs descriptors if the rest of 339 sysfs will work for libusb. */ 340 if (!sysfs_can_relate_devices) 341 sysfs_has_descriptors = 0; 273 342 } else { 274 343 usbi_dbg("sysfs usb info not available"); 275 344 sysfs_has_descriptors = 0; … … out: 930 999 } 931 1000 932 1001 static int sysfs_scan_device(struct libusb_context *ctx, 933 struct discovered_devs **_discdevs, const char *devname, 934 int *usbfs_fallback) 1002 struct discovered_devs **_discdevs, const char *devname) 935 1003 { 936 1004 int r; 937 1005 FILE *fd; … … static int sysfs_scan_device(struct libusb_context *ctx, 962 1030 fd = fopen(filename, "r"); 963 1031 if (!fd) { 964 1032 if (errno == ENOENT) { 965 usbi_dbg("busnum not found, cannot relate sysfs to usbfs, " 966 "falling back on pure usbfs"); 967 sysfs_can_relate_devices = 0; 968 *usbfs_fallback = 1; 969 return LIBUSB_ERROR_OTHER; 1033 /* busnum doesn't exist. Assume the device has been 1034 disconnected (unplugged). */ 1035 return LIBUSB_ERROR_NO_DEVICE; 970 1036 } 971 1037 usbi_err(ctx, "open busnum failed, errno=%d", errno); 972 1038 return LIBUSB_ERROR_IO; 973 1039 } 974 1040 975 sysfs_can_relate_devices = 1;976 977 1041 r = fscanf(fd, "%d", &busnum); 978 1042 fclose(fd); 979 1043 if (r != 1) { 980 1044 usbi_err(ctx, "fscanf busnum returned %d, errno=%d", r, errno); 981 return LIBUSB_ERROR_ IO;1045 return LIBUSB_ERROR_NO_DEVICE; 982 1046 } 983 1047 984 1048 snprintf(filename, PATH_MAX, "%s/%s/devnum", SYSFS_DEVICE_PATH, devname); 985 1049 fd = fopen(filename, "r"); 986 1050 if (!fd) { 1051 if (errno == ENOENT) { 1052 /* devnum doesn't exist. Assume the device has been 1053 disconnected (unplugged). */ 1054 return LIBUSB_ERROR_NO_DEVICE; 1055 } 987 1056 usbi_err(ctx, "open devnum failed, errno=%d", errno); 988 1057 return LIBUSB_ERROR_IO; 989 1058 } … … static int sysfs_scan_device(struct libusb_context *ctx, 992 1061 fclose(fd); 993 1062 if (r != 1) { 994 1063 usbi_err(ctx, "fscanf devnum returned %d, errno=%d", r, errno); 995 return LIBUSB_ERROR_ IO;1064 return LIBUSB_ERROR_NO_DEVICE; 996 1065 } 997 1066 998 1067 usbi_dbg("bus=%d dev=%d", busnum, devaddr); … … static int sysfs_scan_device(struct libusb_context *ctx, 1004 1073 } 1005 1074 1006 1075 static int sysfs_get_device_list(struct libusb_context *ctx, 1007 struct discovered_devs **_discdevs , int *usbfs_fallback)1076 struct discovered_devs **_discdevs) 1008 1077 { 1009 1078 struct discovered_devs *discdevs = *_discdevs; 1010 1079 DIR *devices = opendir(SYSFS_DEVICE_PATH); … … static int sysfs_get_device_list(struct libusb_context *ctx, 1023 1092 || strchr(entry->d_name, ':')) 1024 1093 continue; 1025 1094 1026 r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name, 1027 usbfs_fallback); 1028 if (r < 0) 1095 r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name); 1096 if (r < 0 && r != LIBUSB_ERROR_NO_DEVICE) 1029 1097 goto out; 1030 1098 discdevs = discdevs_new; 1031 } 1032 1099 } 1100 r = 0; 1033 1101 out: 1034 1102 closedir(devices); 1035 1103 *_discdevs = discdevs; … … static int op_get_device_list(struct libusb_context *ctx, 1044 1112 * any autosuspended USB devices. however, sysfs is not available 1045 1113 * everywhere, so we need a usbfs fallback too. 1046 1114 * 1047 * as described in the "sysfs vs usbfs" comment , sometimes we have1048 * sysfs but not enough information to relate sysfs devices to usbfs1049 * nodes. the usbfs_fallback variable is used to indicate that we should1050 * fall back on usbfs.1115 * as described in the "sysfs vs usbfs" comment at the top of this 1116 * file, sometimes we have sysfs but not enough information to 1117 * relate sysfs devices to usbfs nodes. op_init() determines the 1118 * adequacy of sysfs and sets sysfs_can_relate_devices. 1051 1119 */ 1052 if (sysfs_can_relate_devices != 0) { 1053 int usbfs_fallback = 0; 1054 int r = sysfs_get_device_list(ctx, _discdevs, &usbfs_fallback); 1055 if (!usbfs_fallback) 1056 return r; 1057 } 1058 1059 return usbfs_get_device_list(ctx, _discdevs); 1120 if (sysfs_can_relate_devices != 0) 1121 return sysfs_get_device_list(ctx, _discdevs); 1122 else 1123 return usbfs_get_device_list(ctx, _discdevs); 1060 1124 } 1061 1125 1062 1126 static int op_open(struct libusb_device_handle *handle)