#1112486 adduser: deluser --remove-all-files fails when a path includes $'\177'

#1112486#5
Date:
2025-08-30 02:10:15 UTC
From:
To:
Package: adduser
Version: 3.152

Offhand, and without remembering perl's exact semantics on this front,
I'd guess this is a case of assuming paths are decodable in the current
locale. If so, it should be possible to reproduce the problem from a
UTF-8 locale like this:

  touch /any/where/not-utf-8-but-possible-$'\177'
  deluser --remove-all-files someuser

Here the equivalent eventually fails to remove /home/someuser,
reporting:

  invalid characters in input string, see trace output for more details
  at /usr/share/perl5/Debian/AdduserCommon.pm line 143.

Though it doesn't actually indicate there was trouble in the exit
status, i.e. the exit status was 0.

(I had a path like that in a different user's $HOME, leftover from
 running some bup tests for this kind of thing.)

Thanks

#1112486#10
Date:
2025-08-30 02:51:29 UTC
From:
To:
Rob Browning <rlb@defaultvalue.org> writes:

Oh, wait, probably not locale related since DEL is valid UTF-8.

In any case, I worked around it for now by just using find's -delete
with -user and -group to remove all the files and then ran deluser
without --remove-all-files.

#1112486#15
Date:
2025-08-30 08:52:08 UTC
From:
To:
I think this might finally the straw that breaks the camel's back and
will cause us to either deprecate and eventuelly remove
--remove-all-files, or just delegate that task to the "Essential: yes" 
find(1) in the first place. I am wondering why that wasn't implemented
directly anyway.

When implementing the find(1) thing, deluser will just error out if find
is not present. That will only happen if some package decides to
_delete_ a user with all their data in the installer, in a fully
installed base system the Esssential packages are at least going to be
unpacked.

Greetings
Marc

#1112486#20
Date:
2025-08-30 17:54:52 UTC
From:
To:
Marc Haber <mh+debian-packages@zugschlus.de> writes:

From a quick glance, it seemed like there was notable pattern matching
in various places, so I wondered if it was trying to be careful on some
way that might or might not be important (as compared to find).

But of course in the end, and as I think you suggest, I'd imagine
--remove-all-files ought to handle everything except '\0' if we're going
to have it.

I suppose we could also consider making it exit(2) or something for now
when it fails there (assuming it shouldn't be "fine" to exit without
actually removing all the files).

In any case, thanks for taking a loook.

#1112486#25
Date:
2025-08-31 10:23:43 UTC
From:
To:
Hi,

That worked for me in an nspawn container with en_US.UTF-8 locale:

