POSIX defines faccessat() as having a fourth "flags" argument, while the linux syscall doesn't have it. Glibc tries to emulate AT_EACCESS and AT_SYMLINK_NOFOLLOW, but AT_EACCESS emulation is broken. Add a new faccessat(2) syscall with the added flags argument and implement both flags. The value of AT_EACCESS is defined in glibc headers to be the same as AT_REMOVEDIR. Use this value for the kernel interface as well, together with the explanatory comment. Also add AT_EMPTY_PATH support, which is not documented by POSIX, but can be useful and is trivial to implement.
Unified · Split
| @@ -477,3 +477,4 @@ · 476 unmodified lines | |||
| 477 | # 545 reserved for clone3 | 477 | # 545 reserved for clone3 |
| 478 | 547 common openat2 sys_openat2 | 478 | 547 common openat2 sys_openat2 |
| 479 | 548 common pidfd_getfd sys_pidfd_getfd | 479 | 548 common pidfd_getfd sys_pidfd_getfd |
| 480 | 549 common faccessat2 sys_faccessat2 | ||
| @@ -451,3 +451,4 @@ · 450 unmodified lines | |||
| 451 | 435 common clone3 sys_clone3 | 451 | 435 common clone3 sys_clone3 |
| 452 | 437 common openat2 sys_openat2 | 452 | 437 common openat2 sys_openat2 |
| 453 | 438 common pidfd_getfd sys_pidfd_getfd | 453 | 438 common pidfd_getfd sys_pidfd_getfd |
| 454 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -38,7 +38,7 @@ · 37 unmodified lines | |||
| 38 | #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) | 38 | #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) |
| 39 | #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) | 39 | #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) |
| 40 | 40 | ||
| 41 | #define __NR_compat_syscalls 439 | 41 | #define __NR_compat_syscalls 440 |
| 42 | #endif | 42 | #endif |
| 43 | 43 | ||
| 44 | #define __ARCH_WANT_SYS_CLONE | 44 | #define __ARCH_WANT_SYS_CLONE |
| @@ -883,6 +883,8 @@ __SYSCALL(__NR_clone3, sys_clone3) · 882 unmodified lines | |||
| 883 | __SYSCALL(__NR_openat2, sys_openat2) | 883 | __SYSCALL(__NR_openat2, sys_openat2) |
| 884 | #define __NR_pidfd_getfd 438 | 884 | #define __NR_pidfd_getfd 438 |
| 885 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) | 885 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) |
| 886 | #define __NR_faccessat2 439 | ||
| 887 | __SYSCALL(__NR_faccessat2, sys_faccessat2) | ||
| 886 | 888 | ||
| 887 | /* | 889 | /* |
| 888 | * Please add new compat syscalls above this comment and update | 890 | * Please add new compat syscalls above this comment and update |
| @@ -358,3 +358,4 @@ · 357 unmodified lines | |||
| 358 | # 435 reserved for clone3 | 358 | # 435 reserved for clone3 |
| 359 | 437 common openat2 sys_openat2 | 359 | 437 common openat2 sys_openat2 |
| 360 | 438 common pidfd_getfd sys_pidfd_getfd | 360 | 438 common pidfd_getfd sys_pidfd_getfd |
| 361 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -437,3 +437,4 @@ · 436 unmodified lines | |||
| 437 | 435 common clone3 __sys_clone3 | 437 | 435 common clone3 __sys_clone3 |
| 438 | 437 common openat2 sys_openat2 | 438 | 437 common openat2 sys_openat2 |
| 439 | 438 common pidfd_getfd sys_pidfd_getfd | 439 | 438 common pidfd_getfd sys_pidfd_getfd |
| 440 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -443,3 +443,4 @@ · 442 unmodified lines | |||
| 443 | 435 common clone3 sys_clone3 | 443 | 435 common clone3 sys_clone3 |
| 444 | 437 common openat2 sys_openat2 | 444 | 437 common openat2 sys_openat2 |
| 445 | 438 common pidfd_getfd sys_pidfd_getfd | 445 | 438 common pidfd_getfd sys_pidfd_getfd |
| 446 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -376,3 +376,4 @@ · 375 unmodified lines | |||
| 376 | 435 n32 clone3 __sys_clone3 | 376 | 435 n32 clone3 __sys_clone3 |
| 377 | 437 n32 openat2 sys_openat2 | 377 | 437 n32 openat2 sys_openat2 |
| 378 | 438 n32 pidfd_getfd sys_pidfd_getfd | 378 | 438 n32 pidfd_getfd sys_pidfd_getfd |
| 379 | 439 n32 faccessat2 sys_faccessat2 | ||
| @@ -352,3 +352,4 @@ · 351 unmodified lines | |||
| 352 | 435 n64 clone3 __sys_clone3 | 352 | 435 n64 clone3 __sys_clone3 |
| 353 | 437 n64 openat2 sys_openat2 | 353 | 437 n64 openat2 sys_openat2 |
| 354 | 438 n64 pidfd_getfd sys_pidfd_getfd | 354 | 438 n64 pidfd_getfd sys_pidfd_getfd |
| 355 | 439 n64 faccessat2 sys_faccessat2 | ||
| @@ -425,3 +425,4 @@ · 424 unmodified lines | |||
| 425 | 435 o32 clone3 __sys_clone3 | 425 | 435 o32 clone3 __sys_clone3 |
| 426 | 437 o32 openat2 sys_openat2 | 426 | 437 o32 openat2 sys_openat2 |
| 427 | 438 o32 pidfd_getfd sys_pidfd_getfd | 427 | 438 o32 pidfd_getfd sys_pidfd_getfd |
| 428 | 439 o32 faccessat2 sys_faccessat2 | ||
| @@ -435,3 +435,4 @@ · 434 unmodified lines | |||
| 435 | 435 common clone3 sys_clone3_wrapper | 435 | 435 common clone3 sys_clone3_wrapper |
| 436 | 437 common openat2 sys_openat2 | 436 | 437 common openat2 sys_openat2 |
| 437 | 438 common pidfd_getfd sys_pidfd_getfd | 437 | 438 common pidfd_getfd sys_pidfd_getfd |
| 438 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -527,3 +527,4 @@ · 526 unmodified lines | |||
| 527 | 435 spu clone3 sys_ni_syscall | 527 | 435 spu clone3 sys_ni_syscall |
| 528 | 437 common openat2 sys_openat2 | 528 | 437 common openat2 sys_openat2 |
| 529 | 438 common pidfd_getfd sys_pidfd_getfd | 529 | 438 common pidfd_getfd sys_pidfd_getfd |
| 530 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -440,3 +440,4 @@ · 439 unmodified lines | |||
| 440 | 435 common clone3 sys_clone3 sys_clone3 | 440 | 435 common clone3 sys_clone3 sys_clone3 |
| 441 | 437 common openat2 sys_openat2 sys_openat2 | 441 | 437 common openat2 sys_openat2 sys_openat2 |
| 442 | 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd | 442 | 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd |
| 443 | 439 common faccessat2 sys_faccessat2 sys_faccessat2 | ||
| @@ -440,3 +440,4 @@ · 439 unmodified lines | |||
| 440 | # 435 reserved for clone3 | 440 | # 435 reserved for clone3 |
| 441 | 437 common openat2 sys_openat2 | 441 | 437 common openat2 sys_openat2 |
| 442 | 438 common pidfd_getfd sys_pidfd_getfd | 442 | 438 common pidfd_getfd sys_pidfd_getfd |
| 443 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -483,3 +483,4 @@ · 482 unmodified lines | |||
| 483 | # 435 reserved for clone3 | 483 | # 435 reserved for clone3 |
| 484 | 437 common openat2 sys_openat2 | 484 | 437 common openat2 sys_openat2 |
| 485 | 438 common pidfd_getfd sys_pidfd_getfd | 485 | 438 common pidfd_getfd sys_pidfd_getfd |
| 486 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -442,3 +442,4 @@ · 441 unmodified lines | |||
| 442 | 435 i386 clone3 sys_clone3 | 442 | 435 i386 clone3 sys_clone3 |
| 443 | 437 i386 openat2 sys_openat2 | 443 | 437 i386 openat2 sys_openat2 |
| 444 | 438 i386 pidfd_getfd sys_pidfd_getfd | 444 | 438 i386 pidfd_getfd sys_pidfd_getfd |
| 445 | 439 i386 faccessat2 sys_faccessat2 | ||
| @@ -359,6 +359,7 @@ · 358 unmodified lines | |||
| 359 | 435 common clone3 sys_clone3 | 359 | 435 common clone3 sys_clone3 |
| 360 | 437 common openat2 sys_openat2 | 360 | 437 common openat2 sys_openat2 |
| 361 | 438 common pidfd_getfd sys_pidfd_getfd | 361 | 438 common pidfd_getfd sys_pidfd_getfd |
| 362 | 439 common faccessat2 sys_faccessat2 | ||
| 362 | 363 | ||
| 363 | # | 364 | # |
| 364 | # x32-specific system call numbers start at 512 to avoid cache impact | 365 | # x32-specific system call numbers start at 512 to avoid cache impact |
| @@ -408,3 +408,4 @@ · 407 unmodified lines | |||
| 408 | 435 common clone3 sys_clone3 | 408 | 435 common clone3 sys_clone3 |
| 409 | 437 common openat2 sys_openat2 | 409 | 437 common openat2 sys_openat2 |
| 410 | 438 common pidfd_getfd sys_pidfd_getfd | 410 | 438 common pidfd_getfd sys_pidfd_getfd |
| 411 | 439 common faccessat2 sys_faccessat2 | ||
| @@ -126,7 +126,6 @@ extern struct open_how build_open_how(int flags, umode_t mode); · 125 unmodified lines | |||
| 126 | extern int build_open_flags(const struct open_how *how, struct open_flags *op); | 126 | extern int build_open_flags(const struct open_how *how, struct open_flags *op); |
| 127 | 127 | ||
| 128 | long do_sys_ftruncate(unsigned int fd, loff_t length, int small); | 128 | long do_sys_ftruncate(unsigned int fd, loff_t length, int small); |
| 129 | long do_faccessat(int dfd, const char __user *filename, int mode); | ||
| 130 | int do_fchmodat(int dfd, const char __user *filename, umode_t mode); | 129 | int do_fchmodat(int dfd, const char __user *filename, umode_t mode); |
| 131 | int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, | 130 | int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, |
| 132 | int flag); | 131 | int flag); |
| @@ -394,20 +394,30 @@ static const struct cred *access_override_creds(void) · 393 unmodified lines | |||
| 394 | return old_cred; | 394 | return old_cred; |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | long do_faccessat(int dfd, const char __user *filename, int mode) | 397 | long do_faccessat(int dfd, const char __user *filename, int mode, int flags) |
| 398 | { | 398 | { |
| 399 | struct path path; | 399 | struct path path; |
| 400 | struct inode *inode; | 400 | struct inode *inode; |
| 401 | int res; | 401 | int res; |
| 402 | unsigned int lookup_flags = LOOKUP_FOLLOW; | 402 | unsigned int lookup_flags = LOOKUP_FOLLOW; |
| 403 | const struct cred *old_cred; | 403 | const struct cred *old_cred = NULL; |
| 404 | 404 | ||
| 405 | if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ | 405 | if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ |
| 406 | return -EINVAL; | 406 | return -EINVAL; |
| 407 | 407 | ||
| 408 | old_cred = access_override_creds(); | 408 | if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) |
| 409 | if (!old_cred) | 409 | return -EINVAL; |
| 410 | return -ENOMEM; | 410 | |
| 411 | if (flags & AT_SYMLINK_NOFOLLOW) | ||
| 412 | lookup_flags &= ~LOOKUP_FOLLOW; | ||
| 413 | if (flags & AT_EMPTY_PATH) | ||
| 414 | lookup_flags |= LOOKUP_EMPTY; | ||
| 415 | |||
| 416 | if (!(flags & AT_EACCESS)) { | ||
| 417 | old_cred = access_override_creds(); | ||
| 418 | if (!old_cred) | ||
| 419 | return -ENOMEM; | ||
| 420 | } | ||
| 411 | 421 | ||
| 412 | retry: | 422 | retry: |
| 413 | res = user_path_at(dfd, filename, lookup_flags, &path); | 423 | res = user_path_at(dfd, filename, lookup_flags, &path); |
| @@ -450,18 +460,26 @@ out_path_release: · 36 unmodified lines | |||
| 450 | goto retry; | 460 | goto retry; |
| 451 | } | 461 | } |
| 452 | out: | 462 | out: |
| 453 | revert_creds(old_cred); | 463 | if (old_cred) |
| 464 | revert_creds(old_cred); | ||
| 465 | |||
| 454 | return res; | 466 | return res; |
| 455 | } | 467 | } |
| 456 | 468 | ||
| 457 | SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) | 469 | SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) |
| 458 | { | 470 | { |
| 459 | return do_faccessat(dfd, filename, mode); | 471 | return do_faccessat(dfd, filename, mode, 0); |
| 472 | } | ||
| 473 | |||
| 474 | SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode, | ||
| 475 | int, flags) | ||
| 476 | { | ||
| 477 | return do_faccessat(dfd, filename, mode, flags); | ||
| 460 | } | 478 | } |
| 461 | 479 | ||
| 462 | SYSCALL_DEFINE2(access, const char __user *, filename, int, mode) | 480 | SYSCALL_DEFINE2(access, const char __user *, filename, int, mode) |
| 463 | { | 481 | { |
| 464 | return do_faccessat(AT_FDCWD, filename, mode); | 482 | return do_faccessat(AT_FDCWD, filename, mode, 0); |
| 465 | } | 483 | } |
| 466 | 484 | ||
| 467 | int ksys_chdir(const char __user *filename) | 485 | int ksys_chdir(const char __user *filename) |
| @@ -428,6 +428,8 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length); · 427 unmodified lines | |||
| 428 | #endif | 428 | #endif |
| 429 | asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); | 429 | asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); |
| 430 | asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode); | 430 | asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode); |
| 431 | asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode, | ||
| 432 | int flags); | ||
| 431 | asmlinkage long sys_chdir(const char __user *filename); | 433 | asmlinkage long sys_chdir(const char __user *filename); |
| 432 | asmlinkage long sys_fchdir(unsigned int fd); | 434 | asmlinkage long sys_fchdir(unsigned int fd); |
| 433 | asmlinkage long sys_chroot(const char __user *filename); | 435 | asmlinkage long sys_chroot(const char __user *filename); |
| @@ -1333,11 +1335,11 @@ static inline int ksys_chmod(const char __user *filename, umode_t mode) · 899 unmodified lines | |||
| 1333 | return do_fchmodat(AT_FDCWD, filename, mode); | 1335 | return do_fchmodat(AT_FDCWD, filename, mode); |
| 1334 | } | 1336 | } |
| 1335 | 1337 | ||
| 1336 | extern long do_faccessat(int dfd, const char __user *filename, int mode); | 1338 | long do_faccessat(int dfd, const char __user *filename, int mode, int flags); |
| 1337 | 1339 | ||
| 1338 | static inline long ksys_access(const char __user *filename, int mode) | 1340 | static inline long ksys_access(const char __user *filename, int mode) |
| 1339 | { | 1341 | { |
| 1340 | return do_faccessat(AT_FDCWD, filename, mode); | 1342 | return do_faccessat(AT_FDCWD, filename, mode, 0); |
| 1341 | } | 1343 | } |
| 1342 | 1344 | ||
| 1343 | extern int do_fchownat(int dfd, const char __user *filename, uid_t user, | 1345 | extern int do_fchownat(int dfd, const char __user *filename, uid_t user, |
| @@ -855,9 +855,11 @@ __SYSCALL(__NR_clone3, sys_clone3) · 854 unmodified lines | |||
| 855 | __SYSCALL(__NR_openat2, sys_openat2) | 855 | __SYSCALL(__NR_openat2, sys_openat2) |
| 856 | #define __NR_pidfd_getfd 438 | 856 | #define __NR_pidfd_getfd 438 |
| 857 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) | 857 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) |
| 858 | #define __NR_faccessat2 439 | ||
| 859 | __SYSCALL(__NR_faccessat2, sys_faccessat2) | ||
| 858 | 860 | ||
| 859 | #undef __NR_syscalls | 861 | #undef __NR_syscalls |
| 860 | #define __NR_syscalls 439 | 862 | #define __NR_syscalls 440 |
| 861 | 863 | ||
| 862 | /* | 864 | /* |
| 863 | * 32 bit systems traditionally used different | 865 | * 32 bit systems traditionally used different |
| @@ -84,10 +84,20 @@ · 83 unmodified lines | |||
| 84 | #define DN_ATTRIB 0x00000020 /* File changed attibutes */ | 84 | #define DN_ATTRIB 0x00000020 /* File changed attibutes */ |
| 85 | #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ | 85 | #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ |
| 86 | 86 | ||
| 87 | /* | ||
| 88 | * The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is | ||
| 89 | * meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to | ||
| 90 | * unlinkat. The two functions do completely different things and therefore, | ||
| 91 | * the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to | ||
| 92 | * faccessat would be undefined behavior and thus treating it equivalent to | ||
| 93 | * AT_EACCESS is valid undefined behavior. | ||
| 94 | */ | ||
| 87 | #define AT_FDCWD -100 /* Special value used to indicate | 95 | #define AT_FDCWD -100 /* Special value used to indicate |
| 88 | openat should use the current | 96 | openat should use the current |
| 89 | working directory. */ | 97 | working directory. */ |
| 90 | #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */ | 98 | #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */ |
| 99 | #define AT_EACCESS 0x200 /* Test access permitted for | ||
| 100 | effective IDs, not real IDs. */ | ||
| 91 | #define AT_REMOVEDIR 0x200 /* Remove directory instead of | 101 | #define AT_REMOVEDIR 0x200 /* Remove directory instead of |
| 92 | unlinking file. */ | 102 | unlinking file. */ |
| 93 | #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ | 103 | #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ |