Bug 236647

Summary: kldload can choose wrong module name for hardlinked modules
Product: Base System Reporter: Alan Somers <asomers>
Component: kernAssignee: Alan Somers <asomers>
Status: New ---    
Severity: Affects Many People CC: cem, gjb, rgrimes, sbruno
Priority: ---    
Version: CURRENT   
Hardware: Any   
OS: Any   

Description Alan Somers freebsd_committer 2019-03-19 21:28:19 UTC
When a module gets renamed, it's customary to hard link the new module name to the old one so newly upgraded systems can still boot before they've been reconfigured.  However, sometimes "kldload newname" will register the module with the old name, as shown below:

root@freebsd:/boot/kernel # ls -li fuse*.ko
2328482 -r-xr-xr-x  2 root  wheel  113928 Mar 19 20:00 fuse.ko
2328482 -r-xr-xr-x  2 root  wheel  113928 Mar 19 20:00 fusefs.ko
root@freebsd:/boot/kernel # kldload fusefs
fuse-freebsd: version 0.4.4, FUSE ABI 7.8
root@freebsd:/boot/kernel # kldstat
Id Refs Address                Size Name
 1    3 0xffffffff80200000  25140f8 kernel
 2    1 0xffffffff82811000     a270 fuse.ko

There are four separate functions that cause this bug.  Taken individually, each one of them behaves reasonably.  But the combination is problematic:

1) kldxref(8), which generates /boot/linker.hints, examines each file in turn and independently writes an entry for it into the hints file.  In this case, it legitimately writes a "fusefs" => "fuse.ko" entry.  It writes it before the "fusefs" -> "fusefs.ko" entry probably because fuse.ko sorts lexicographically before fusefs.ko.  This behavior is reasonable, because fuse.ko _does_ technically implement the fusefs module.

2) In the kernel, linker_hints_lookup chooses the first filename from the hints file that matches the desired module name.  This behavior is reasonable, because it would be slower to read the entire hints file, looking for the "best" matching filename.

3) linker_hints_lookup accepts filenames that don't exactly match module names.  This behavior is also reasonable, because a user might legitimately have multiple filenames implementing the same module, if they're at different versions or something.

4) linker_load_file registers the module by its filename, not its module name.  This might also be reasonable for the case described above where multiple files implement different versions of the same module.

What's the best solution?  I can think of two:
1) Change linker_load_file to register by module name, not filename.

2) Add special logic to kldxref for handling multiple-linked files.  Only generate one hint instead of many.
Comment 1 Conrad Meyer freebsd_committer 2019-03-19 22:51:49 UTC
Couldn't we just use symlinks instead of hardlinks?  There's no "right" name for a hardlink, and a .ko may contain many modules (none of which must match the .ko name).  With symlinks, the canonical name would be the VREG file.
Comment 2 Alan Somers freebsd_committer 2019-03-19 23:16:46 UTC
(In reply to Conrad Meyer from comment #1)
I've been told that the boot loader doesn't understand symlinks.
Comment 3 Conrad Meyer freebsd_committer 2019-03-19 23:32:00 UTC
Huh.  That might be true.  It seems like something that would be easy to fix, but yeah that's a good reason to continue to use hardlinks.

Still -- I'm not sure there's a good way to get the behavior you want with hardlinks, given everything else.
Comment 4 Alan Somers freebsd_committer 2019-03-19 23:37:42 UTC
Well, the default install only contains 3 pairs of hardlinked modules.  It shouldn't be impossible to come up with a solution that works for three things.  I could even do something as petty as sort those three pairs' old halves last within kldxref.
Comment 5 commit-hook freebsd_committer 2019-03-21 21:41:24 UTC
A commit references this bug:

Author: asomers
Date: Thu Mar 21 21:41:07 UTC 2019
New revision: 345386
URL: https://svnweb.freebsd.org/changeset/base/345386

Log:
  fusefs: don't check for the fusefs module during the tests

  It's sufficient to check for /dev/fuse.  And due to bug 236647, the module
  could be named either fuse or fusefs.

  PR:		236647
  Sponsored by:	The FreeBSD Foundation

Changes:
  projects/fuse2/tests/sys/fs/fusefs/utils.cc