Ticket #70: 0001-Fix-Race-Condition-in-sysfs_get_device_list.patch
| File 0001-Fix-Race-Condition-in-sysfs_get_device_list.patch, 8.3 KB (added by stuge, 2 years ago) |
|---|
-
libusb/os/linux_usbfs.c
From fe264aa6c76d0e1e7c09d81a3314690545845878 Mon Sep 17 00:00:00 2001 From: Alan Ott <alan@signal11.us> Date: Wed, 5 Jan 2011 00:37:28 -0500 Subject: [PATCH] Fix 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> --- libusb/os/linux_usbfs.c | 125 +++++++++++++++++++++++++++++++++++------------ 1 files changed, 94 insertions(+), 31 deletions(-) diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index a44688d..1e98137 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) 234 234 return sublevel >= 32; 235 235 } 236 236 237 /* Return 1 if filename exists inside dirname in sysfs. 238 SYSFS_DEVICE_PATH is assumed to be the beginning of the path. */ 239 static int sysfs_has_file(const char *dirname, const char *filename) 240 { 241 struct stat statbuf; 242 char path[PATH_MAX]; 243 int r; 244 245 snprintf(path, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, dirname, filename); 246 r = stat(path, &statbuf); 247 if (r == 0 && S_ISREG(statbuf.st_mode)) 248 return 1; 249 250 return 0; 251 } 252 237 253 static int op_init(struct libusb_context *ctx) 238 254 { 239 255 struct stat statbuf; … … static int op_init(struct libusb_context *ctx) 261 277 262 278 r = stat(SYSFS_DEVICE_PATH, &statbuf); 263 279 if (r == 0 && S_ISDIR(statbuf.st_mode)) { 280 DIR *devices = opendir(SYSFS_DEVICE_PATH); 281 struct dirent *entry; 282 264 283 usbi_dbg("found usb devices in sysfs"); 284 285 if (!devices) { 286 usbi_err(ctx, "opendir devices failed errno=%d", errno); 287 return LIBUSB_ERROR_IO; 288 } 289 290 /* Make sure sysfs supports all the required files. If it 291 * does not, then usbfs will be used instead. Determine 292 * this by looping through the directories in 293 * SYSFS_DEVICE_PATH. With the assumption that there will 294 * always be subdirectories of the name usbN (usb1, usb2, 295 * etc) representing the root hubs, check the usbN 296 * subdirectories to see if they have all the needed files. 297 * This algorithm uses the usbN subdirectories (root hubs) 298 * because a device disconnection will cause a race 299 * condition regarding which files are available, sometimes 300 * causing an incorrect result. The root hubs are used 301 * because it is assumed that they will always be present. 302 * See the "sysfs vs usbfs" comment at the top of this file 303 * for more details. */ 304 while ((entry = readdir(devices))) { 305 int has_busnum=0, has_devnum=0, has_descriptors=0; 306 int has_configuration_value=0; 307 308 /* Only check the usbN directories. */ 309 if (strncmp(entry->d_name, "usb", 3) != 0) 310 continue; 311 312 /* Check for the files libusb needs from sysfs. */ 313 has_busnum = sysfs_has_file(entry->d_name, "busnum"); 314 has_devnum = sysfs_has_file(entry->d_name, "devnum"); 315 has_descriptors = sysfs_has_file(entry->d_name, "descriptors"); 316 has_configuration_value = sysfs_has_file(entry->d_name, "bConfigurationValue"); 317 318 if (has_busnum && has_devnum && has_configuration_value) 319 sysfs_can_relate_devices = 1; 320 if (has_descriptors) 321 sysfs_has_descriptors = 1; 322 323 /* Only need to check until we've found ONE device which 324 has all the attributes. */ 325 if (sysfs_has_descriptors && sysfs_can_relate_devices) 326 break; 327 } 328 329 /* Only use sysfs descriptors if the rest of 330 sysfs will work for libusb. */ 331 if (!sysfs_can_relate_devices) 332 sysfs_has_descriptors = 0; 265 333 } else { 266 334 usbi_dbg("sysfs usb info not available"); 267 335 sysfs_has_descriptors = 0; … … out: 922 990 } 923 991 924 992 static int sysfs_scan_device(struct libusb_context *ctx, 925 struct discovered_devs **_discdevs, const char *devname, 926 int *usbfs_fallback) 993 struct discovered_devs **_discdevs, const char *devname) 927 994 { 928 995 int r; 929 996 FILE *fd; … … static int sysfs_scan_device(struct libusb_context *ctx, 954 1021 fd = fopen(filename, "r"); 955 1022 if (!fd) { 956 1023 if (errno == ENOENT) { 957 usbi_dbg("busnum not found, cannot relate sysfs to usbfs, " 958 "falling back on pure usbfs"); 959 sysfs_can_relate_devices = 0; 960 *usbfs_fallback = 1; 961 return LIBUSB_ERROR_OTHER; 1024 /* busnum doesn't exist. Assume the device has been 1025 disconnected (unplugged). */ 1026 return LIBUSB_ERROR_NO_DEVICE; 962 1027 } 963 1028 usbi_err(ctx, "open busnum failed, errno=%d", errno); 964 1029 return LIBUSB_ERROR_IO; 965 1030 } 966 1031 967 sysfs_can_relate_devices = 1;968 969 1032 r = fscanf(fd, "%d", &busnum); 970 1033 fclose(fd); 971 1034 if (r != 1) { 972 1035 usbi_err(ctx, "fscanf busnum returned %d, errno=%d", r, errno); 973 return LIBUSB_ERROR_ IO;1036 return LIBUSB_ERROR_NO_DEVICE; 974 1037 } 975 1038 976 1039 snprintf(filename, PATH_MAX, "%s/%s/devnum", SYSFS_DEVICE_PATH, devname); 977 1040 fd = fopen(filename, "r"); 978 1041 if (!fd) { 1042 if (errno == ENOENT) { 1043 /* devnum doesn't exist. Assume the device has been 1044 disconnected (unplugged). */ 1045 return LIBUSB_ERROR_NO_DEVICE; 1046 } 979 1047 usbi_err(ctx, "open devnum failed, errno=%d", errno); 980 1048 return LIBUSB_ERROR_IO; 981 1049 } … … static int sysfs_scan_device(struct libusb_context *ctx, 984 1052 fclose(fd); 985 1053 if (r != 1) { 986 1054 usbi_err(ctx, "fscanf devnum returned %d, errno=%d", r, errno); 987 return LIBUSB_ERROR_ IO;1055 return LIBUSB_ERROR_NO_DEVICE; 988 1056 } 989 1057 990 1058 usbi_dbg("bus=%d dev=%d", busnum, devaddr); … … static int sysfs_scan_device(struct libusb_context *ctx, 996 1064 } 997 1065 998 1066 static int sysfs_get_device_list(struct libusb_context *ctx, 999 struct discovered_devs **_discdevs , int *usbfs_fallback)1067 struct discovered_devs **_discdevs) 1000 1068 { 1001 1069 struct discovered_devs *discdevs = *_discdevs; 1002 1070 DIR *devices = opendir(SYSFS_DEVICE_PATH); … … static int sysfs_get_device_list(struct libusb_context *ctx, 1015 1083 || strchr(entry->d_name, ':')) 1016 1084 continue; 1017 1085 1018 r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name, 1019 usbfs_fallback); 1020 if (r < 0) 1086 r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name); 1087 if (r < 0 && r != LIBUSB_ERROR_NO_DEVICE) 1021 1088 goto out; 1022 1089 discdevs = discdevs_new; 1023 } 1024 1090 } 1091 r = 0; 1025 1092 out: 1026 1093 closedir(devices); 1027 1094 *_discdevs = discdevs; … … static int op_get_device_list(struct libusb_context *ctx, 1036 1103 * any autosuspended USB devices. however, sysfs is not available 1037 1104 * everywhere, so we need a usbfs fallback too. 1038 1105 * 1039 * as described in the "sysfs vs usbfs" comment , sometimes we have1040 * sysfs but not enough information to relate sysfs devices to usbfs1041 * nodes. the usbfs_fallback variable is used to indicate that we should1042 * fall back on usbfs.1106 * as described in the "sysfs vs usbfs" comment at the top of this 1107 * file, sometimes we have sysfs but not enough information to 1108 * relate sysfs devices to usbfs nodes. op_init() determines the 1109 * adequacy of sysfs and sets sysfs_can_relate_devices. 1043 1110 */ 1044 if (sysfs_can_relate_devices != 0) { 1045 int usbfs_fallback = 0; 1046 int r = sysfs_get_device_list(ctx, _discdevs, &usbfs_fallback); 1047 if (!usbfs_fallback) 1048 return r; 1049 } 1050 1051 return usbfs_get_device_list(ctx, _discdevs); 1111 if (sysfs_can_relate_devices != 0) 1112 return sysfs_get_device_list(ctx, _discdevs); 1113 else 1114 return usbfs_get_device_list(ctx, _discdevs); 1052 1115 } 1053 1116 1054 1117 static int op_open(struct libusb_device_handle *handle)