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 | 477 | # 545 reserved for clone3 |
| 478 | 478 | 547 common openat2 sys_openat2 |
| 479 | 479 | 548 common pidfd_getfd sys_pidfd_getfd |
| 480 | +549 common faccessat2 sys_faccessat2 | |
| @@ -451,3 +451,4 @@ · 450 unmodified lines | ||
| 451 | 451 | 435 common clone3 sys_clone3 |
| 452 | 452 | 437 common openat2 sys_openat2 |
| 453 | 453 | 438 common pidfd_getfd sys_pidfd_getfd |
| 454 | +439 common faccessat2 sys_faccessat2 | |
| @@ -38,7 +38,7 @@ · 37 unmodified lines | ||
| 38 | 38 | #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) |
| 39 | 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 | 42 | #endif |
| 43 | 43 | |
| 44 | 44 | #define __ARCH_WANT_SYS_CLONE |
| @@ -883,6 +883,8 @@ __SYSCALL(__NR_clone3, sys_clone3) · 882 unmodified lines | ||
| 883 | 883 | __SYSCALL(__NR_openat2, sys_openat2) |
| 884 | 884 | #define __NR_pidfd_getfd 438 |
| 885 | 885 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) |
| 886 | +#define __NR_faccessat2 439 | |
| 887 | +__SYSCALL(__NR_faccessat2, sys_faccessat2) | |
| 886 | 888 | |
| 887 | 889 | /* |
| 888 | 890 | * Please add new compat syscalls above this comment and update |
| @@ -358,3 +358,4 @@ · 357 unmodified lines | ||
| 358 | 358 | # 435 reserved for clone3 |
| 359 | 359 | 437 common openat2 sys_openat2 |
| 360 | 360 | 438 common pidfd_getfd sys_pidfd_getfd |
| 361 | +439 common faccessat2 sys_faccessat2 | |
| @@ -437,3 +437,4 @@ · 436 unmodified lines | ||
| 437 | 437 | 435 common clone3 __sys_clone3 |
| 438 | 438 | 437 common openat2 sys_openat2 |
| 439 | 439 | 438 common pidfd_getfd sys_pidfd_getfd |
| 440 | +439 common faccessat2 sys_faccessat2 | |
| @@ -443,3 +443,4 @@ · 442 unmodified lines | ||
| 443 | 443 | 435 common clone3 sys_clone3 |
| 444 | 444 | 437 common openat2 sys_openat2 |
| 445 | 445 | 438 common pidfd_getfd sys_pidfd_getfd |
| 446 | +439 common faccessat2 sys_faccessat2 | |
| @@ -376,3 +376,4 @@ · 375 unmodified lines | ||
| 376 | 376 | 435 n32 clone3 __sys_clone3 |
| 377 | 377 | 437 n32 openat2 sys_openat2 |
| 378 | 378 | 438 n32 pidfd_getfd sys_pidfd_getfd |
| 379 | +439 n32 faccessat2 sys_faccessat2 | |
| @@ -352,3 +352,4 @@ · 351 unmodified lines | ||
| 352 | 352 | 435 n64 clone3 __sys_clone3 |
| 353 | 353 | 437 n64 openat2 sys_openat2 |
| 354 | 354 | 438 n64 pidfd_getfd sys_pidfd_getfd |
| 355 | +439 n64 faccessat2 sys_faccessat2 | |
| @@ -425,3 +425,4 @@ · 424 unmodified lines | ||
| 425 | 425 | 435 o32 clone3 __sys_clone3 |
| 426 | 426 | 437 o32 openat2 sys_openat2 |
| 427 | 427 | 438 o32 pidfd_getfd sys_pidfd_getfd |
| 428 | +439 o32 faccessat2 sys_faccessat2 | |
| @@ -435,3 +435,4 @@ · 434 unmodified lines | ||
| 435 | 435 | 435 common clone3 sys_clone3_wrapper |
| 436 | 436 | 437 common openat2 sys_openat2 |
| 437 | 437 | 438 common pidfd_getfd sys_pidfd_getfd |
| 438 | +439 common faccessat2 sys_faccessat2 | |
| @@ -527,3 +527,4 @@ · 526 unmodified lines | ||
| 527 | 527 | 435 spu clone3 sys_ni_syscall |
| 528 | 528 | 437 common openat2 sys_openat2 |
| 529 | 529 | 438 common pidfd_getfd sys_pidfd_getfd |
| 530 | +439 common faccessat2 sys_faccessat2 | |
| @@ -440,3 +440,4 @@ · 439 unmodified lines | ||
| 440 | 440 | 435 common clone3 sys_clone3 sys_clone3 |
| 441 | 441 | 437 common openat2 sys_openat2 sys_openat2 |
| 442 | 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 | 440 | # 435 reserved for clone3 |
| 441 | 441 | 437 common openat2 sys_openat2 |
| 442 | 442 | 438 common pidfd_getfd sys_pidfd_getfd |
| 443 | +439 common faccessat2 sys_faccessat2 | |
| @@ -483,3 +483,4 @@ · 482 unmodified lines | ||
| 483 | 483 | # 435 reserved for clone3 |
| 484 | 484 | 437 common openat2 sys_openat2 |
| 485 | 485 | 438 common pidfd_getfd sys_pidfd_getfd |
| 486 | +439 common faccessat2 sys_faccessat2 | |
| @@ -442,3 +442,4 @@ · 441 unmodified lines | ||
| 442 | 442 | 435 i386 clone3 sys_clone3 |
| 443 | 443 | 437 i386 openat2 sys_openat2 |
| 444 | 444 | 438 i386 pidfd_getfd sys_pidfd_getfd |
| 445 | +439 i386 faccessat2 sys_faccessat2 | |
| @@ -359,6 +359,7 @@ · 358 unmodified lines | ||
| 359 | 359 | 435 common clone3 sys_clone3 |
| 360 | 360 | 437 common openat2 sys_openat2 |
| 361 | 361 | 438 common pidfd_getfd sys_pidfd_getfd |
| 362 | +439 common faccessat2 sys_faccessat2 | |
| 362 | 363 | |
| 363 | 364 | # |
| 364 | 365 | # x32-specific system call numbers start at 512 to avoid cache impact |
| @@ -408,3 +408,4 @@ · 407 unmodified lines | ||
| 408 | 408 | 435 common clone3 sys_clone3 |
| 409 | 409 | 437 common openat2 sys_openat2 |
| 410 | 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 | 126 | extern int build_open_flags(const struct open_how *how, struct open_flags *op); |
| 127 | 127 | |
| 128 | 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 | 129 | int do_fchmodat(int dfd, const char __user *filename, umode_t mode); |
| 131 | 130 | int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, |
| 132 | 131 | int flag); |
| @@ -394,20 +394,30 @@ static const struct cred *access_override_creds(void) · 393 unmodified lines | ||
| 394 | 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 | 399 | struct path path; |
| 400 | 400 | struct inode *inode; |
| 401 | 401 | int res; |
| 402 | 402 | unsigned int lookup_flags = LOOKUP_FOLLOW; |
| 403 | − const struct cred *old_cred; | |
| 403 | + const struct cred *old_cred = NULL; | |
| 404 | 404 | |
| 405 | 405 | if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ |
| 406 | 406 | return -EINVAL; |
| 407 | 407 | |
| 408 | − old_cred = access_override_creds(); | |
| 409 | − if (!old_cred) | |
| 410 | − return -ENOMEM; | |
| 408 | + if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) | |
| 409 | + return -EINVAL; | |
| 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 | 422 | retry: |
| 413 | 423 | res = user_path_at(dfd, filename, lookup_flags, &path); |
| @@ -450,18 +460,26 @@ out_path_release: · 36 unmodified lines | ||
| 450 | 460 | goto retry; |
| 451 | 461 | } |
| 452 | 462 | out: |
| 453 | − revert_creds(old_cred); | |
| 463 | + if (old_cred) | |
| 464 | + revert_creds(old_cred); | |
| 465 | + | |
| 454 | 466 | return res; |
| 455 | 467 | } |
| 456 | 468 | |
| 457 | 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 | 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 | 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 | 428 | #endif |
| 429 | 429 | asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); |
| 430 | 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 | 433 | asmlinkage long sys_chdir(const char __user *filename); |
| 432 | 434 | asmlinkage long sys_fchdir(unsigned int fd); |
| 433 | 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 | 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 | 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 | 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 | 855 | __SYSCALL(__NR_openat2, sys_openat2) |
| 856 | 856 | #define __NR_pidfd_getfd 438 |
| 857 | 857 | __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) |
| 858 | +#define __NR_faccessat2 439 | |
| 859 | +__SYSCALL(__NR_faccessat2, sys_faccessat2) | |
| 858 | 860 | |
| 859 | 861 | #undef __NR_syscalls |
| 860 | −#define __NR_syscalls 439 | |
| 862 | +#define __NR_syscalls 440 | |
| 861 | 863 | |
| 862 | 864 | /* |
| 863 | 865 | * 32 bit systems traditionally used different |
| @@ -84,10 +84,20 @@ · 83 unmodified lines | ||
| 84 | 84 | #define DN_ATTRIB 0x00000020 /* File changed attibutes */ |
| 85 | 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 | 95 | #define AT_FDCWD -100 /* Special value used to indicate |
| 88 | 96 | openat should use the current |
| 89 | 97 | working directory. */ |
| 90 | 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 | 101 | #define AT_REMOVEDIR 0x200 /* Remove directory instead of |
| 92 | 102 | unlinking file. */ |
| 93 | 103 | #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ |