mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCHES][CFT] rename deadlock fixes
@ 2023-11-22 19:30 Al Viro
  2023-11-22 19:36 ` [PATCH 1/9] reiserfs: Avoid touching renamed directory if parent does not change Al Viro
  2023-11-25 20:10 ` [PATCHES v2][CFT] rename deadlock fixes Al Viro
  0 siblings, 2 replies; 28+ messages in thread
From: Al Viro @ 2023-11-22 19:30 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Linus Torvalds, Mo Zou, Jan Kara, linux-kernel

	Directory locking used to provide the following warranties:
1.  Any read operations (lookup, readdir) are done with directory locked at
least shared.
2.  Any link creation or removal is done with directory locked exclusive.
3.  Any link count changes are done with the object locked exclusive.
4.  Any emptiness checks (for rmdir() or overwriting rename()) are done with
the victim locked exclusive.
5.  Any rename of a non-directory is done with the object locked exclusive
(the last part is needed by nfsd).

	As far as directory contents is concerned, it very nearly amounted
to "all reads are done with directory locked shared, all modifications
- exclusive".  There had been one gap in that, though - rename() can change
the parent of subdirectory and strictly speaking that does modify
the contents - ".." entry might need to be altered to match the new parent.
For almost all filesystems it posed no problem - location and representation
of ".." entry is fs-dependent, but it tends to be unaffected by any other
directory modifications.

	However, in some cases it's not true - for example, a filesystem
might have the contents of small directories kept directly in the
inode, switched to separate allocation when enough entries are added.
For such beasts we need an exclusion between modifying ".." and (at least)
switchover from small to large directory format.

	One solution would be an fs-private locking inside the method,
another - having cross-directory ->rename() take the normal lock on
directory being moved.

	Or one could make vfs_rename() itself lock that directory instead,
sparing the ->rename() instances all that headache.  That had been done in
6.5; unfortunately, locking the moved subdirectory had been done in *all*
cases, cross-directory or not.	And that turns out to be more than a bit
of harmless overlocking - deadlock prevention relies upon the fact that
we never lock two directories that are not descendents of each other
without holding ->s_vfs_rename_mutex.  Kudos to Mo Zou for pointing to
the holes in proof of correctness - that's what uncovered the problem...

	We could revert to pre-6.5 locking scheme, but there's a less
painful solution; the cause of problem is same-directory case and in those
there's no reason for ->rename() to touch the ".." entry at all - the
parent does not change, so the modification of ".." would be tautological.

	Let's keep locking moved subdirectory in cross-directory move;
that spares ->rename() instances the need to do home-grown exclusion.
They need to be careful in one respect - if they do rely upon the exclusion
between the change of ".." and other directory modifications, they should
only touch ".." if the parent does get changed.  Exclusion is still provided
by the caller for such (cross-directory) renames.

	The series lives in 
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.rename;
individual patches in followups.  It does surivive local beating, but
it needs more - additional review and testing would be very welcome.

	It starts with making sure that ->rename() instances are
careful.  Then the locking rules for rename get changed, so that we don't
lock moved subdirectory in same-directory case.  The proof of correctness
gets updated^Wfixed - the current one had several holes.

1/9..6/9) (me and Jan) don't do tautological ".." changes in instances.
      reiserfs: Avoid touching renamed directory if parent does not change
      ocfs2: Avoid touching renamed directory if parent does not change
      udf_rename(): only access the child content on cross-directory rename
      ext2: Avoid reading renamed directory if parent does not change
      ext4: don't access the source subdirectory content on same-directory rename
      f2fs: Avoid reading renamed directory if parent does not change

7/9) rename(): fix the locking of subdirectories

	We should never lock two subdirectories without having taken
->s_vfs_rename_mutex; inode pointer order or not, the "order" proposed
in 28eceeda130f "fs: Lock moved directories" is not transitive, with
the usual consequences.

	The rationale for locking renamed subdirectory in all cases was
