From debbugs-submit-bounces@debbugs.gnu.org Tue Dec 22 11:10:31 2020 Received: (at submit) by debbugs.gnu.org; 22 Dec 2020 16:10:31 +0000 Received: from localhost ([127.0.0.1]:50785 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1krkF5-0002uO-Eq for submit@debbugs.gnu.org; Tue, 22 Dec 2020 11:10:31 -0500 Received: from lists.gnu.org ([209.51.188.17]:60068) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1krkF1-0002uA-Ce for submit@debbugs.gnu.org; Tue, 22 Dec 2020 11:10:30 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:45344) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1krkF1-0006PB-88 for bug-coreutils@gnu.org; Tue, 22 Dec 2020 11:10:27 -0500 Received: from relay4-d.mail.gandi.net ([217.70.183.196]:51067) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1krkEy-0001Vs-I7 for bug-coreutils@gnu.org; Tue, 22 Dec 2020 11:10:27 -0500 X-Originating-IP: 94.10.124.211 Received: from chazelas.org (unknown [94.10.124.211]) (Authenticated sender: stephane@chazelas.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id CE63EE0005 for ; Tue, 22 Dec 2020 16:10:18 +0000 (UTC) Date: Tue, 22 Dec 2020 16:10:18 +0000 From: Stephane Chazelas To: bug-coreutils@gnu.org Subject: race condition in rm --preserve-root=all Message-ID: <20201222161018.ndyu3f26goareden@chazelas.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Received-SPF: pass client-ip=217.70.183.196; envelope-from=stephane@chazelas.org; helo=relay4-d.mail.gandi.net X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: -1.6 (-) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.6 (--) Hello, [reproduced with rm 8.30 and current git head on ubuntu 20.04 amd64] Whilst trying to answer https://unix.stackexchange.com/questions/505317/using-rm-one-file-system-to-only-delete-files-on-the-local-filesystem I noticed that "rm -rf --preserve-root=all some-dir" was not race-free. "some-dir", as the root of a filesystem could still be removed recursively if the filesystem is mounted after "rm" has checked that "some-dir" was not a mountpoint but before "rm" opens that directory. That can be reproduced with gdb: $ mkdir -p 1 2/dir $ gdb --quiet --args rm -rf --preserve-root=all --one-file-system 1 Reading symbols from rm... (gdb) break __fxstatat Breakpoint 1 at 0x25e0 (gdb) break __lxstat Breakpoint 2 at 0x2570 (gdb) break unlinkat Breakpoint 3 at 0x24d0 (gdb) r Starting program: /home/chazelas/install/cvs/coreutils/INSTALL.d/bin/rm -rf --preserve-root=all --one-file-system 1 Breakpoint 2, __GI___lxstat (vers=1, name=0x55555556350c "/", buf=0x7fffffffd820) at ../sysdeps/unix/sysv/linux/wordsize-64/lxstat.c:33 33 ../sysdeps/unix/sysv/linux/wordsize-64/lxstat.c: No such file or directory. (gdb) c Continuing. Breakpoint 1, __GI___fxstatat (vers=1, fd=-100, file=0x5555555705b0 "1", st=0x555555570520, flag=256) at ../sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c:36 36 ../sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c: No such file or directory. (gdb) c Continuing. Breakpoint 2, __GI___lxstat (vers=1, name=0x555555570700 "1/..", buf=0x7fffffffd7b0) at ../sysdeps/unix/sysv/linux/wordsize-64/lxstat.c:33 33 ../sysdeps/unix/sysv/linux/wordsize-64/lxstat.c: No such file or directory. (gdb) fin Run till exit from #0 __GI___lxstat (vers=1, name=0x555555570700 "1/..", buf=0x7fffffffd7b0) at ../sysdeps/unix/sysv/linux/wordsize-64/lxstat.c:33 0x000055555555839e in rm_fts (fts=0x55555556f200, ent=0x5555555704b0, x=0x7fffffffd900) at src/remove.c:473 473 if (!parent || lstat (parent, &statbuf)) Value returned is $1 = 0 (gdb) !bindfs --no-allow-other 2 1 (gdb) !ls 1 dir (gdb) c Continuing. Breakpoint 1, __GI___fxstatat (vers=1, fd=4, file=0x5555555706c0 "dir", st=0x555555570630, flag=256) at ../sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c:36 36 ../sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c: No such file or directory. (gdb) c Continuing. Breakpoint 3, unlinkat () at ../sysdeps/unix/syscall-template.S:78 78 ../sysdeps/unix/syscall-template.S: No such file or directory. (gdb) bt #0 unlinkat () at ../sysdeps/unix/syscall-template.S:78 #1 0x0000555555557ddc in excise (fts=0x55555556f200, ent=0x5555555705c0, x=0x7fffffffd900, is_dir=true) at src/remove.c:370 #2 0x0000555555558547 in rm_fts (fts=0x55555556f200, ent=0x5555555705c0, x=0x7fffffffd900) at src/remove.c:508 #3 0x000055555555892d in rm (file=0x7fffffffda58, x=0x7fffffffd900) at src/remove.c:608 #4 0x000055555555749d in main (argc=5, argv=0x7fffffffda38) at src/rm.c:370 (gdb) bindfs is a fuse-based file system to mount one directory over another. 2 is mounted over 1 after rm has compared the result of stat("1") and stat("1/.."), and we see unlink("1/dir") being done. It seems to me that race window could be closed if "." and ".." were compared after the directory is opened. -- Stephane