The uutils implementation of 'chmod' has a TOCTOU race that can lead it to change the permissions of a file pointed to by a symbolic link during traversal. See the following example: ``` $ mkdir -p a/b $ touch a/b/1 a/b/2 a/b/3 $ strace -e trace='/stat|chmod' uu_chmod -R 777 a [...] newfstatat(3, "b", {st_mode=S_IFDIR|0777, st_size=6, ...}, AT_SYMLINK_NOFOLLOW) = 0 statx(AT_FDCWD, "a/b", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFDIR|0777, stx_size=6, ...}) = 0 statx(AT_FDCWD, "a/b", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFDIR|0777, stx_size=6, ...}) = 0 fchmodat(3, "b", 0777) = 0 fstat(5, {st_mode=S_IFDIR|0777, st_size=6, ...}) = 0 newfstatat(4, "1", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 statx(AT_FDCWD, "a/b/1", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 statx(AT_FDCWD, "a/b/1", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 fchmodat(4, "1", 0777) = 0 newfstatat(4, "2", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 statx(AT_FDCWD, "a/b/2", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 statx(AT_FDCWD, "a/b/2", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 fchmodat(4, "2", 0777) = 0 newfstatat(4, "3", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 statx(AT_FDCWD, "a/b/3", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 statx(AT_FDCWD, "a/b/3", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID|STATX_SUBVOL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0 fchmodat(4, "3", 0777) = 0 +++ exited with 0 +++ ``` It is unsafe to rely on the result of stat or statx in the 'chmod' command, generally. The directory entry may change between the call to statx and fchmodat. A malicious user and/or process may create a symbolic link to an arbitrary file in its place between the two calls. If timed correctly, uutils 'chmod' will change the permissions of the file that the symbolic link points to. The classic way to do this is to generate lots of load on a system to slow down the 'uu_chmod' command. Then, use a process with higher priority to remove the file and create a symbolic link in it's place. I think this is worth assigning a CVE. Reproducible: Always Steps to Reproduce: Given in the description. Actual Results: Given in the description. Expected Results: uutils 'chmod' should use functions that do not follow symbolic links. For example, GNU coreutils handles the same example safely using fchmodat2 and the AT_SYMLINK_NOFOLLOW flag: ``` $ strace -e trace='/stat|chmod' chmod -R 777 a [...] newfstatat(4, "b", {st_mode=S_IFDIR|0777, st_size=6, ...}, AT_SYMLINK_NOFOLLOW) = 0 fchmodat2(4, "b", 0777, AT_SYMLINK_NOFOLLOW) = 0 fstat(3, {st_mode=S_IFDIR|0777, st_size=6, ...}) = 0 newfstatat(5, "1", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 fchmodat2(5, "1", 0777, AT_SYMLINK_NOFOLLOW) = 0 newfstatat(5, "2", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 fchmodat2(5, "2", 0777, AT_SYMLINK_NOFOLLOW) = 0 newfstatat(5, "3", {st_mode=S_IFREG|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0 fchmodat2(5, "3", 0777, AT_SYMLINK_NOFOLLOW) = 0 +++ exited with 0 +++