the possibility of race between rename modifying .. in a subdirectory to
reflect the new parent and another thread modifying the same subdirectory.
For a lot of filesystems that's not a problem, but for some it can lead
to trouble (e.g. the case when short directory contents is kept in the
inode, but creating a file in it might push it across the size limit
and copy its contents into separate data block(s)).

	However, we need that only in case when the parent does change -
otherwise ->rename() doesn't need to do anything with .. entry in the
first place.  Some instances are lazy and do a tautological update anyway,
but it's really not hard to avoid.

Amended locking rules for rename():
	find the parent(s) of source and target
	if source and target have the same parent
		lock the common parent
	else
		lock ->s_vfs_rename_mutex
		lock both parents, in ancestor-first order; if neither
		is an ancestor of another, lock the parent of source
		first.
	find the source and target.
	if source and target have the same parent
		if operation is an overwriting rename of a subdirectory
			lock the target subdirectory
	else
		if source is a subdirectory
			lock the source
		if target is a subdirectory
			lock the target
	lock non-directories involved, in inode pointer order if both
	source and target are such.

That way we are guaranteed that parents are locked (for obvious reasons),
that any renamed non-directory is locked (nfsd relies upon that),
that any victim is locked (emptiness check needs that, among other things)
and subdirectory that changes parent is locked (needed to protect the update
of .. entries).  We are also guaranteed that any operation locking more
than one directory either takes ->s_vfs_rename_mutex or locks a parent
followed by its child.

8/9) kill lock_two_inodes()
	Folded into the sole caller and simplified - it doesn't
need to deal with the mix of directories and non-directories anymore.

9/9) rename(): avoid a deadlock in the case of parents having no common ancestor

... and fix the directory locking documentation and proof of correctness.
Holding ->s_vfs_rename_mutex *almost* prevents ->d_parent changes; the
case where we really don't want it is splicing the root of disconnected
tree to somewhere.

In other words, ->s_vfs_rename_mutex is sufficient to stabilize "X is an
ancestor of Y" only if X and Y are already in the same tree.  Otherwise
it can go from false to true, and one can construct a deadlock on that.

Make lock_two_directories() report an error in such case and update the
callers of lock_rename()/lock_rename_child() to handle such errors.
The ones that could get an error, that is - e.g. debugfs_rename() is
never asked to change the parent and shouldn't be using lock_rename()
in the first place; that's a separate series, though.

