Hi Bruno and Andreas,
Sorry, my previous message was unclear. Here are four separate scenarios.
I repeated these tests on Linux aarch64, using Gnulib
8f5547126aedc70a9c2cdb7c63829c87c3d71df1, libacl 2.3.2-2+b1 and
libattr 2.5.2-3, with USE_ACL=1 and USE_XATTR=1. All files and directories
were owned by the same unprivileged user (UID 1000). All supplied file
descriptors were valid positive numbers, except the explicit -1 in case 4.
For each test, create fresh entries in an otherwise empty directory.
Use these initial ACLs (the numeric user IDs are only test data):
source access: user::rwx,user:2001:r-x,group::r-x,mask::r-x,other::---
target/other access: user::rwx,user:2002:rw-,group::r--,mask::rw-,other::---
source default: user::rwx,user:2003:r--,group::r-x,mask::r-x,other::---
target/other default: user::rwx,user:2004:rw-,group::r--,mask::rw-,other::---
1. Reading a directory's default ACL
Steps: Create directories source and other with the ACLs above. Open
source and save its fd and st_mode. Rename source to source-open, then
create the symlink source -> other. Call
get_permissions("source", source_fd, source_mode, &ctx).
Expected: Both ACLs in ctx come from source-open: access contains user
2001, and default contains user 2003.
Actual: The function returns 0. The access ACL contains user 2001, but
the default ACL contains user 2004, from other. source-open still has
default user 2003.
2. Setting a directory's default ACL
Steps: Create directories source, target and other. Open source and
target. Save source's st_mode and read its ACLs with
get_permissions("source", source_fd, source_mode, &ctx). Then rename
target to target-open and create target -> other. Call
set_permissions(&ctx, "target", target_fd).
Expected: target-open receives both source ACLs; other is unchanged.
Actual: The function returns 0. target-open receives access user 2001,
but keeps default user 2004. other receives default user 2003 instead.
3. Deleting a directory's default ACL
Steps: Repeat case 2, but start with no default ACL on source. target
and other still start with the default ACL containing user 2004.
Expected: target-open has no default ACL after set_permissions; other
keeps its default ACL.
Actual: The function returns 0. target-open keeps its default ACL,
while other's default ACL is deleted.
4. A source pathname with an open destination fd
Steps: Create regular files source, target and other, using only the
access ACLs above. Save source's st_mode (0100750) and open target.
Rename target to target-open and create target -> other. Call
qcopy_acl("source", -1, "target", target_fd, source_mode).
Expected, assuming this mixed call is supported: Copy source's access
ACL to target-open through target_fd; leave other unchanged; return 0.
Actual: return=-1, errno=95 (Operation not supported). target-open still
has user 2002, but its mask has already changed from rw- to r-x.
other is unchanged. This is a failed copy with a partial change, not a
successful copy to other.
All four tests pass if the names are left unchanged. Case 4 also passes
with the replaced target name when both source and target have valid
positive fds. Applying only the separate fd=0 patch leaves all four
results above unchanged.
These tests call the helpers directly; they do not demonstrate an
exploit through cp, mv, install, sed or tar.
Are cases 1-3 expected limitations with this libacl version, or should
Gnulib use the newer libacl APIs when available? Is the mixed call in
case 4 intended to honor the valid destination fd?
Thanks,
Eric Cai
On Sun, 25 Oct 2026 01:01:07 +0200, Bruno Haible <[email protected]> wrote:
> Hi,
>
> Eric Cai wrote:
> > This AI-assisted message is an RFC and test report
>
> I don't understand your message. It uses words like "object", "contract",
> "decoy", "baseline", "blanket".
>
> Please write a message by hand, that humans can understand. Ideally with
> one or more separate "how to reproduce" scenarios.
>
> Also please CC Andreas Gruenbacher.
>
> Bruno