On a cross-filesystem move, the xattr copy loop stops at the first per-attribute error. mv then discards that error, unlinks the source and exits 0 with no diagnostic — so the destination silently ends up with fewer attributes than the source, including attributes the destination filesystem fully supports.
Line references are against main at be00b4c4e.
Two separable defects
1. The loop aborts the whole list (src/uucore/src/lib/features/fsxattr.rs)
copy_xattrs (:34), copy_xattrs_fd (:56) and copy_xattrs_skip_selinux (:80) all ? out on the first failure:
for attr_name in xattr::list(&source)? {
if let Some(value) = xattr::get(&source, &attr_name)? {
xattr::set(&dest, &attr_name, &value)?; // aborts the remaining attributes
}
}
The *_ignore_unsupported wrappers only map ENOTSUP/EOPNOTSUPP, so ENOSPC, EPERM, EACCES, EDQUOT and E2BIG abort the rest of the list. GNU reports the failing attribute and continues with the others.
This is shared uucore code, so it also affects cp --preserve=xattr / cp -a.
2. mv throws the result away
src/uu/mv/src/mv.rs:1043, :1423 and :1488 are each let _ = fsxattr::copy_xattrs*(...). This is what makes the loss silent. cp does propagate the error (cp.rs:1847), so this half is mv-only.
Reproduction
Source on tmpfs (accepts a 60 KB value), destination on ext4 (~4 KB per-attribute limit), file carrying both user.big and security.capability in that order:
$ setcap cap_net_bind_service=ep src/art # plus a 60000-byte user.big
$ uutils-mv src/art dst/art ; echo "exit: $?"
exit: 0 # no output at all
$ listxattr dst/art -> ['security.selinux']
$ getcap dst/art -> (nothing)
$ gnu-mv src/art2 dst/art2 ; echo "exit: $?"
mv: setting attribute 'user.big': No space left on device
exit: 0
$ listxattr dst/art2 -> ['security.selinux', 'security.capability']
$ getcap dst/art2 -> cap_net_bind_service=ep
Both remove the source. Only GNU preserves the capability, which the destination supports and which failed to be copied only because an unrelated earlier attribute did.
Control: GNU coreutils 9.11.130 (master).
Expected
Record the failure, keep going, and report each failed attribute on stderr, while keeping exit 0 for a completed move — GNU's behaviour.
Suggested test
Two attributes in a known order; force the first destination setxattr to fail; assert the second is still attempted and preserved.
Reported by Hongkai Chen (SEFCOM Lab, Arizona State University). Triaged as a correctness bug rather than a security issue — the demonstrated outcome is a deployed file with fewer privileges than intended.
On a cross-filesystem move, the xattr copy loop stops at the first per-attribute error.
mvthen discards that error, unlinks the source and exits 0 with no diagnostic — so the destination silently ends up with fewer attributes than the source, including attributes the destination filesystem fully supports.Line references are against
mainatbe00b4c4e.Two separable defects
1. The loop aborts the whole list (
src/uucore/src/lib/features/fsxattr.rs)copy_xattrs(:34),copy_xattrs_fd(:56) andcopy_xattrs_skip_selinux(:80) all?out on the first failure:The
*_ignore_unsupportedwrappers only mapENOTSUP/EOPNOTSUPP, soENOSPC,EPERM,EACCES,EDQUOTandE2BIGabort the rest of the list. GNU reports the failing attribute and continues with the others.This is shared
uucorecode, so it also affectscp --preserve=xattr/cp -a.2.
mvthrows the result awaysrc/uu/mv/src/mv.rs:1043,:1423and:1488are eachlet _ = fsxattr::copy_xattrs*(...). This is what makes the loss silent.cpdoes propagate the error (cp.rs:1847), so this half ismv-only.Reproduction
Source on tmpfs (accepts a 60 KB value), destination on ext4 (~4 KB per-attribute limit), file carrying both
user.bigandsecurity.capabilityin that order:Both remove the source. Only GNU preserves the capability, which the destination supports and which failed to be copied only because an unrelated earlier attribute did.
Control: GNU coreutils 9.11.130 (master).
Expected
Record the failure, keep going, and report each failed attribute on stderr, while keeping exit 0 for a completed move — GNU's behaviour.
Suggested test
Two attributes in a known order; force the first destination
setxattrto fail; assert the second is still attempted and preserved.Reported by Hongkai Chen (SEFCOM Lab, Arizona State University). Triaged as a correctness bug rather than a security issue — the demonstrated outcome is a deployed file with fewer privileges than intended.