On Mon, Aug 31, 2026 at 12:51:47PM +0200, Greg Kroah-Hartman wrote:
> The ability to add and remove devices from a driver through the sysfs
> "bind" and "unbind" files was created all those decades ago as a way
> that kernel developers can iterate faster, and provide a debugging way
> for users to attempt to add a new device to a driver without having to
> rebuild their kernel.
> 
> This api over the years has been abused and recently come under a major
> fuzzing "attack" through tools like syzbot which decided that it would
> attempt to just randomly bind any driver to any type of device, causing
> loads of unneeded errors and pointless kernel patches to be generated by
> unsuspecting new developers.
> 
> Handle all of this by adding a new taint flag, TAINT_FORCED_BIND, which
> will be set on the driver if the bind/unbind sysfs files are ever
> written to.  This lets kernel developers "know" that a user is
> attempting to do something that is not normal, and as such, if the
> kernel breaks they get to keep the shiny pieces laying around on the
> floor.
> 
> The flag is 'Y' which was unused, and can remembered as the user is
> "yeeting" the device being operated on here (thrown with force without
> regard for the thing being thrown).
> 
> Note, the taint flag gets set _BEFORE_ the bind/unbind callback happens,
> as many times crashes/oops/warnings/failures happen within the callback,
> and the taint flag needs to be there to show what was being attempted.
> If it were to be set after the callback happens, the oops report would
> not properly reflect what foolishness was being attempted.
> 
> Fuzzing tools like syzbot, that doesn't have hand-crafted rules to keep
> the tool from hitting bind/unbind, should be run with panic_on_taint
> enabled so that they fall over and don't continue on, thinking that they
> actually found a real issue.
> 
> Userspace operations that rely on the bind/unbind files to work around
> the lack of will to upgrade a kernel image to a newer version with
> proper support for new devices, or the lack of will to submit valid
> device ids to driver authors, will still work properly, but now the
> kernel will be flagged in a way that will show that perhaps those users
> should reconsider their behavior and work to have the drivers properly
> support these devices in a "native" manner.
> 
> Finally, the bind/unbind files can find real use-after-free issues with
> some drivers by forcing the process to happen virtually without having
> to rely on manual removal processes.  Those real bugs should still be
> worked on, but by adding this taint flag, developers can more easily
> determine bug reports that are actually worth looking at.
> 
> Reviewed-by: Johan Hovold <[email protected]>
> Tested-by: Johan Hovold <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
> ---
>  Documentation/admin-guide/tainted-kernels.rst | 52 
> ++++++++++++++-------------
>  drivers/base/bus.c                            |  3 ++
>  include/linux/panic.h                         |  3 +-
>  include/trace/events/module.h                 |  3 +-
>  kernel/panic.c                                |  5 +--
>  tools/debugging/kernel-chktaint               |  8 +++++
>  6 files changed, 46 insertions(+), 28 deletions(-)
> 
> diff --git a/Documentation/admin-guide/tainted-kernels.rst 
> b/Documentation/admin-guide/tainted-kernels.rst
> index 9ead927a37c0..abbf5e3dd749 100644
> --- a/Documentation/admin-guide/tainted-kernels.rst
> +++ b/Documentation/admin-guide/tainted-kernels.rst
> @@ -79,30 +79,31 @@ which bits are set::
>  Table for decoding tainted state
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>  
> -===  ===  ======  ========================================================
> -Bit  Log  Number  Reason that got the kernel tainted
> -===  ===  ======  ========================================================
> -  0  G/P       1  proprietary module was loaded
> -  1  _/F       2  module was force loaded
> -  2  _/S       4  kernel running on an out of specification system
> -  3  _/R       8  module was force unloaded
> -  4  _/M      16  processor reported a Machine Check Exception (MCE)
> -  5  _/B      32  bad page referenced or some unexpected page flags
> -  6  _/U      64  taint requested by userspace application
> -  7  _/D     128  kernel died recently, i.e. there was an OOPS or BUG
> -  8  _/A     256  ACPI table overridden by user
> -  9  _/W     512  kernel issued warning
> - 10  _/C    1024  staging driver was loaded
> - 11  _/I    2048  workaround for bug in platform firmware applied
> - 12  _/O    4096  externally-built ("out-of-tree") module was loaded
> - 13  _/E    8192  unsigned module was loaded
> - 14  _/L   16384  soft lockup occurred
> - 15  _/K   32768  kernel has been live patched
> - 16  _/X   65536  auxiliary taint, defined for and used by distros
> - 17  _/T  131072  kernel was built with the struct randomization plugin
> - 18  _/N  262144  an in-kernel test has been run
> - 19  _/J  524288  userspace used a mutating debug operation in fwctl
> -===  ===  ======  ========================================================
> +===  ===  =======  ========================================================
> +Bit  Log  Number   Reason that got the kernel tainted
> +===  ===  =======  ========================================================
> +  0  G/P        1  proprietary module was loaded
> +  1  _/F        2  module was force loaded
> +  2  _/S        4  kernel running on an out of specification system
> +  3  _/R        8  module was force unloaded
> +  4  _/M       16  processor reported a Machine Check Exception (MCE)
> +  5  _/B       32  bad page referenced or some unexpected page flags
> +  6  _/U       64  taint requested by userspace application
> +  7  _/D      128  kernel died recently, i.e. there was an OOPS or BUG
> +  8  _/A      256  ACPI table overridden by user
> +  9  _/W      512  kernel issued warning
> + 10  _/C     1024  staging driver was loaded
> + 11  _/I     2048  workaround for bug in platform firmware applied
> + 12  _/O     4096  externally-built ("out-of-tree") module was loaded
> + 13  _/E     8192  unsigned module was loaded
> + 14  _/L    16384  soft lockup occurred
> + 15  _/K    32768  kernel has been live patched
> + 16  _/X    65536  auxiliary taint, defined for and used by distros
> + 17  _/T   131072  kernel was built with the struct randomization plugin
> + 18  _/N   262144  an in-kernel test has been run
> + 19  _/J   524288  userspace used a mutating debug operation in fwctl
> + 20  _/Y  1048576  device was manually bound or unbound from a driver
> +===  ===  =======  ========================================================
>  
>  Note: The character ``_`` is representing a blank in this table to make 
> reading
>  easier.
> @@ -189,3 +190,6 @@ More detailed explanation for tainting
>   19) ``J`` if userspace opened /dev/fwctl/* and performed a 
> FWTCL_RPC_DEBUG_WRITE
>       to use the devices debugging features. Device debugging features could
>       cause the device to malfunction in undefined ways.
> +
> + 20) ``Y`` If userspace wrote to the `bind` or `unbind` sysfs files and
> +     successfully bound or removed a device from a driver.
> diff --git a/drivers/base/bus.c b/drivers/base/bus.c
> index d17bd91490ee..c51ad96d4de4 100644
> --- a/drivers/base/bus.c
> +++ b/drivers/base/bus.c
> @@ -19,6 +19,7 @@
>  #include <linux/string.h>
>  #include <linux/mutex.h>
>  #include <linux/sysfs.h>
> +#include <linux/panic.h>
>  #include "base.h"
>  #include "power/power.h"
>  
> @@ -241,6 +242,7 @@ static ssize_t unbind_store(struct device_driver *drv, 
> const char *buf,
>  
>       dev = bus_find_device_by_name(bus, NULL, buf);
>       if (dev && dev->driver == drv) {
> +             add_taint_module(drv->owner, TAINT_FORCED_BIND, 
> LOCKDEP_STILL_OK);
>               device_driver_detach(dev);
>               err = count;
>       }
> @@ -264,6 +266,7 @@ static ssize_t bind_store(struct device_driver *drv, 
> const char *buf,
>  
>       dev = bus_find_device_by_name(bus, NULL, buf);
>       if (dev && driver_match_device(drv, dev)) {
> +             add_taint_module(drv->owner, TAINT_FORCED_BIND, 
> LOCKDEP_STILL_OK);
>               err = device_driver_attach(drv, dev);
>               if (!err) {
>                       /* success */
> diff --git a/include/linux/panic.h b/include/linux/panic.h
> index f1dd417e54b2..8e7250b0e913 100644
> --- a/include/linux/panic.h
> +++ b/include/linux/panic.h
> @@ -88,7 +88,8 @@ static inline void set_arch_panic_timeout(int timeout, int 
> arch_default_timeout)
>  #define TAINT_RANDSTRUCT             17
>  #define TAINT_TEST                   18
>  #define TAINT_FWCTL                  19
> -#define TAINT_FLAGS_COUNT            20
> +#define TAINT_FORCED_BIND            20
> +#define TAINT_FLAGS_COUNT            21
>  #define TAINT_FLAGS_MAX                      ((1UL << TAINT_FLAGS_COUNT) - 1)
>  
>  struct taint_flag {
> diff --git a/include/trace/events/module.h b/include/trace/events/module.h
> index e5a006be9dc6..19df3e39bba4 100644
> --- a/include/trace/events/module.h
> +++ b/include/trace/events/module.h
> @@ -26,7 +26,8 @@ struct module;
>       { (1UL << TAINT_OOT_MODULE),            "O" },          \
>       { (1UL << TAINT_FORCED_MODULE),         "F" },          \
>       { (1UL << TAINT_CRAP),                  "C" },          \
> -     { (1UL << TAINT_UNSIGNED_MODULE),       "E" })
> +     { (1UL << TAINT_UNSIGNED_MODULE),       "E" },          \
> +     { (1UL << TAINT_FORCED_BIND),           "Y" })
>  
>  TRACE_EVENT(module_load,
>  
> diff --git a/kernel/panic.c b/kernel/panic.c
> index 213725b612aa..6bf60f9dd120 100644
> --- a/kernel/panic.c
> +++ b/kernel/panic.c
> @@ -826,6 +826,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
>       TAINT_FLAG(RANDSTRUCT,                  'T', ' '),
>       TAINT_FLAG(TEST,                        'N', ' '),
>       TAINT_FLAG(FWCTL,                       'J', ' '),
> +     TAINT_FLAG(FORCED_BIND,                 'Y', ' '),
>  };
>  
>  #undef TAINT_FLAG
> @@ -862,9 +863,9 @@ static void print_tainted_seq(struct seq_buf *s, bool 
> verbose)
>   * exact size is allocated dynamically; the initial buffer remains
>   * as a fallback if allocation fails.
>   *
> - * The verbose taint string currently requires up to 327 characters.
> + * The verbose taint string currently requires up to 344 characters.
>   */
> -#define INIT_TAINT_BUF_MAX 350
> +#define INIT_TAINT_BUF_MAX 370
>  
>  static char init_taint_buf[INIT_TAINT_BUF_MAX] __initdata;
>  static char *taint_buf __refdata = init_taint_buf;
> diff --git a/tools/debugging/kernel-chktaint b/tools/debugging/kernel-chktaint
> index e1571c04afb5..d8628be37214 100755
> --- a/tools/debugging/kernel-chktaint
> +++ b/tools/debugging/kernel-chktaint
> @@ -211,6 +211,14 @@ else
>       addout "J"
>       echo " * fwctl's mutating debug interface was used (#19)"
>  fi
> +
> +T=`expr $T / 2`
> +if [ `expr $T % 2` -eq 0 ]; then
> +     addout " "
> +else
> +     addout "Y"
> +     echo " * device was manually bound or unbound from a driver (#20)"
> +fi
>  echo "Raw taint value as int/string: $taint/'$out'"
>  
>  # report on any tainted loadable modules
> 
> -- 
> 2.55.0
> 

Hi Greg,

After you replace the accidental comma with a space in the parameter list
of add_taint_module() in module.h under !CONFIG_MODULES:

Reviewed-by: Aaron Tomlin <[email protected]>

Kind regards,
-- 
Aaron Tomlin

Reply via email to