^ permalink raw reply	[flat|nested] 28+ messages in thread
* [RFC][PATCHES v2] checksum stuff
@ 2023-12-05  2:21 Al Viro
  2023-12-05  2:23 ` [PATCH v2 01/18] make net/checksum.h self-contained Al Viro
  0 siblings, 1 reply; 28+ messages in thread
From: Al Viro @ 2023-12-05  2:21 UTC (permalink / raw)
  To: linux-arch
  Cc: gus Gusenleitner Klaus, Al Viro, Thomas Gleixner, lkml,
	Ingo Molnar, bp, dave.hansen, x86, David S. Miller, dsahern,
	kuba, Paolo Abeni, Eric Dumazet

We need a way for csum_and_copy_{from,to}_user() to report faults.
The approach taken back in 2020 (avoid 0 as return value by starting
summing from ~0U, use 0 to report faults) had been broken; it does
yield the right value modulo 2^16-1, but the case when data is
entirely zero-filled is not handled right.  It almost works, since
for most of the codepaths we have a non-zero value added in
and there 0 is not different from anything divisible by 0xffff.
However, there are cases (ICMPv4 replies, for example) where we
are not guaranteed that.

In other words, we really need to have those primitives return 0
on filled-with-zeroes input.  So let's make them return a 64bit
value instead; we can do that cheaply (all supported architectures
do that via a couple of registers) and we can use that to report
faults without disturbing the 32bit csum.

New type: __wsum_fault.  64bit, returned by csum_and_copy_..._user().
Primitives:
        * CSUM_FAULT representing the fault
        * to_wsum_fault() folding __wsum value into that
        * from_wsum_fault() extracting __wsum value
        * wsum_is_fault() checking if it's a fault value

Representation depends upon the target.
        CSUM_FAULT: ~0ULL
        to_wsum_fault(v32): (u64)v32 for 64bit and 32bit l-e,
(u64)v32 << 32 for 32bit b-e.

Rationale: relationship between the calling conventions for returning 64bit
and those for returning 32bit values.  On 64bit architectures the same
register is used; on 32bit l-e the lower half of the value goes in the
same register that is used for returning 32bit values and the upper half
goes into additional register.  On 32bit b-e the opposite happens -
upper 32 bits go into the register used for returning 32bit values and
the lower 32 bits get stuffed into additional register.

So with this choice of representation we need minimal changes on the
asm side (zero an extra register in 32bit case, nothing in 64bit case),
and from_wsum_fault() is as cheap as it gets.

Sum calculation is back to "start from 0".

The rest of the series consists of cleaning up assorted asm/checksum.h.

Branch lives in
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.csum
Individual patches in followups.  Help with review and testing would
be very welcome.

Al Viro (18):
      make net/checksum.h self-contained
      get rid of asm/checksum.h includes outside of include/net/checksum.h and arch
      make net/checksum.h the sole user of asm/checksum.h
      Fix the csum_and_copy_..._user() idiocy
      bits missing from csum_and_copy_{from,to}_user() unexporting.
      consolidate csum_tcpudp_magic(), take default variant into net/checksum.h
      consolidate default ip_compute_csum()
      alpha: pull asm-generic/checksum.h
      mips: pull include of asm-generic/checksum.h out of #if
      nios2: pull asm-generic/checksum.h
      x86: merge csum_fold() for 32bit and 64bit
      x86: merge ip_fast_csum() for 32bit and 64bit
      x86: merge csum_tcpudp_nofold() for 32bit and 64bit
      amd64: saner handling of odd address in csum_partial()
      x86: optimized csum_add() is the same for 32bit and 64bit
      x86: lift the extern for csum_partial() into checksum.h
      x86_64: move csum_ipv6_magic() from csum-wrappers_64.c to csum-partial_64.c
      uml/x86: use normal x86 checksum.h

 arch/alpha/include/asm/asm-prototypes.h   |   2 +-
 arch/alpha/include/asm/checksum.h         |  68 ++----------
 arch/alpha/lib/csum_partial_copy.c        |  74 ++++++-------
 arch/arm/include/asm/checksum.h           |  27 +----
 arch/arm/kernel/armksyms.c                |   3 +-
 arch/arm/lib/csumpartialcopygeneric.S     |   3 +-
 arch/arm/lib/csumpartialcopyuser.S        |   8 +-
 arch/hexagon/include/asm/checksum.h       |   4 +-
 arch/hexagon/kernel/hexagon_ksyms.c       |   1 -
 arch/hexagon/lib/checksum.c               |   1 +
 arch/m68k/include/asm/checksum.h          |  24 +---
 arch/m68k/lib/checksum.c                  |   8 +-
 arch/microblaze/kernel/microblaze_ksyms.c |   2 +-
 arch/mips/include/asm/asm-prototypes.h    |   2 +-
 arch/mips/include/asm/checksum.h          |  32 ++----
 arch/mips/lib/csum_partial.S              |  12 +-
 arch/nios2/include/asm/checksum.h         |  13 +--
 arch/openrisc/kernel/or32_ksyms.c         |   2 +-
 arch/parisc/include/asm/checksum.h        |  21 ----
 arch/powerpc/include/asm/asm-prototypes.h |   2 +-
 arch/powerpc/include/asm/checksum.h       |  27 +----
 arch/powerpc/lib/checksum_32.S            |   6 +-
 arch/powerpc/lib/checksum_64.S            |   4 +-
 arch/powerpc/lib/checksum_wrappers.c      |  14 +--
 arch/s390/include/asm/checksum.h          |  18 ---
 arch/s390/kernel/ipl.c                    |   2 +-
 arch/s390/kernel/os_info.c                |   2 +-
 arch/sh/include/asm/checksum_32.h         |  32 +-----
 arch/sh/kernel/sh_ksyms_32.c              |   2 +-
 arch/sh/lib/checksum.S                    |   6 +-
 arch/sparc/include/asm/asm-prototypes.h   |   2 +-
 arch/sparc/include/asm/checksum_32.h      |  63 ++++++-----
 arch/sparc/include/asm/checksum_64.h      |  21 +---
 arch/sparc/lib/checksum_32.S              |   2 +-
 arch/sparc/lib/csum_copy.S                |   4 +-
 arch/sparc/lib/csum_copy_from_user.S      |   2 +-
 arch/sparc/lib/csum_copy_to_user.S        |   2 +-
 arch/x86/include/asm/asm-prototypes.h     |   2 +-
 arch/x86/include/asm/checksum.h           | 177 ++++++++++++++++++++++++++++++
 arch/x86/include/asm/checksum_32.h        | 141 ++----------------------
 arch/x86/include/asm/checksum_64.h        | 172 +----------------------------
 arch/x86/lib/checksum_32.S                |  20 +++-
 arch/x86/lib/csum-copy_64.S               |   6 +-
 arch/x86/lib/csum-partial_64.c            |  41 ++++---
 arch/x86/lib/csum-wrappers_64.c           |  43 ++------
 arch/x86/um/asm/checksum.h                | 119 --------------------
 arch/x86/um/asm/checksum_32.h             |  38 -------
 arch/x86/um/asm/checksum_64.h             |  19 ----
 arch/xtensa/include/asm/asm-prototypes.h  |   2 +-
 arch/xtensa/include/asm/checksum.h        |  33 +-----
 arch/xtensa/lib/checksum.S                |   6 +-
 drivers/net/ethernet/brocade/bna/bnad.h   |   2 -
 drivers/net/ethernet/lantiq_etop.c        |   2 -
 drivers/net/vmxnet3/vmxnet3_int.h         |   1 -
 drivers/s390/char/zcore.c                 |   2 +-
 include/asm-generic/checksum.h            |  15 +--
 include/net/checksum.h                    |  81 ++++++++++++--
 include/net/ip6_checksum.h                |   1 -
 lib/checksum_kunit.c                      |   2 +-
 net/core/datagram.c                       |   8 +-
 net/core/skbuff.c                         |   8 +-
 net/ipv6/ip6_checksum.c                   |   1 -
 62 files changed, 501 insertions(+), 959 deletions(-)

Part 1: sorting out the includes.
	We have asm/checksum.h and net/checksum.h; the latter pulls
the former.  A lot of things would become easier if we could move
the things from asm/checksum.h to net/checksum.h; for that we need to
make net/checksum.h the only file that pulls asm/checksum.h.

1/18) make net/checksum.h self-contained
right now it has an implicit dependency upon linux/bitops.h (for the
sake of ror32()).

2/18) get rid of asm/checksum.h includes outside of include/net/checksum.h and arch
In almost all cases include is redundant; zcore.c and checksum_kunit.c
are the sole exceptions and those got switched to net/checksum.h

3/18) make net/checksum.h the sole user of asm/checksum.h
All other users (all in arch/* by now) can pull net/checksum.h.

Part 2: fix the fault reporting.

4/18) Fix the csum_and_copy_..._user() idiocy

Fix the breakage introduced back in 2020 - see above for details.

Part 3: trimming related crap

5/18) bits missing from csum_and_copy_{from,to}_user() unexporting.
6/18) consolidate csum_tcpudp_magic(), take default variant into net/checksum.h
7/18) consolidate default ip_compute_csum()
... and take it into include/net/checksum.h
8/18) alpha: pull asm-generic/checksum.h
9/18) mips: pull include of asm-generic/checksum.h out of #if
10/18) nios2: pull asm-generic/checksum.h

Part 4: trimming x86 crap
11/18) x86: merge csum_fold() for 32bit and 64bit
identical...
12/18) x86: merge ip_fast_csum() for 32bit and 64bit
Identical, except that 32bit version uses asm volatile where 64bit
one uses plain asm.  The former had become pointless when memory
clobber got added to both versions...
13/18) x86: merge csum_tcpudp_nofold() for 32bit and 64bit
identical...
14/18) amd64: saner handling of odd address in csum_partial()
all we want there is to have return value congruent to result * 256
modulo 0xffff; no need to convert from 32bit to 16bit (i.e. take it
modulo 0xffff) first - cyclic shift of 32bit value by 8 bits (in either
direction) will work.
Kills the from32to16() helper and yields better code...
15/18) x86: optimized csum_add() is the same for 32bit and 64bit
16/18) x86: lift the extern for csum_partial() into checksum.h
17/18) x86_64: move csum_ipv6_magic() from csum-wrappers_64.c to csum-partial_64.c
... and make uml/amd64 use it.
18/18) uml/x86: use normal x86 checksum.h
The only difference left is that UML really does *NOT* want the
csum-and-uaccess combinations; leave those in 
arch/x86/include/asm/checksum_{32,64}, move the rest into
arch/x86/include/asm/checksum.h (under ifdefs) and that's 
pretty much it.


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2023-12-05  2:25 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-22 19:30 [PATCHES][CFT] rename deadlock fixes Al Viro
2023-11-22 19:36 ` [PATCH 1/9] reiserfs: Avoid touching renamed directory if parent does not change Al Viro
2023-11-22 19:36   ` [PATCH 2/9] ocfs2: " Al Viro
2023-11-22 19:36   ` [PATCH 3/9] udf_rename(): only access the child content on cross-directory rename Al Viro
2023-11-23  9:22     ` Jan Kara
2023-11-22 19:36   ` [PATCH 4/9] ext2: Avoid reading renamed directory if parent does not change Al Viro
2023-11-22 19:36   ` [PATCH 5/9] ext4: don't access the source subdirectory content on same-directory rename Al Viro
2023-11-23  9:31     ` Jan Kara
2023-11-22 19:36   ` [PATCH 6/9] f2fs: Avoid reading renamed directory if parent does not change Al Viro
2023-11-22 19:36   ` [PATCH 7/9] rename(): fix the locking of subdirectories Al Viro
2023-11-23  9:50     ` Jan Kara
2023-11-22 19:36   ` [PATCH 8/9] kill lock_two_inodes() Al Viro
2023-11-23  9:53     ` Jan Kara
2023-11-22 19:36   ` [PATCH 9/9] rename(): avoid a deadlock in the case of parents having no common ancestor Al Viro
2023-11-23 10:36     ` Jan Kara
2023-11-24  6:15       ` Al Viro
2023-11-24  7:24     ` Amir Goldstein
2023-11-25 20:10 ` [PATCHES v2][CFT] rename deadlock fixes Al Viro
2023-11-25 20:11   ` [PATCH v2 1/9] reiserfs: Avoid touching renamed directory if parent does not change Al Viro
2023-11-25 20:11     ` [PATCH v2 2/9] ocfs2: " Al Viro
2023-11-25 20:11     ` [PATCH v2 3/9] udf_rename(): only access the child content on cross-directory rename Al Viro
2023-11-25 20:11     ` [PATCH v2 4/9] ext2: Avoid reading renamed directory if parent does not change Al Viro
2023-11-25 20:11     ` [PATCH v2 5/9] ext4: don't access the source subdirectory content on same-directory rename Al Viro
2023-11-25 20:11     ` [PATCH v2 6/9] f2fs: Avoid reading renamed directory if parent does not change Al Viro
2023-11-25 20:11     ` [PATCH v2 7/9] rename(): fix the locking of subdirectories Al Viro
2023-11-25 20:11     ` [PATCH v2 8/9] kill lock_two_inodes() Al Viro
2023-11-25 20:11     ` [PATCH v2 9/9] rename(): avoid a deadlock in the case of parents having no common ancestor Al Viro
2023-12-05  2:21 [RFC][PATCHES v2] checksum stuff Al Viro
2023-12-05  2:23 ` [PATCH v2 01/18] make net/checksum.h self-contained Al Viro
2023-12-05  2:23   ` [PATCH v2 2/9] ocfs2: Avoid touching renamed directory if parent does not change Al Viro

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®