| .. | .. |
|---|
| 20 | 20 | #include <linux/syscalls.h> |
|---|
| 21 | 21 | #include <linux/unistd.h> |
|---|
| 22 | 22 | #include <linux/compat.h> |
|---|
| 23 | | - |
|---|
| 24 | 23 | #include <linux/uaccess.h> |
|---|
| 24 | + |
|---|
| 25 | +#include <asm/unaligned.h> |
|---|
| 26 | + |
|---|
| 27 | +/* |
|---|
| 28 | + * Note the "unsafe_put_user() semantics: we goto a |
|---|
| 29 | + * label for errors. |
|---|
| 30 | + */ |
|---|
| 31 | +#define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \ |
|---|
| 32 | + char __user *dst = (_dst); \ |
|---|
| 33 | + const char *src = (_src); \ |
|---|
| 34 | + size_t len = (_len); \ |
|---|
| 35 | + unsafe_put_user(0, dst+len, label); \ |
|---|
| 36 | + unsafe_copy_to_user(dst, src, len, label); \ |
|---|
| 37 | +} while (0) |
|---|
| 38 | + |
|---|
| 25 | 39 | |
|---|
| 26 | 40 | int iterate_dir(struct file *file, struct dir_context *ctx) |
|---|
| 27 | 41 | { |
|---|
| .. | .. |
|---|
| 88 | 102 | * filename length, and the above "soft error" worry means |
|---|
| 89 | 103 | * that it's probably better left alone until we have that |
|---|
| 90 | 104 | * issue clarified. |
|---|
| 105 | + * |
|---|
| 106 | + * Note the PATH_MAX check - it's arbitrary but the real |
|---|
| 107 | + * kernel limit on a possible path component, not NAME_MAX, |
|---|
| 108 | + * which is the technical standard limit. |
|---|
| 91 | 109 | */ |
|---|
| 92 | 110 | static int verify_dirent_name(const char *name, int len) |
|---|
| 93 | 111 | { |
|---|
| 94 | | - if (!len) |
|---|
| 112 | + if (len <= 0 || len >= PATH_MAX) |
|---|
| 95 | 113 | return -EIO; |
|---|
| 96 | 114 | if (memchr(name, '/', len)) |
|---|
| 97 | 115 | return -EIO; |
|---|
| .. | .. |
|---|
| 142 | 160 | } |
|---|
| 143 | 161 | buf->result++; |
|---|
| 144 | 162 | dirent = buf->dirent; |
|---|
| 145 | | - if (!access_ok(VERIFY_WRITE, dirent, |
|---|
| 163 | + if (!user_write_access_begin(dirent, |
|---|
| 146 | 164 | (unsigned long)(dirent->d_name + namlen + 1) - |
|---|
| 147 | 165 | (unsigned long)dirent)) |
|---|
| 148 | 166 | goto efault; |
|---|
| 149 | | - if ( __put_user(d_ino, &dirent->d_ino) || |
|---|
| 150 | | - __put_user(offset, &dirent->d_offset) || |
|---|
| 151 | | - __put_user(namlen, &dirent->d_namlen) || |
|---|
| 152 | | - __copy_to_user(dirent->d_name, name, namlen) || |
|---|
| 153 | | - __put_user(0, dirent->d_name + namlen)) |
|---|
| 154 | | - goto efault; |
|---|
| 167 | + unsafe_put_user(d_ino, &dirent->d_ino, efault_end); |
|---|
| 168 | + unsafe_put_user(offset, &dirent->d_offset, efault_end); |
|---|
| 169 | + unsafe_put_user(namlen, &dirent->d_namlen, efault_end); |
|---|
| 170 | + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
|---|
| 171 | + user_write_access_end(); |
|---|
| 155 | 172 | return 0; |
|---|
| 173 | +efault_end: |
|---|
| 174 | + user_write_access_end(); |
|---|
| 156 | 175 | efault: |
|---|
| 157 | 176 | buf->result = -EFAULT; |
|---|
| 158 | 177 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 195 | 214 | struct getdents_callback { |
|---|
| 196 | 215 | struct dir_context ctx; |
|---|
| 197 | 216 | struct linux_dirent __user * current_dir; |
|---|
| 198 | | - struct linux_dirent __user * previous; |
|---|
| 217 | + int prev_reclen; |
|---|
| 199 | 218 | int count; |
|---|
| 200 | 219 | int error; |
|---|
| 201 | 220 | }; |
|---|
| .. | .. |
|---|
| 203 | 222 | static int filldir(struct dir_context *ctx, const char *name, int namlen, |
|---|
| 204 | 223 | loff_t offset, u64 ino, unsigned int d_type) |
|---|
| 205 | 224 | { |
|---|
| 206 | | - struct linux_dirent __user * dirent; |
|---|
| 225 | + struct linux_dirent __user *dirent, *prev; |
|---|
| 207 | 226 | struct getdents_callback *buf = |
|---|
| 208 | 227 | container_of(ctx, struct getdents_callback, ctx); |
|---|
| 209 | 228 | unsigned long d_ino; |
|---|
| 210 | 229 | int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, |
|---|
| 211 | 230 | sizeof(long)); |
|---|
| 231 | + int prev_reclen; |
|---|
| 212 | 232 | |
|---|
| 213 | 233 | buf->error = verify_dirent_name(name, namlen); |
|---|
| 214 | 234 | if (unlikely(buf->error)) |
|---|
| .. | .. |
|---|
| 221 | 241 | buf->error = -EOVERFLOW; |
|---|
| 222 | 242 | return -EOVERFLOW; |
|---|
| 223 | 243 | } |
|---|
| 224 | | - dirent = buf->previous; |
|---|
| 225 | | - if (dirent) { |
|---|
| 226 | | - if (signal_pending(current)) |
|---|
| 227 | | - return -EINTR; |
|---|
| 228 | | - if (__put_user(offset, &dirent->d_off)) |
|---|
| 229 | | - goto efault; |
|---|
| 230 | | - } |
|---|
| 244 | + prev_reclen = buf->prev_reclen; |
|---|
| 245 | + if (prev_reclen && signal_pending(current)) |
|---|
| 246 | + return -EINTR; |
|---|
| 231 | 247 | dirent = buf->current_dir; |
|---|
| 232 | | - if (__put_user(d_ino, &dirent->d_ino)) |
|---|
| 248 | + prev = (void __user *) dirent - prev_reclen; |
|---|
| 249 | + if (!user_write_access_begin(prev, reclen + prev_reclen)) |
|---|
| 233 | 250 | goto efault; |
|---|
| 234 | | - if (__put_user(reclen, &dirent->d_reclen)) |
|---|
| 235 | | - goto efault; |
|---|
| 236 | | - if (copy_to_user(dirent->d_name, name, namlen)) |
|---|
| 237 | | - goto efault; |
|---|
| 238 | | - if (__put_user(0, dirent->d_name + namlen)) |
|---|
| 239 | | - goto efault; |
|---|
| 240 | | - if (__put_user(d_type, (char __user *) dirent + reclen - 1)) |
|---|
| 241 | | - goto efault; |
|---|
| 242 | | - buf->previous = dirent; |
|---|
| 243 | | - dirent = (void __user *)dirent + reclen; |
|---|
| 244 | | - buf->current_dir = dirent; |
|---|
| 251 | + |
|---|
| 252 | + /* This might be 'dirent->d_off', but if so it will get overwritten */ |
|---|
| 253 | + unsafe_put_user(offset, &prev->d_off, efault_end); |
|---|
| 254 | + unsafe_put_user(d_ino, &dirent->d_ino, efault_end); |
|---|
| 255 | + unsafe_put_user(reclen, &dirent->d_reclen, efault_end); |
|---|
| 256 | + unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); |
|---|
| 257 | + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
|---|
| 258 | + user_write_access_end(); |
|---|
| 259 | + |
|---|
| 260 | + buf->current_dir = (void __user *)dirent + reclen; |
|---|
| 261 | + buf->prev_reclen = reclen; |
|---|
| 245 | 262 | buf->count -= reclen; |
|---|
| 246 | 263 | return 0; |
|---|
| 264 | +efault_end: |
|---|
| 265 | + user_write_access_end(); |
|---|
| 247 | 266 | efault: |
|---|
| 248 | 267 | buf->error = -EFAULT; |
|---|
| 249 | 268 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 253 | 272 | struct linux_dirent __user *, dirent, unsigned int, count) |
|---|
| 254 | 273 | { |
|---|
| 255 | 274 | struct fd f; |
|---|
| 256 | | - struct linux_dirent __user * lastdirent; |
|---|
| 257 | 275 | struct getdents_callback buf = { |
|---|
| 258 | 276 | .ctx.actor = filldir, |
|---|
| 259 | 277 | .count = count, |
|---|
| 260 | 278 | .current_dir = dirent |
|---|
| 261 | 279 | }; |
|---|
| 262 | 280 | int error; |
|---|
| 263 | | - |
|---|
| 264 | | - if (!access_ok(VERIFY_WRITE, dirent, count)) |
|---|
| 265 | | - return -EFAULT; |
|---|
| 266 | 281 | |
|---|
| 267 | 282 | f = fdget_pos(fd); |
|---|
| 268 | 283 | if (!f.file) |
|---|
| .. | .. |
|---|
| 271 | 286 | error = iterate_dir(f.file, &buf.ctx); |
|---|
| 272 | 287 | if (error >= 0) |
|---|
| 273 | 288 | error = buf.error; |
|---|
| 274 | | - lastdirent = buf.previous; |
|---|
| 275 | | - if (lastdirent) { |
|---|
| 289 | + if (buf.prev_reclen) { |
|---|
| 290 | + struct linux_dirent __user * lastdirent; |
|---|
| 291 | + lastdirent = (void __user *)buf.current_dir - buf.prev_reclen; |
|---|
| 292 | + |
|---|
| 276 | 293 | if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
|---|
| 277 | 294 | error = -EFAULT; |
|---|
| 278 | 295 | else |
|---|
| .. | .. |
|---|
| 285 | 302 | struct getdents_callback64 { |
|---|
| 286 | 303 | struct dir_context ctx; |
|---|
| 287 | 304 | struct linux_dirent64 __user * current_dir; |
|---|
| 288 | | - struct linux_dirent64 __user * previous; |
|---|
| 305 | + int prev_reclen; |
|---|
| 289 | 306 | int count; |
|---|
| 290 | 307 | int error; |
|---|
| 291 | 308 | }; |
|---|
| .. | .. |
|---|
| 293 | 310 | static int filldir64(struct dir_context *ctx, const char *name, int namlen, |
|---|
| 294 | 311 | loff_t offset, u64 ino, unsigned int d_type) |
|---|
| 295 | 312 | { |
|---|
| 296 | | - struct linux_dirent64 __user *dirent; |
|---|
| 313 | + struct linux_dirent64 __user *dirent, *prev; |
|---|
| 297 | 314 | struct getdents_callback64 *buf = |
|---|
| 298 | 315 | container_of(ctx, struct getdents_callback64, ctx); |
|---|
| 299 | 316 | int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, |
|---|
| 300 | 317 | sizeof(u64)); |
|---|
| 318 | + int prev_reclen; |
|---|
| 301 | 319 | |
|---|
| 302 | 320 | buf->error = verify_dirent_name(name, namlen); |
|---|
| 303 | 321 | if (unlikely(buf->error)) |
|---|
| .. | .. |
|---|
| 305 | 323 | buf->error = -EINVAL; /* only used if we fail.. */ |
|---|
| 306 | 324 | if (reclen > buf->count) |
|---|
| 307 | 325 | return -EINVAL; |
|---|
| 308 | | - dirent = buf->previous; |
|---|
| 309 | | - if (dirent) { |
|---|
| 310 | | - if (signal_pending(current)) |
|---|
| 311 | | - return -EINTR; |
|---|
| 312 | | - if (__put_user(offset, &dirent->d_off)) |
|---|
| 313 | | - goto efault; |
|---|
| 314 | | - } |
|---|
| 326 | + prev_reclen = buf->prev_reclen; |
|---|
| 327 | + if (prev_reclen && signal_pending(current)) |
|---|
| 328 | + return -EINTR; |
|---|
| 315 | 329 | dirent = buf->current_dir; |
|---|
| 316 | | - if (__put_user(ino, &dirent->d_ino)) |
|---|
| 330 | + prev = (void __user *)dirent - prev_reclen; |
|---|
| 331 | + if (!user_write_access_begin(prev, reclen + prev_reclen)) |
|---|
| 317 | 332 | goto efault; |
|---|
| 318 | | - if (__put_user(0, &dirent->d_off)) |
|---|
| 319 | | - goto efault; |
|---|
| 320 | | - if (__put_user(reclen, &dirent->d_reclen)) |
|---|
| 321 | | - goto efault; |
|---|
| 322 | | - if (__put_user(d_type, &dirent->d_type)) |
|---|
| 323 | | - goto efault; |
|---|
| 324 | | - if (copy_to_user(dirent->d_name, name, namlen)) |
|---|
| 325 | | - goto efault; |
|---|
| 326 | | - if (__put_user(0, dirent->d_name + namlen)) |
|---|
| 327 | | - goto efault; |
|---|
| 328 | | - buf->previous = dirent; |
|---|
| 329 | | - dirent = (void __user *)dirent + reclen; |
|---|
| 330 | | - buf->current_dir = dirent; |
|---|
| 333 | + |
|---|
| 334 | + /* This might be 'dirent->d_off', but if so it will get overwritten */ |
|---|
| 335 | + unsafe_put_user(offset, &prev->d_off, efault_end); |
|---|
| 336 | + unsafe_put_user(ino, &dirent->d_ino, efault_end); |
|---|
| 337 | + unsafe_put_user(reclen, &dirent->d_reclen, efault_end); |
|---|
| 338 | + unsafe_put_user(d_type, &dirent->d_type, efault_end); |
|---|
| 339 | + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
|---|
| 340 | + user_write_access_end(); |
|---|
| 341 | + |
|---|
| 342 | + buf->prev_reclen = reclen; |
|---|
| 343 | + buf->current_dir = (void __user *)dirent + reclen; |
|---|
| 331 | 344 | buf->count -= reclen; |
|---|
| 332 | 345 | return 0; |
|---|
| 346 | + |
|---|
| 347 | +efault_end: |
|---|
| 348 | + user_write_access_end(); |
|---|
| 333 | 349 | efault: |
|---|
| 334 | 350 | buf->error = -EFAULT; |
|---|
| 335 | 351 | return -EFAULT; |
|---|
| 336 | 352 | } |
|---|
| 337 | 353 | |
|---|
| 338 | | -int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, |
|---|
| 339 | | - unsigned int count) |
|---|
| 354 | +SYSCALL_DEFINE3(getdents64, unsigned int, fd, |
|---|
| 355 | + struct linux_dirent64 __user *, dirent, unsigned int, count) |
|---|
| 340 | 356 | { |
|---|
| 341 | 357 | struct fd f; |
|---|
| 342 | | - struct linux_dirent64 __user * lastdirent; |
|---|
| 343 | 358 | struct getdents_callback64 buf = { |
|---|
| 344 | 359 | .ctx.actor = filldir64, |
|---|
| 345 | 360 | .count = count, |
|---|
| 346 | 361 | .current_dir = dirent |
|---|
| 347 | 362 | }; |
|---|
| 348 | 363 | int error; |
|---|
| 349 | | - |
|---|
| 350 | | - if (!access_ok(VERIFY_WRITE, dirent, count)) |
|---|
| 351 | | - return -EFAULT; |
|---|
| 352 | 364 | |
|---|
| 353 | 365 | f = fdget_pos(fd); |
|---|
| 354 | 366 | if (!f.file) |
|---|
| .. | .. |
|---|
| 357 | 369 | error = iterate_dir(f.file, &buf.ctx); |
|---|
| 358 | 370 | if (error >= 0) |
|---|
| 359 | 371 | error = buf.error; |
|---|
| 360 | | - lastdirent = buf.previous; |
|---|
| 361 | | - if (lastdirent) { |
|---|
| 372 | + if (buf.prev_reclen) { |
|---|
| 373 | + struct linux_dirent64 __user * lastdirent; |
|---|
| 362 | 374 | typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
|---|
| 363 | | - if (__put_user(d_off, &lastdirent->d_off)) |
|---|
| 375 | + |
|---|
| 376 | + lastdirent = (void __user *) buf.current_dir - buf.prev_reclen; |
|---|
| 377 | + if (put_user(d_off, &lastdirent->d_off)) |
|---|
| 364 | 378 | error = -EFAULT; |
|---|
| 365 | 379 | else |
|---|
| 366 | 380 | error = count - buf.count; |
|---|
| 367 | 381 | } |
|---|
| 368 | 382 | fdput_pos(f); |
|---|
| 369 | 383 | return error; |
|---|
| 370 | | -} |
|---|
| 371 | | - |
|---|
| 372 | | - |
|---|
| 373 | | -SYSCALL_DEFINE3(getdents64, unsigned int, fd, |
|---|
| 374 | | - struct linux_dirent64 __user *, dirent, unsigned int, count) |
|---|
| 375 | | -{ |
|---|
| 376 | | - return ksys_getdents64(fd, dirent, count); |
|---|
| 377 | 384 | } |
|---|
| 378 | 385 | |
|---|
| 379 | 386 | #ifdef CONFIG_COMPAT |
|---|
| .. | .. |
|---|
| 411 | 418 | } |
|---|
| 412 | 419 | buf->result++; |
|---|
| 413 | 420 | dirent = buf->dirent; |
|---|
| 414 | | - if (!access_ok(VERIFY_WRITE, dirent, |
|---|
| 421 | + if (!user_write_access_begin(dirent, |
|---|
| 415 | 422 | (unsigned long)(dirent->d_name + namlen + 1) - |
|---|
| 416 | 423 | (unsigned long)dirent)) |
|---|
| 417 | 424 | goto efault; |
|---|
| 418 | | - if ( __put_user(d_ino, &dirent->d_ino) || |
|---|
| 419 | | - __put_user(offset, &dirent->d_offset) || |
|---|
| 420 | | - __put_user(namlen, &dirent->d_namlen) || |
|---|
| 421 | | - __copy_to_user(dirent->d_name, name, namlen) || |
|---|
| 422 | | - __put_user(0, dirent->d_name + namlen)) |
|---|
| 423 | | - goto efault; |
|---|
| 425 | + unsafe_put_user(d_ino, &dirent->d_ino, efault_end); |
|---|
| 426 | + unsafe_put_user(offset, &dirent->d_offset, efault_end); |
|---|
| 427 | + unsafe_put_user(namlen, &dirent->d_namlen, efault_end); |
|---|
| 428 | + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
|---|
| 429 | + user_write_access_end(); |
|---|
| 424 | 430 | return 0; |
|---|
| 431 | +efault_end: |
|---|
| 432 | + user_write_access_end(); |
|---|
| 425 | 433 | efault: |
|---|
| 426 | 434 | buf->result = -EFAULT; |
|---|
| 427 | 435 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 458 | 466 | struct compat_getdents_callback { |
|---|
| 459 | 467 | struct dir_context ctx; |
|---|
| 460 | 468 | struct compat_linux_dirent __user *current_dir; |
|---|
| 461 | | - struct compat_linux_dirent __user *previous; |
|---|
| 469 | + int prev_reclen; |
|---|
| 462 | 470 | int count; |
|---|
| 463 | 471 | int error; |
|---|
| 464 | 472 | }; |
|---|
| .. | .. |
|---|
| 466 | 474 | static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, |
|---|
| 467 | 475 | loff_t offset, u64 ino, unsigned int d_type) |
|---|
| 468 | 476 | { |
|---|
| 469 | | - struct compat_linux_dirent __user * dirent; |
|---|
| 477 | + struct compat_linux_dirent __user *dirent, *prev; |
|---|
| 470 | 478 | struct compat_getdents_callback *buf = |
|---|
| 471 | 479 | container_of(ctx, struct compat_getdents_callback, ctx); |
|---|
| 472 | 480 | compat_ulong_t d_ino; |
|---|
| 473 | 481 | int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + |
|---|
| 474 | 482 | namlen + 2, sizeof(compat_long_t)); |
|---|
| 483 | + int prev_reclen; |
|---|
| 475 | 484 | |
|---|
| 485 | + buf->error = verify_dirent_name(name, namlen); |
|---|
| 486 | + if (unlikely(buf->error)) |
|---|
| 487 | + return buf->error; |
|---|
| 476 | 488 | buf->error = -EINVAL; /* only used if we fail.. */ |
|---|
| 477 | 489 | if (reclen > buf->count) |
|---|
| 478 | 490 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 481 | 493 | buf->error = -EOVERFLOW; |
|---|
| 482 | 494 | return -EOVERFLOW; |
|---|
| 483 | 495 | } |
|---|
| 484 | | - dirent = buf->previous; |
|---|
| 485 | | - if (dirent) { |
|---|
| 486 | | - if (signal_pending(current)) |
|---|
| 487 | | - return -EINTR; |
|---|
| 488 | | - if (__put_user(offset, &dirent->d_off)) |
|---|
| 489 | | - goto efault; |
|---|
| 490 | | - } |
|---|
| 496 | + prev_reclen = buf->prev_reclen; |
|---|
| 497 | + if (prev_reclen && signal_pending(current)) |
|---|
| 498 | + return -EINTR; |
|---|
| 491 | 499 | dirent = buf->current_dir; |
|---|
| 492 | | - if (__put_user(d_ino, &dirent->d_ino)) |
|---|
| 500 | + prev = (void __user *) dirent - prev_reclen; |
|---|
| 501 | + if (!user_write_access_begin(prev, reclen + prev_reclen)) |
|---|
| 493 | 502 | goto efault; |
|---|
| 494 | | - if (__put_user(reclen, &dirent->d_reclen)) |
|---|
| 495 | | - goto efault; |
|---|
| 496 | | - if (copy_to_user(dirent->d_name, name, namlen)) |
|---|
| 497 | | - goto efault; |
|---|
| 498 | | - if (__put_user(0, dirent->d_name + namlen)) |
|---|
| 499 | | - goto efault; |
|---|
| 500 | | - if (__put_user(d_type, (char __user *) dirent + reclen - 1)) |
|---|
| 501 | | - goto efault; |
|---|
| 502 | | - buf->previous = dirent; |
|---|
| 503 | | - dirent = (void __user *)dirent + reclen; |
|---|
| 504 | | - buf->current_dir = dirent; |
|---|
| 503 | + |
|---|
| 504 | + unsafe_put_user(offset, &prev->d_off, efault_end); |
|---|
| 505 | + unsafe_put_user(d_ino, &dirent->d_ino, efault_end); |
|---|
| 506 | + unsafe_put_user(reclen, &dirent->d_reclen, efault_end); |
|---|
| 507 | + unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); |
|---|
| 508 | + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
|---|
| 509 | + user_write_access_end(); |
|---|
| 510 | + |
|---|
| 511 | + buf->prev_reclen = reclen; |
|---|
| 512 | + buf->current_dir = (void __user *)dirent + reclen; |
|---|
| 505 | 513 | buf->count -= reclen; |
|---|
| 506 | 514 | return 0; |
|---|
| 515 | +efault_end: |
|---|
| 516 | + user_write_access_end(); |
|---|
| 507 | 517 | efault: |
|---|
| 508 | 518 | buf->error = -EFAULT; |
|---|
| 509 | 519 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 513 | 523 | struct compat_linux_dirent __user *, dirent, unsigned int, count) |
|---|
| 514 | 524 | { |
|---|
| 515 | 525 | struct fd f; |
|---|
| 516 | | - struct compat_linux_dirent __user * lastdirent; |
|---|
| 517 | 526 | struct compat_getdents_callback buf = { |
|---|
| 518 | 527 | .ctx.actor = compat_filldir, |
|---|
| 519 | 528 | .current_dir = dirent, |
|---|
| 520 | 529 | .count = count |
|---|
| 521 | 530 | }; |
|---|
| 522 | 531 | int error; |
|---|
| 523 | | - |
|---|
| 524 | | - if (!access_ok(VERIFY_WRITE, dirent, count)) |
|---|
| 525 | | - return -EFAULT; |
|---|
| 526 | 532 | |
|---|
| 527 | 533 | f = fdget_pos(fd); |
|---|
| 528 | 534 | if (!f.file) |
|---|
| .. | .. |
|---|
| 531 | 537 | error = iterate_dir(f.file, &buf.ctx); |
|---|
| 532 | 538 | if (error >= 0) |
|---|
| 533 | 539 | error = buf.error; |
|---|
| 534 | | - lastdirent = buf.previous; |
|---|
| 535 | | - if (lastdirent) { |
|---|
| 540 | + if (buf.prev_reclen) { |
|---|
| 541 | + struct compat_linux_dirent __user * lastdirent; |
|---|
| 542 | + lastdirent = (void __user *)buf.current_dir - buf.prev_reclen; |
|---|
| 543 | + |
|---|
| 536 | 544 | if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
|---|
| 537 | 545 | error = -EFAULT; |
|---|
| 538 | 546 | else |
|---|