Hello,

In Gnulib 8f5547126aedc70a9c2cdb7c63829c87c3d71df1, qcopy_acl's
USE_XATTR branch tests source_desc <= 0 || dest_desc <= 0 when choosing
between attr_copy_file and attr_copy_fd. A descriptor of zero is valid,
so this can use pathnames instead of the already-open objects, contrary
to the function's documented descriptor semantics.

The proposal below changes only that validity test. Negative
descriptors retain the existing pathname fallback. It does not address
mixed pathname/descriptor calls or the POSIX default-ACL helpers.

Reproduction outline (all fixtures owned by the same ordinary UID):

1. On an ACL-capable Linux filesystem, create source, destination and
   decoy files with distinct nontrivial access ACLs, and open them.
2. Rename the source, retaining its open descriptor, and replace the
   original source name with a symlink to the decoy. Use dup2 to put the
   source descriptor at zero, closing its former descriptor.
3. Call qcopy_acl with these source/destination names and valid
   descriptors, and the original source's st_mode. Compare the opened
   destination's ACL with the opened source's ACL; also check that the
   decoy ACL is unchanged.
4. In a separate fresh fixture, instead replace the destination name
   and put its descriptor at zero. Repeat the same checks.
5. Repeat both cases with positive descriptors as controls; also test
   stable pathname-only calls with both descriptors set to -1.

With real Gnulib objects, USE_ACL=1 and USE_XATTR=1:

- Source descriptor zero: before the patch, qcopy_acl returned 0 but the
  destination ACL did not match the opened source. Afterward it returned
  0 and the ACL checks passed.
- Destination descriptor zero: before the patch, qcopy_acl returned -1
  and the checks failed. Afterward it returned 0 and the checks passed.
  This case is not evidence of arbitrary-target modification.
- Positive-descriptor replacement controls, stable pathname-only
  control and an ordinary directory default-ACL control passed both
  before and after.
- The six existing copy-acl/set-mode-acl test scripts passed before and
  after: 6 PASS, 0 FAIL, 0 SKIP each time. A restored-baseline repeat and
  a fresh build reproduced the same results.

Environment: Linux ext4, UID 1000, libacl 2.3.2-2+b1, libattr 2.5.2-3.
The linux/amd64 binaries ran through user-mode emulation on an ARM64
host. No syscall mocks, link wrappers or LD_PRELOAD were used. The
private test container had no network, capabilities or business data.

This is a deterministic API/object-identity test, not a probabilistic
race or a cross-user privilege-escalation demonstration. I am not
claiming a new CVE or a demonstrated exploit through any downstream CLI.
Four separate default-ACL/mixed-descriptor diagnostics still fail and
are being raised separately as an RFC; the overall downstream release
gate remains on HOLD.

I checked the current Savannah HEAD (unchanged at the revision above),
recent relevant source history and public mailing-list/search results.
I did not find an equivalent fd=0 fix in that bounded search; private
reports and unindexed mail may exist. The related 2025 NFSv4 fix retains
this validity test:
https://lists.gnu.org/archive/html/bug-gnulib/2025-05/msg00123.html

AI assistance was used to prepare this one-line proposal. No generated
test harness or larger implementation is being submitted. Please advise
if additional contribution paperwork is required.

Thanks,
Eric Cai

* lib/qcopy-acl.c (qcopy_acl): Treat descriptor zero as valid when
choosing descriptor-based attribute copying.

---
 lib/qcopy-acl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
--- a/lib/qcopy-acl.c
+++ b/lib/qcopy-acl.c
@@ -89,7 +89,7 @@ qcopy_acl (const char *src_name, int source_desc,
const char *dst_name,
      to copy */
   if (ret == 0)
     {
-      ret = source_desc <= 0 || dest_desc <= 0
+      ret = source_desc < 0 || dest_desc < 0
         ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
         : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
                         is_attr_permissions, NULL);

Reply via email to