Causes test breakage in sys-apps/coreutils-8.7: gnulib-tests/test-utimensat gnulib-tests/test-fdutimensat Pseudocode is something like this ("path" is the path to a symlink): lstat(path,&st1); sleep(1); utimensat(AT_FDCWD, path, (struct timespec const[]){ {946684800, UTIME_OMIT}, {946684800, 0} }, AT_SYMLINK_NOFOLLOW); sleep(1); lstat(path,&st2); assert(st1.st_atim.tv_sec == st2.st_atim.tv_sec && st1.st_atim.tv_nsec == st2.st_atim.tv_nsec); The symlink is never followed, so its atime should not change. When running in sandbox, it *does* change (strace suggests that this is due to a call to readlink()). I *think* it happens when check_syscall() calls resolve_path() (which calls erealpath()/realpath()). Unfortunately, it is not as simple as changing resolved_path = resolve_path(file, 1); to resolved_path = resolve_path(file, !(flags & AT_SYMLINK_NOFOLLOW)); because (AIUI) "a/b/c",AT_SYMLINK_NOFOLLOW still potentially resolves symlinks a and b. Additionally, Linux (or glibc) appears to check the validity of timespecs before resolving the symlink. That is, utimensat(AT_FDCWD, path, (struct timespec const[]){ {946684800, -1}, {946684800, 1000000000} }, 0); does not read the link because -1 and 1000000000 are not valid in tv_nsec. (This assumes UTIME_NOW and UTIME_OMIT are not -1 or 10**9; /usr/include/bits/stat.h says they're 2**30-1 and 2**30-2 respectively.) However, checking for the validity of timespecs does not fit into the autogenerated syscall-wrapper structure as easily. It also is conceptually a less reliable assumption to make (timespec-checking could conceptually happen in an a kernel-internal function that operates on an inode). Patch coming up...
dupe of bug 348640 ?
Nope, utimensat() correctly returns EINVAL (in bug 348640 it returns ENOSYS "unimplemented system call" or so).
Created attachment 257208 [details] Test case. It goes something like this # cc -o tc-utimensat tc-utimensat.c # sandbox ./tc-utimensat 1292387703.2773884 1292387703.102770037 tc-utimensat: tc-utimensat.c:36: main: Assertion `t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec' failed. Sandboxed process killed by signal: Aborted # ./tc-utimensat 1292387713.572769865 1292387713.572769865 Caveats: - The FS needs to update atimes (i.e. not be mounted with relatime/noatime). - The dir needs to be writable in the sandbox The easiest way is to stick it in /dev/shm (I use /var/tmp/portage because it's a tempfs for me).
Created attachment 257210 [details, diff] sandbox-2.4/libsandbox/libsandbox.c patch Bugs: - It doesn't special-case the empty string (does it need to?). - It doesn't apply only to the syscalls which accept AT_SYMLINK_NOFOLLOW (e.g. it might be possible to bypass the sandbox with openat(AT_SYMLINK_NOFOLLOW)). I'm not sure what the kernel does here (curiously, there's also AT_SYMLINK_FOLLOW though I can't find the syscall where it's used). - Everything else I forgot, because it's 5 AM (oops).
so let me see if i have this right ... the issue is that dereferencing a symlink updates its atime, and using utimensat(AT_SYMLINK_NOFOLLOW) will implicitly dereference the symlink by sandbox internals and thus update the atime ?
assuming my comment #5 is what you mean, i dont see this worth fixing. atime is almost universally useless.
It's just that it happens to check atime - it (presumably) also checks permissions against the destination of the link despite being a system call that shouldn't follow the link.