forked from mirrors/linux
		
	gpiolib: Fix line event handling in syscall compatible mode
The introduced line event handling ABI in the commit61f922db72("gpio: userspace ABI for reading GPIO line events") missed the fact that 64-bit kernel may serve for 32-bit applications. In such case the very first check in the lineevent_read() will fail due to alignment differences. To workaround this introduce lineevent_get_size() helper which returns actual size of the structure in user space. Fixes:61f922db72("gpio: userspace ABI for reading GPIO line events") Suggested-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Tested-by: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This commit is contained in:
		
							parent
							
								
									1b02d9e770
								
							
						
					
					
						commit
						5ad284ab3a
					
				
					 1 changed files with 30 additions and 4 deletions
				
			
		|  | @ -423,6 +423,21 @@ static __poll_t lineevent_poll(struct file *file, | ||||||
| 	return events; | 	return events; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static ssize_t lineevent_get_size(void) | ||||||
|  | { | ||||||
|  | #ifdef __x86_64__ | ||||||
|  | 	/* i386 has no padding after 'id' */ | ||||||
|  | 	if (in_ia32_syscall()) { | ||||||
|  | 		struct compat_gpioeevent_data { | ||||||
|  | 			compat_u64	timestamp; | ||||||
|  | 			u32		id; | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		return sizeof(struct compat_gpioeevent_data); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 	return sizeof(struct gpioevent_data); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static ssize_t lineevent_read(struct file *file, | static ssize_t lineevent_read(struct file *file, | ||||||
| 			      char __user *buf, | 			      char __user *buf, | ||||||
|  | @ -432,9 +447,20 @@ static ssize_t lineevent_read(struct file *file, | ||||||
| 	struct lineevent_state *le = file->private_data; | 	struct lineevent_state *le = file->private_data; | ||||||
| 	struct gpioevent_data ge; | 	struct gpioevent_data ge; | ||||||
| 	ssize_t bytes_read = 0; | 	ssize_t bytes_read = 0; | ||||||
|  | 	ssize_t ge_size; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (count < sizeof(ge)) | 	/*
 | ||||||
|  | 	 * When compatible system call is being used the struct gpioevent_data, | ||||||
|  | 	 * in case of at least ia32, has different size due to the alignment | ||||||
|  | 	 * differences. Because we have first member 64 bits followed by one of | ||||||
|  | 	 * 32 bits there is no gap between them. The only difference is the | ||||||
|  | 	 * padding at the end of the data structure. Hence, we calculate the | ||||||
|  | 	 * actual sizeof() and pass this as an argument to copy_to_user() to | ||||||
|  | 	 * drop unneeded bytes from the output. | ||||||
|  | 	 */ | ||||||
|  | 	ge_size = lineevent_get_size(); | ||||||
|  | 	if (count < ge_size) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	do { | 	do { | ||||||
|  | @ -470,10 +496,10 @@ static ssize_t lineevent_read(struct file *file, | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) | 		if (copy_to_user(buf + bytes_read, &ge, ge_size)) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 		bytes_read += sizeof(ge); | 		bytes_read += ge_size; | ||||||
| 	} while (count >= bytes_read + sizeof(ge)); | 	} while (count >= bytes_read + ge_size); | ||||||
| 
 | 
 | ||||||
| 	return bytes_read; | 	return bytes_read; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Andy Shevchenko
						Andy Shevchenko