| root@salida-sid-buildd-amd64-08lb:/srv# LANG=en_US.UTF-8
| root@salida-sid-buildd-amd64-08lb:/srv# locale
| LANG=en_US.UTF-8
| LANGUAGE=
| LC_CTYPE="en_US.UTF-8"
| LC_NUMERIC="en_US.UTF-8"
| LC_TIME="en_US.UTF-8"
| LC_COLLATE="en_US.UTF-8"
| LC_MONETARY="en_US.UTF-8"
| LC_MESSAGES="en_US.UTF-8"
| LC_PAPER="en_US.UTF-8"
| LC_NAME="en_US.UTF-8"
| LC_ADDRESS="en_US.UTF-8"
| LC_TELEPHONE="en_US.UTF-8"
| LC_MEASUREMENT="en_US.UTF-8"
| LC_IDENTIFICATION="en_US.UTF-8"
| LC_ALL=
| root@salida-sid-buildd-amd64-08lb:/srv# adduser --disabled-password --comment="blubb" blubb
| root@salida-sid-buildd-amd64-08lb:/srv# touch /var/lib/not-utf-8-but-possible-$'\177'
| root@salida-sid-buildd-amd64-08lb:/srv# ls -al /var/lib/ /home
| /home:
| total 12
| drwxr-xr-x  3 root  root  4096 Aug 31 12:19 .
| drwxr-xr-x 17 root  root  4096 Dec  7  2023 ..
| drwx------  2 blubb blubb 4096 Aug 31 12:19 blubb
|
| /var/lib/:
| total 36
| drwxr-xr-x  8 root root 4096 Aug 31 12:18  .
| drwxr-xr-x 12 root root 4096 Apr 19  2024  ..
| drwxr-xr-x  5 root root 4096 Aug 31 12:17  apt
| drwxr-xr-x  7 root root 4096 Aug 31 12:17  dpkg
| drwxr-xr-x  2 root root 4096 Jun 11  2023  misc
| -rw-r--r--  1 root root    0 Aug 31 12:19 'not-utf-8-but-possible-'$'\177'
| drwxr-xr-x  2 root root 4096 Jun 30 08:01  pam
| drwxr-xr-x  2 root root 4096 Feb  5  2025  python
| -rw-r--r--  1 root root   84 Aug 14 18:28  shells.state
| drwxr-xr-x  3 root root 4096 Dec  7  2023  systemd
| root@salida-sid-buildd-amd64-08lb:/srv# deluser --remove-all-files blubb
| warn: Not backing up/removing `/lib64', it matches ^/lib.
| warn: Not backing up/removing `/lib', it matches ^/lib.
| warn: Not backing up/removing `/sbin', it matches ^/sbin$.
| warn: Not backing up/removing `/bin', it matches ^/bin$.
| warn: Not backing up/removing `/var', it matches ^/var$.
| warn: Not backing up/removing `/media', it matches ^/media$.
| warn: Not backing up/removing `/usr', it matches ^/usr$.
| warn: Not backing up/removing `/run', it matches ^/run$.
| warn: Not backing up/removing `/srv', it matches ^/srv$.
| warn: Not backing up/removing `/sys', it is a mount point.
| warn: Not backing up/removing `/opt', it matches ^/opt$.
| warn: Not backing up/removing `/mnt', it matches ^/mnt$.
| warn: Not backing up/removing `/tmp', it matches ^/tmp$.
| warn: Not backing up/removing `/proc', it is a mount point.
| warn: Not backing up/removing `/root', it matches ^/root$.
| warn: Not backing up/removing `/dev', it matches ^/dev$.
| warn: Not backing up/removing `/etc', it matches ^/etc$.
| warn: Not backing up/removing `/boot', it matches ^/boot$.
| warn: `/usr/bin/crontab' not executed. Skipping crontab removal. Package `cron' required.
| root@salida-sid-buildd-amd64-08lb:/srv# ls -al /var/lib/ /home
| /home:
| total 8
| drwxr-xr-x  2 root root 4096 Aug 31 12:19 .
| drwxr-xr-x 17 root root 4096 Dec  7  2023 ..
|
| /var/lib/:
| total 36
| drwxr-xr-x  8 root root 4096 Aug 31 12:18  .
| drwxr-xr-x 12 root root 4096 Apr 19  2024  ..
| drwxr-xr-x  5 root root 4096 Aug 31 12:17  apt
| drwxr-xr-x  7 root root 4096 Aug 31 12:17  dpkg
| drwxr-xr-x  2 root root 4096 Jun 11  2023  misc
| -rw-r--r--  1 root root    0 Aug 31 12:19 'not-utf-8-but-possible-'$'\177'
| drwxr-xr-x  2 root root 4096 Jun 30 08:01  pam
| drwxr-xr-x  2 root root 4096 Feb  5  2025  python
| -rw-r--r--  1 root root   84 Aug 14 18:28  shells.state
| drwxr-xr-x  3 root root 4096 Dec  7  2023  systemd
| root@salida-sid-buildd-amd64-08lb:/srv# dpkg --list adduser
| Desired=Unknown/Install/Remove/Purge/Hold
| | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
| |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
| ||/ Name           Version      Architecture Description
| +++-==============-============-============-=================================
| ii  adduser        3.152        all          add and remove users and groups
| root@salida-sid-buildd-amd64-08lb:/srv#

Can you give a better reproducer maybe?

So, the file in question does not even need to belong to the user being
deleted, and the issue stops the file system walk, leaving other files
undeleted?

Greetings
Marc

#1112486#30
Date:
2025-08-31 18:00:01 UTC
From:
To:
Marc Haber <mh+debian-packages@zugschlus.de> writes:

Hmm, wonder if it's because it's in /var, i.e.:

Otherwise, I'd be happy to try to come up with a more solid example.

Right, and still exits 0. I was trying to delete someuser and the file
was in /home/someotheruser/...

Thanks again

#1112486#35
Date:
2025-08-31 18:05:27 UTC
From:
To:
Rob Browning <rlb@defaultvalue.org> writes:

Oh, and as mentioned, I now suspect UTF-8 may have been irrelevant,
i.e. DEL (\177) is a perfectly fine UTF-8 encoding. I was thinking of
something like $'\xb5' (i.e. Latin-1 mu, which has the high bit set).

It looks like sanitze_string (in AdduserCommon.pm) may just be
intentionally rejecting the filename for some reason.

#1112486#40
Date:
2025-09-01 19:02:16 UTC
From:
To:
I was able to reproduce this, write a test case, and change the code so
that the deluser call now proceeds correctly. Files belonging to the
user that is being deleted AND have strange file names might stay aroud
now. I think that addressing this is much harder.

Would you want to review
https://salsa.debian.org/debian/adduser/-/merge_requests/107 ?

Greetings
Marc