@@ -473,21 +473,25 @@ struct compat_linux_dirent {
473
473
struct compat_getdents_callback {
474
474
struct dir_context ctx ;
475
475
struct compat_linux_dirent __user * current_dir ;
476
- struct compat_linux_dirent __user * previous ;
476
+ int prev_reclen ;
477
477
int count ;
478
478
int error ;
479
479
};
480
480
481
481
static int compat_filldir (struct dir_context * ctx , const char * name , int namlen ,
482
482
loff_t offset , u64 ino , unsigned int d_type )
483
483
{
484
- struct compat_linux_dirent __user * dirent ;
484
+ struct compat_linux_dirent __user * dirent , * prev ;
485
485
struct compat_getdents_callback * buf =
486
486
container_of (ctx , struct compat_getdents_callback , ctx );
487
487
compat_ulong_t d_ino ;
488
488
int reclen = ALIGN (offsetof(struct compat_linux_dirent , d_name ) +
489
489
namlen + 2 , sizeof (compat_long_t ));
490
+ int prev_reclen ;
490
491
492
+ buf -> error = verify_dirent_name (name , namlen );
493
+ if (unlikely (buf -> error ))
494
+ return buf -> error ;
491
495
buf -> error = - EINVAL ; /* only used if we fail.. */
492
496
if (reclen > buf -> count )
493
497
return - EINVAL ;
@@ -496,29 +500,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
496
500
buf -> error = - EOVERFLOW ;
497
501
return - EOVERFLOW ;
498
502
}
499
- dirent = buf -> previous ;
500
- if (dirent ) {
501
- if (signal_pending (current ))
502
- return - EINTR ;
503
- if (__put_user (offset , & dirent -> d_off ))
504
- goto efault ;
505
- }
503
+ prev_reclen = buf -> prev_reclen ;
504
+ if (prev_reclen && signal_pending (current ))
505
+ return - EINTR ;
506
506
dirent = buf -> current_dir ;
507
- if (__put_user (d_ino , & dirent -> d_ino ))
508
- goto efault ;
509
- if (__put_user (reclen , & dirent -> d_reclen ))
510
- goto efault ;
511
- if (copy_to_user (dirent -> d_name , name , namlen ))
512
- goto efault ;
513
- if (__put_user (0 , dirent -> d_name + namlen ))
514
- goto efault ;
515
- if (__put_user (d_type , (char __user * ) dirent + reclen - 1 ))
507
+ prev = (void __user * ) dirent - prev_reclen ;
508
+ if (!user_write_access_begin (prev , reclen + prev_reclen ))
516
509
goto efault ;
517
- buf -> previous = dirent ;
518
- dirent = (void __user * )dirent + reclen ;
519
- buf -> current_dir = dirent ;
510
+
511
+ unsafe_put_user (offset , & prev -> d_off , efault_end );
512
+ unsafe_put_user (d_ino , & dirent -> d_ino , efault_end );
513
+ unsafe_put_user (reclen , & dirent -> d_reclen , efault_end );
514
+ unsafe_put_user (d_type , (char __user * ) dirent + reclen - 1 , efault_end );
515
+ unsafe_copy_dirent_name (dirent -> d_name , name , namlen , efault_end );
516
+ user_write_access_end ();
517
+
518
+ buf -> prev_reclen = reclen ;
519
+ buf -> current_dir = (void __user * )dirent + reclen ;
520
520
buf -> count -= reclen ;
521
521
return 0 ;
522
+ efault_end :
523
+ user_write_access_end ();
522
524
efault :
523
525
buf -> error = - EFAULT ;
524
526
return - EFAULT ;
@@ -528,7 +530,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
528
530
struct compat_linux_dirent __user * , dirent , unsigned int , count )
529
531
{
530
532
struct fd f ;
531
- struct compat_linux_dirent __user * lastdirent ;
532
533
struct compat_getdents_callback buf = {
533
534
.ctx .actor = compat_filldir ,
534
535
.current_dir = dirent ,
@@ -546,8 +547,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
546
547
error = iterate_dir (f .file , & buf .ctx );
547
548
if (error >= 0 )
548
549
error = buf .error ;
549
- lastdirent = buf .previous ;
550
- if (lastdirent ) {
550
+ if (buf .prev_reclen ) {
551
+ struct compat_linux_dirent __user * lastdirent ;
552
+ lastdirent = (void __user * )buf .current_dir - buf .prev_reclen ;
553
+
551
554
if (put_user (buf .ctx .pos , & lastdirent -> d_off ))
552
555
error = - EFAULT ;
553
556
else
0 commit comments