Commit c4a047272566b44b44222369d50a307c708c4f74
Committed by
Arnd Bergmann
1 parent
9a181c5861
Exists in
master
and in
4 other branches
fix rawctl compat ioctls breakage on amd64 and itanic
RAW_SETBIND and RAW_GETBIND 32bit versions are fscked in interesting ways. 1) fs/compat_ioctl.c has COMPATIBLE_IOCTL(RAW_SETBIND) followed by HANDLE_IOCTL(RAW_SETBIND, raw_ioctl). The latter is ignored. 2) on amd64 (and itanic) the damn thing is broken - we have int + u64 + u64 and layouts on i386 and amd64 are _not_ the same. raw_ioctl() would work there, but it's never called due to (1). As it is, i386 /sbin/raw definitely doesn't work on amd64 boxen. 3) switching to raw_ioctl() as is would *not* work on e.g. sparc64 and ppc64, which would be rather sad, seeing that normal userland there is 32bit. The thing is, slapping __packed on the struct in question does not DTRT - it eliminates *all* padding. The real solution is to use compat_u64. 4) of course, all that stuff has no business being outside of raw.c in the first place - there should be ->compat_ioctl() for /dev/rawctl instead of messing with compat_ioctl.c. [akpm@linux-foundation.org: coding-style fixes] [arnd@arndb.de: port to 2.6.36] Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Showing 2 changed files with 140 additions and 173 deletions Side-by-side Diff
drivers/char/raw.c
... | ... | @@ -19,8 +19,8 @@ |
19 | 19 | #include <linux/cdev.h> |
20 | 20 | #include <linux/device.h> |
21 | 21 | #include <linux/mutex.h> |
22 | -#include <linux/smp_lock.h> | |
23 | 22 | #include <linux/gfp.h> |
23 | +#include <linux/compat.h> | |
24 | 24 | |
25 | 25 | #include <asm/uaccess.h> |
26 | 26 | |
... | ... | @@ -55,7 +55,6 @@ |
55 | 55 | return 0; |
56 | 56 | } |
57 | 57 | |
58 | - lock_kernel(); | |
59 | 58 | mutex_lock(&raw_mutex); |
60 | 59 | |
61 | 60 | /* |
... | ... | @@ -82,7 +81,6 @@ |
82 | 81 | bdev->bd_inode->i_mapping; |
83 | 82 | filp->private_data = bdev; |
84 | 83 | mutex_unlock(&raw_mutex); |
85 | - unlock_kernel(); | |
86 | 84 | return 0; |
87 | 85 | |
88 | 86 | out2: |
... | ... | @@ -91,7 +89,6 @@ |
91 | 89 | blkdev_put(bdev, filp->f_mode); |
92 | 90 | out: |
93 | 91 | mutex_unlock(&raw_mutex); |
94 | - unlock_kernel(); | |
95 | 92 | return err; |
96 | 93 | } |
97 | 94 | |
98 | 95 | |
99 | 96 | |
100 | 97 | |
101 | 98 | |
... | ... | @@ -125,20 +122,84 @@ |
125 | 122 | raw_ioctl(struct file *filp, unsigned int command, unsigned long arg) |
126 | 123 | { |
127 | 124 | struct block_device *bdev = filp->private_data; |
128 | - int ret; | |
125 | + return blkdev_ioctl(bdev, 0, command, arg); | |
126 | +} | |
129 | 127 | |
130 | - lock_kernel(); | |
131 | - ret = blkdev_ioctl(bdev, 0, command, arg); | |
132 | - unlock_kernel(); | |
128 | +static int bind_set(int number, u64 major, u64 minor) | |
129 | +{ | |
130 | + dev_t dev = MKDEV(major, minor); | |
131 | + struct raw_device_data *rawdev; | |
132 | + int err = 0; | |
133 | 133 | |
134 | - return ret; | |
134 | + if (number <= 0 || number >= MAX_RAW_MINORS) | |
135 | + return -EINVAL; | |
136 | + | |
137 | + if (MAJOR(dev) != major || MINOR(dev) != minor) | |
138 | + return -EINVAL; | |
139 | + | |
140 | + rawdev = &raw_devices[number]; | |
141 | + | |
142 | + /* | |
143 | + * This is like making block devices, so demand the | |
144 | + * same capability | |
145 | + */ | |
146 | + if (!capable(CAP_SYS_ADMIN)) | |
147 | + return -EPERM; | |
148 | + | |
149 | + /* | |
150 | + * For now, we don't need to check that the underlying | |
151 | + * block device is present or not: we can do that when | |
152 | + * the raw device is opened. Just check that the | |
153 | + * major/minor numbers make sense. | |
154 | + */ | |
155 | + | |
156 | + if (MAJOR(dev) == 0 && dev != 0) | |
157 | + return -EINVAL; | |
158 | + | |
159 | + mutex_lock(&raw_mutex); | |
160 | + if (rawdev->inuse) { | |
161 | + mutex_unlock(&raw_mutex); | |
162 | + return -EBUSY; | |
163 | + } | |
164 | + if (rawdev->binding) { | |
165 | + bdput(rawdev->binding); | |
166 | + module_put(THIS_MODULE); | |
167 | + } | |
168 | + if (!dev) { | |
169 | + /* unbind */ | |
170 | + rawdev->binding = NULL; | |
171 | + device_destroy(raw_class, MKDEV(RAW_MAJOR, number)); | |
172 | + } else { | |
173 | + rawdev->binding = bdget(dev); | |
174 | + if (rawdev->binding == NULL) { | |
175 | + err = -ENOMEM; | |
176 | + } else { | |
177 | + dev_t raw = MKDEV(RAW_MAJOR, number); | |
178 | + __module_get(THIS_MODULE); | |
179 | + device_destroy(raw_class, raw); | |
180 | + device_create(raw_class, NULL, raw, NULL, | |
181 | + "raw%d", number); | |
182 | + } | |
183 | + } | |
184 | + mutex_unlock(&raw_mutex); | |
185 | + return err; | |
135 | 186 | } |
136 | 187 | |
137 | -static void bind_device(struct raw_config_request *rq) | |
188 | +static int bind_get(int number, dev_t *dev) | |
138 | 189 | { |
139 | - device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor)); | |
140 | - device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor), NULL, | |
141 | - "raw%d", rq->raw_minor); | |
190 | + struct raw_device_data *rawdev; | |
191 | + struct block_device *bdev; | |
192 | + | |
193 | + if (number <= 0 || number >= MAX_RAW_MINORS) | |
194 | + return -EINVAL; | |
195 | + | |
196 | + rawdev = &raw_devices[number]; | |
197 | + | |
198 | + mutex_lock(&raw_mutex); | |
199 | + bdev = rawdev->binding; | |
200 | + *dev = bdev ? bdev->bd_dev : 0; | |
201 | + mutex_unlock(&raw_mutex); | |
202 | + return 0; | |
142 | 203 | } |
143 | 204 | |
144 | 205 | /* |
145 | 206 | |
146 | 207 | |
147 | 208 | |
148 | 209 | |
149 | 210 | |
150 | 211 | |
151 | 212 | |
152 | 213 | |
153 | 214 | |
154 | 215 | |
155 | 216 | |
156 | 217 | |
157 | 218 | |
158 | 219 | |
... | ... | @@ -149,105 +210,78 @@ |
149 | 210 | unsigned long arg) |
150 | 211 | { |
151 | 212 | struct raw_config_request rq; |
152 | - struct raw_device_data *rawdev; | |
153 | - int err = 0; | |
213 | + dev_t dev; | |
214 | + int err; | |
154 | 215 | |
155 | - lock_kernel(); | |
156 | 216 | switch (command) { |
157 | 217 | case RAW_SETBIND: |
218 | + if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) | |
219 | + return -EFAULT; | |
220 | + | |
221 | + return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); | |
222 | + | |
158 | 223 | case RAW_GETBIND: |
224 | + if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) | |
225 | + return -EFAULT; | |
159 | 226 | |
160 | - /* First, find out which raw minor we want */ | |
227 | + err = bind_get(rq.raw_minor, &dev); | |
228 | + if (err) | |
229 | + return err; | |
161 | 230 | |
162 | - if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) { | |
163 | - err = -EFAULT; | |
164 | - goto out; | |
165 | - } | |
231 | + rq.block_major = MAJOR(dev); | |
232 | + rq.block_minor = MINOR(dev); | |
166 | 233 | |
167 | - if (rq.raw_minor <= 0 || rq.raw_minor >= MAX_RAW_MINORS) { | |
168 | - err = -EINVAL; | |
169 | - goto out; | |
170 | - } | |
171 | - rawdev = &raw_devices[rq.raw_minor]; | |
234 | + if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) | |
235 | + return -EFAULT; | |
172 | 236 | |
173 | - if (command == RAW_SETBIND) { | |
174 | - dev_t dev; | |
237 | + return 0; | |
238 | + } | |
175 | 239 | |
176 | - /* | |
177 | - * This is like making block devices, so demand the | |
178 | - * same capability | |
179 | - */ | |
180 | - if (!capable(CAP_SYS_ADMIN)) { | |
181 | - err = -EPERM; | |
182 | - goto out; | |
183 | - } | |
240 | + return -EINVAL; | |
241 | +} | |
184 | 242 | |
185 | - /* | |
186 | - * For now, we don't need to check that the underlying | |
187 | - * block device is present or not: we can do that when | |
188 | - * the raw device is opened. Just check that the | |
189 | - * major/minor numbers make sense. | |
190 | - */ | |
243 | +#ifdef CONFIG_COMPAT | |
244 | +struct raw32_config_request { | |
245 | + compat_int_t raw_minor; | |
246 | + compat_u64 block_major; | |
247 | + compat_u64 block_minor; | |
248 | +}; | |
191 | 249 | |
192 | - dev = MKDEV(rq.block_major, rq.block_minor); | |
193 | - if ((rq.block_major == 0 && rq.block_minor != 0) || | |
194 | - MAJOR(dev) != rq.block_major || | |
195 | - MINOR(dev) != rq.block_minor) { | |
196 | - err = -EINVAL; | |
197 | - goto out; | |
198 | - } | |
250 | +static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, | |
251 | + unsigned long arg) | |
252 | +{ | |
253 | + struct raw32_config_request __user *user_req = compat_ptr(arg); | |
254 | + struct raw32_config_request rq; | |
255 | + dev_t dev; | |
256 | + int err = 0; | |
199 | 257 | |
200 | - mutex_lock(&raw_mutex); | |
201 | - if (rawdev->inuse) { | |
202 | - mutex_unlock(&raw_mutex); | |
203 | - err = -EBUSY; | |
204 | - goto out; | |
205 | - } | |
206 | - if (rawdev->binding) { | |
207 | - bdput(rawdev->binding); | |
208 | - module_put(THIS_MODULE); | |
209 | - } | |
210 | - if (rq.block_major == 0 && rq.block_minor == 0) { | |
211 | - /* unbind */ | |
212 | - rawdev->binding = NULL; | |
213 | - device_destroy(raw_class, | |
214 | - MKDEV(RAW_MAJOR, rq.raw_minor)); | |
215 | - } else { | |
216 | - rawdev->binding = bdget(dev); | |
217 | - if (rawdev->binding == NULL) | |
218 | - err = -ENOMEM; | |
219 | - else { | |
220 | - __module_get(THIS_MODULE); | |
221 | - bind_device(&rq); | |
222 | - } | |
223 | - } | |
224 | - mutex_unlock(&raw_mutex); | |
225 | - } else { | |
226 | - struct block_device *bdev; | |
258 | + switch (cmd) { | |
259 | + case RAW_SETBIND: | |
260 | + if (copy_from_user(&rq, user_req, sizeof(rq))) | |
261 | + return -EFAULT; | |
227 | 262 | |
228 | - mutex_lock(&raw_mutex); | |
229 | - bdev = rawdev->binding; | |
230 | - if (bdev) { | |
231 | - rq.block_major = MAJOR(bdev->bd_dev); | |
232 | - rq.block_minor = MINOR(bdev->bd_dev); | |
233 | - } else { | |
234 | - rq.block_major = rq.block_minor = 0; | |
235 | - } | |
236 | - mutex_unlock(&raw_mutex); | |
237 | - if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) { | |
238 | - err = -EFAULT; | |
239 | - goto out; | |
240 | - } | |
241 | - } | |
242 | - break; | |
243 | - default: | |
244 | - err = -EINVAL; | |
245 | - break; | |
263 | + return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); | |
264 | + | |
265 | + case RAW_GETBIND: | |
266 | + if (copy_from_user(&rq, user_req, sizeof(rq))) | |
267 | + return -EFAULT; | |
268 | + | |
269 | + err = bind_get(rq.raw_minor, &dev); | |
270 | + if (err) | |
271 | + return err; | |
272 | + | |
273 | + rq.block_major = MAJOR(dev); | |
274 | + rq.block_minor = MINOR(dev); | |
275 | + | |
276 | + if (copy_to_user(user_req, &rq, sizeof(rq))) | |
277 | + return -EFAULT; | |
278 | + | |
279 | + return 0; | |
246 | 280 | } |
247 | -out: | |
248 | - unlock_kernel(); | |
249 | - return err; | |
281 | + | |
282 | + return -EINVAL; | |
250 | 283 | } |
284 | +#endif | |
251 | 285 | |
252 | 286 | static const struct file_operations raw_fops = { |
253 | 287 | .read = do_sync_read, |
... | ... | @@ -263,6 +297,9 @@ |
263 | 297 | |
264 | 298 | static const struct file_operations raw_ctl_fops = { |
265 | 299 | .unlocked_ioctl = raw_ctl_ioctl, |
300 | +#ifdef CONFIG_COMPAT | |
301 | + .compat_ioctl = raw_ctl_compat_ioctl, | |
302 | +#endif | |
266 | 303 | .open = raw_open, |
267 | 304 | .owner = THIS_MODULE, |
268 | 305 | }; |
fs/compat_ioctl.c
... | ... | @@ -599,70 +599,7 @@ |
599 | 599 | #define HIDPGETCONNLIST _IOR('H', 210, int) |
600 | 600 | #define HIDPGETCONNINFO _IOR('H', 211, int) |
601 | 601 | |
602 | -#ifdef CONFIG_BLOCK | |
603 | -struct raw32_config_request | |
604 | -{ | |
605 | - compat_int_t raw_minor; | |
606 | - __u64 block_major; | |
607 | - __u64 block_minor; | |
608 | -} __attribute__((packed)); | |
609 | 602 | |
610 | -static int get_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) | |
611 | -{ | |
612 | - int ret; | |
613 | - | |
614 | - if (!access_ok(VERIFY_READ, user_req, sizeof(struct raw32_config_request))) | |
615 | - return -EFAULT; | |
616 | - | |
617 | - ret = __get_user(req->raw_minor, &user_req->raw_minor); | |
618 | - ret |= __get_user(req->block_major, &user_req->block_major); | |
619 | - ret |= __get_user(req->block_minor, &user_req->block_minor); | |
620 | - | |
621 | - return ret ? -EFAULT : 0; | |
622 | -} | |
623 | - | |
624 | -static int set_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) | |
625 | -{ | |
626 | - int ret; | |
627 | - | |
628 | - if (!access_ok(VERIFY_WRITE, user_req, sizeof(struct raw32_config_request))) | |
629 | - return -EFAULT; | |
630 | - | |
631 | - ret = __put_user(req->raw_minor, &user_req->raw_minor); | |
632 | - ret |= __put_user(req->block_major, &user_req->block_major); | |
633 | - ret |= __put_user(req->block_minor, &user_req->block_minor); | |
634 | - | |
635 | - return ret ? -EFAULT : 0; | |
636 | -} | |
637 | - | |
638 | -static int raw_ioctl(unsigned fd, unsigned cmd, | |
639 | - struct raw32_config_request __user *user_req) | |
640 | -{ | |
641 | - int ret; | |
642 | - | |
643 | - switch (cmd) { | |
644 | - case RAW_SETBIND: | |
645 | - default: { /* RAW_GETBIND */ | |
646 | - struct raw_config_request req; | |
647 | - mm_segment_t oldfs = get_fs(); | |
648 | - | |
649 | - if ((ret = get_raw32_request(&req, user_req))) | |
650 | - return ret; | |
651 | - | |
652 | - set_fs(KERNEL_DS); | |
653 | - ret = sys_ioctl(fd,cmd,(unsigned long)&req); | |
654 | - set_fs(oldfs); | |
655 | - | |
656 | - if ((!ret) && (cmd == RAW_GETBIND)) { | |
657 | - ret = set_raw32_request(&req, user_req); | |
658 | - } | |
659 | - break; | |
660 | - } | |
661 | - } | |
662 | - return ret; | |
663 | -} | |
664 | -#endif /* CONFIG_BLOCK */ | |
665 | - | |
666 | 603 | struct serial_struct32 { |
667 | 604 | compat_int_t type; |
668 | 605 | compat_int_t line; |
... | ... | @@ -1262,9 +1199,6 @@ |
1262 | 1199 | COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) |
1263 | 1200 | COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) |
1264 | 1201 | COMPATIBLE_IOCTL(OSS_GETVERSION) |
1265 | -/* Raw devices */ | |
1266 | -COMPATIBLE_IOCTL(RAW_SETBIND) | |
1267 | -COMPATIBLE_IOCTL(RAW_GETBIND) | |
1268 | 1202 | /* SMB ioctls which do not need any translations */ |
1269 | 1203 | COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) |
1270 | 1204 | /* Watchdog */ |
... | ... | @@ -1523,10 +1457,6 @@ |
1523 | 1457 | case MTIOCGET32: |
1524 | 1458 | case MTIOCPOS32: |
1525 | 1459 | return mt_ioctl_trans(fd, cmd, argp); |
1526 | - /* Raw devices */ | |
1527 | - case RAW_SETBIND: | |
1528 | - case RAW_GETBIND: | |
1529 | - return raw_ioctl(fd, cmd, argp); | |
1530 | 1460 | #endif |
1531 | 1461 | /* One SMB ioctl needs translations. */ |
1532 | 1462 | #define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t) |