Version in base suite: 0.8.5-2 Base version: multipath-tools_0.8.5-2 Target version: multipath-tools_0.8.5-2+deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/m/multipath-tools/multipath-tools_0.8.5-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/m/multipath-tools/multipath-tools_0.8.5-2+deb11u1.dsc .gitlab-ci.yml | 8 NEWS | 11 changelog | 13 clean | 1 multipath-tools.init | 1 multipath-tools.install | 1 patches/CVE-2022-41973.patch | 187 +++++ patches/CVE-2022-41974-commit1.patch | 1107 +++++++++++++++++++++++++++++++++++ patches/CVE-2022-41974-commit2.patch | 385 ++++++++++++ patches/series | 5 rules | 10 11 files changed, 1725 insertions(+), 4 deletions(-) diff -Nru multipath-tools-0.8.5/debian/.gitlab-ci.yml multipath-tools-0.8.5/debian/.gitlab-ci.yml --- multipath-tools-0.8.5/debian/.gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ multipath-tools-0.8.5/debian/.gitlab-ci.yml 2022-12-27 09:16:08.000000000 +0000 @@ -0,0 +1,8 @@ +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +variables: + RELEASE: 'bullseye' + SALSA_CI_COMPONENTS: 'main contrib non-free' + SALSA_CI_DISABLE_REPROTEST: 1 + SALSA_CI_DISABLE_LINTIAN: 1 diff -Nru multipath-tools-0.8.5/debian/NEWS multipath-tools-0.8.5/debian/NEWS --- multipath-tools-0.8.5/debian/NEWS 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/NEWS 2023-02-28 13:59:15.000000000 +0000 @@ -1,3 +1,14 @@ +multipath-tools (0.8.5-2+deb11u1) bullseye-security; urgency=high + + This uploaded fixes CVE-2022-41973: + The fix involves switching from /dev/shm to systemd-tmpfiles. + The tmpfs is mounted to /run/multipath. + + If you have previously accessed /dev/shm directly in your setup, + please update to the new path to facilitate this change. + + -- Tobias Frost Tue, 27 Dec 2022 09:46:24 +0100 + multipath-tools (0.7.7-1) unstable; urgency=medium systemd support is well integrated for multipath-tools now. The old diff -Nru multipath-tools-0.8.5/debian/changelog multipath-tools-0.8.5/debian/changelog --- multipath-tools-0.8.5/debian/changelog 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/changelog 2023-02-28 13:59:15.000000000 +0000 @@ -1,3 +1,16 @@ +multipath-tools (0.8.5-2+deb11u1) bullseye-security; urgency=high + + * Non-maintainer upload by the Security Team. + * Backport patch for CVE-2022-41974 and CVE-2022-41973. (Closes: #1022742) + - multipath.rules is now rebuilt from multipath.rules.in, superceding + 0010-multipath.rules-do-not-assume-usrmerged-paths.patch. + - to rebuild multipath.rules reliably: + - Reorder d/rules so it is built the file is copied + - Remove the generated multipath.rules in d/clean + - Remove also the patch that would have patched the output file + + -- Tobias Frost Tue, 28 Feb 2023 14:59:15 +0100 + multipath-tools (0.8.5-2) unstable; urgency=medium * [373f5c5] Fix bashism in script kpartx/kpartx_id. diff -Nru multipath-tools-0.8.5/debian/clean multipath-tools-0.8.5/debian/clean --- multipath-tools-0.8.5/debian/clean 1970-01-01 00:00:00.000000000 +0000 +++ multipath-tools-0.8.5/debian/clean 2023-02-01 20:45:55.000000000 +0000 @@ -0,0 +1 @@ +multipath/multipath.rules diff -Nru multipath-tools-0.8.5/debian/multipath-tools.init multipath-tools-0.8.5/debian/multipath-tools.init --- multipath-tools-0.8.5/debian/multipath-tools.init 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/multipath-tools.init 2023-02-28 13:51:31.000000000 +0000 @@ -59,6 +59,7 @@ start) log_daemon_msg "Starting $DESC" "$NAME" modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath 2> /dev/null || true + mkdir -m 0700 -p /run/multipath/ start-stop-daemon --oknodo --start --quiet --pidfile /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS log_end_msg $? ;; diff -Nru multipath-tools-0.8.5/debian/multipath-tools.install multipath-tools-0.8.5/debian/multipath-tools.install --- multipath-tools-0.8.5/debian/multipath-tools.install 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/multipath-tools.install 2022-12-27 09:16:08.000000000 +0000 @@ -14,3 +14,4 @@ /usr/include/libdmmp/libdmmp.h /lib/systemd/system/multipathd.service /lib/systemd/system/multipathd.socket +/lib/tmpfiles.d/multipath.conf /usr/lib/tmpfiles.d/ diff -Nru multipath-tools-0.8.5/debian/patches/CVE-2022-41973.patch multipath-tools-0.8.5/debian/patches/CVE-2022-41973.patch --- multipath-tools-0.8.5/debian/patches/CVE-2022-41973.patch 1970-01-01 00:00:00.000000000 +0000 +++ multipath-tools-0.8.5/debian/patches/CVE-2022-41973.patch 2023-02-01 20:44:42.000000000 +0000 @@ -0,0 +1,187 @@ +Description: CVE-2022-41973 use /run instead of /dev/shm + /dev/shm may have unsafe permissions. Use /run instead. + Use systemd's tmpfiles.d mechanism to create /run/multipath + early during boot. +Origin: https://github.com/opensvc/multipath-tools/commit/cb57b930fa690ab79b3904846634681685e3470f +Bug-Debian: https://bugs.debian.org/1022742 +Forwarded: not needed +Applied-Upstream: https://github.com/opensvc/multipath-tools/commit/c4912a639b7ff527aa11d665944594926ff94a7a +Last-Update: 2022-12-23 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -57,6 +57,7 @@ + usr_prefix = $(prefix) + bindir = $(exec_prefix)/sbin + libudevdir = $(prefix)/$(SYSTEMDPATH)/udev ++tmpfilesdir = $(prefix)/$(SYSTEMDPATH)/tmpfiles.d + udevrulesdir = $(libudevdir)/rules.d + multipathdir = $(TOPDIR)/libmultipath + man8dir = $(prefix)/usr/share/man/man8 +@@ -73,6 +74,7 @@ + nvmedir = $(TOPDIR)/libmultipath/nvme + includedir = $(prefix)/usr/include + pkgconfdir = $(usrlibdir)/pkgconfig ++runtimedir = /$(RUN) + + GZIP = gzip -9 -c + RM = rm -f +@@ -100,10 +102,11 @@ + WARNFLAGS := -Wall -Wextra -Wformat=2 -Werror=implicit-int \ + -Werror=implicit-function-declaration -Werror=format-security \ + $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) +-CPPFLAGS := $(shell dpkg-buildflags --get CPPFLAGS) -Wp,-D_FORTIFY_SOURCE=2 ++CPPFLAGS := $(shell dpkg-buildflags --get CPPFLAGS) -Wp,-D_FORTIFY_SOURCE=2 \ ++ -DRUNTIME_DIR=\"$(runtimedir)\" + CFLAGS := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \ + -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \ +- -MMD -MP ++ -MMD -MP $(CPPFLAGS) + BIN_CFLAGS = -fPIE -DPIE + LIB_CFLAGS = -fPIC + SHARED_FLAGS = -shared +--- a/multipath/Makefile ++++ b/multipath/Makefile +@@ -12,7 +12,7 @@ + + OBJS = main.o + +-all: $(EXEC) ++all: $(EXEC) multipath.rules tmpfiles.conf + + $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so + $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS) +@@ -24,7 +24,9 @@ + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir) + $(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir) +- $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules ++ $(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules ++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(tmpfilesdir) ++ $(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) +@@ -38,9 +40,13 @@ + $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz + + clean: dep_clean +- $(RM) core *.o $(EXEC) *.gz ++ $(RM) core *.o $(EXEC) *.gz multipath.rules tmpfiles.conf + + include $(wildcard $(OBJS:.o=.d)) + + dep_clean: + $(RM) $(OBJS:.o=.d) ++ ++%: %.in ++ sed 's,@RUNTIME_DIR@,$(runtimedir),' $< >$@ ++ +--- /dev/null ++++ b/multipath/multipath.rules.in +@@ -0,0 +1,91 @@ ++# Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath ++SUBSYSTEM!="block", GOTO="end_mpath" ++KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath" ++ACTION=="remove", TEST=="@RUNTIME_DIR@/multipath/find_multipaths/$major:$minor", \ ++ RUN+="/bin/rm -f @RUNTIME_DIR@/multipath/find_multipaths/$major:$minor" ++ACTION!="add|change", GOTO="end_mpath" ++ ++IMPORT{cmdline}="nompath" ++ENV{nompath}=="?*", GOTO="end_mpath" ++IMPORT{cmdline}="multipath" ++ENV{multipath}=="off", GOTO="end_mpath" ++ ++ENV{DEVTYPE}!="partition", GOTO="test_dev" ++IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH" ++ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \ ++ ENV{SYSTEMD_READY}="0" ++GOTO="end_mpath" ++ ++LABEL="test_dev" ++ ++ENV{MPATH_SBIN_PATH}="/sbin" ++TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" ++ ++# FIND_MULTIPATHS_WAIT_UNTIL is the timeout (in seconds after the ++# epoch). ++IMPORT{db}="FIND_MULTIPATHS_WAIT_UNTIL" ++ENV{.SAVED_FM_WAIT_UNTIL}="$env{FIND_MULTIPATHS_WAIT_UNTIL}" ++ ++# multipath -u needs to know if this device has ever been exported ++IMPORT{db}="DM_MULTIPATH_DEVICE_PATH" ++ ++# multipath -u sets DM_MULTIPATH_DEVICE_PATH and, ++# if "find_multipaths smart", also FIND_MULTIPATHS_WAIT_UNTIL. ++IMPORT{program}="$env{MPATH_SBIN_PATH}/multipath -u %k" ++ ++# case 1: this is definitely multipath ++ENV{DM_MULTIPATH_DEVICE_PATH}=="1", \ ++ ENV{ID_FS_TYPE}="mpath_member", ENV{SYSTEMD_READY}="0", \ ++ GOTO="stop_wait" ++ ++# case 2: this is definitely not multipath, or timeout has expired ++ENV{DM_MULTIPATH_DEVICE_PATH}!="2", \ ++ GOTO="stop_wait" ++ ++# Code below here is only run in "smart" mode. ++# multipath -u has indicated this is "maybe" multipath. ++ ++# Note that DM_MULTIPATH_DEVICE_PATH has the value 2 at this point. ++# This value will never propagate to other rules files, because ++# it will be reset to 1 in the "pretend_multipath" section below. ++ ++# This shouldn't happen, just in case. ++ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="?*", GOTO="end_mpath" ++ ++# Be careful not to start the timer twice. ++ACTION!="add", GOTO="pretend_mpath" ++ENV{.SAVED_FM_WAIT_UNTIL}=="?*", GOTO="pretend_mpath" ++ ++# At this point, we are seeing this path for the first time, and it's "maybe" multipath. ++ ++# The actual start command for the timer. ++# ++# The purpose of this command is only to make sure we will receive another ++# uevent eventually. *Any* uevent may cause waiting to finish if it either ends ++# in case 1-3 above, or if it arrives after FIND_MULTIPATHS_WAIT_UNTIL. ++# ++# Note that this will try to activate multipathd if it isn't running yet. ++# If that fails, the unit starts and expires nonetheless. If multipathd ++# startup needs to wait for other services, this wait time will add up with ++# the --on-active timeout. ++# ++# We must trigger an "add" event because LVM2 will only act on those. ++ ++RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property Conflicts=initrd-cleanup.service --timer-property Before=initrd-cleanup.service --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Conflicts=initrd-cleanup.service --property Before=initrd-cleanup.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /bin/udevadm trigger --action=add $sys$devpath" ++ ++LABEL="pretend_mpath" ++ENV{DM_MULTIPATH_DEVICE_PATH}="1" ++ENV{SYSTEMD_READY}="0" ++GOTO="end_mpath" ++ ++LABEL="stop_wait" ++# If timeout hasn't expired but we're not in "maybe" state any more, stop timer ++# Do this only once, and only if the timer has been started before ++IMPORT{db}="FIND_MULTIPATHS_WAIT_CANCELLED" ++ENV{FIND_MULTIPATHS_WAIT_CANCELLED}!="?*", \ ++ ENV{FIND_MULTIPATHS_WAIT_UNTIL}=="?*", \ ++ ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="0", \ ++ ENV{FIND_MULTIPATHS_WAIT_CANCELLED}="1", \ ++ RUN+="/bin/systemctl stop cancel-multipath-wait-$kernel.timer" ++ ++LABEL="end_mpath" +--- /dev/null ++++ b/multipath/tmpfiles.conf.in +@@ -0,0 +1 @@ ++d @RUNTIME_DIR@/multipath 0700 root root - +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -67,7 +67,7 @@ + #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" + #define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys" + #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" +-#define MULTIPATH_SHM_BASE "/dev/shm/multipath/" ++#define MULTIPATH_SHM_BASE RUNTIME_DIR "/multipath/" + + + static inline char *set_default(char *str) diff -Nru multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit1.patch multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit1.patch --- multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit1.patch 1970-01-01 00:00:00.000000000 +0000 +++ multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit1.patch 2022-12-27 08:48:45.000000000 +0000 @@ -0,0 +1,1107 @@ +Description: CVE-2022-41974 part 1 + Backport upstream commit to address CVE: + The current parser allows commands like "path sda show" or "list list + add path sda", because it doesn't enforce ordering of the keywords + and simply adds rather than or-s the values of the keys. + . + Rework this by using a separate byte for the "verb" and the allowed + qualifiers in the different positions of the command. No command + needs more than 3 qualifiers, so we disallow command vectors with + more than 4 elements, and change the type of the fingerprint to + uint32_t. + . + init_callbacks() is now required in both multipathc and multipathd + to initialize the table of handlers and fingerprints. But as m + ultipathc never calls any handler, we need not link the + entire handlers code into it. +Origin: https://github.com/opensvc/multipath-tools/commit/f812466f68b8e020818c6454d7b7a7e278bc99f6 +Bug-Debian: https://bugs.debian.org/1022742 +Forwarded: not needed +Applied-Upstream: https://github.com/opensvc/multipath-tools/commit/c4912a639b7ff527aa11d665944594926ff94a7a +Last-Update: 2022-12-23 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -17,6 +17,9 @@ + #include "cli.h" + #include "debug.h" + ++/* See KEY_INVALID in cli.h */ ++#define INVALID_FINGERPRINT ((uint32_t)(0)) ++ + static vector keys; + static vector handlers; + +@@ -33,7 +36,7 @@ + } + + static int +-add_key (vector vec, char * str, uint64_t code, int has_param) ++add_key (vector vec, char * str, uint32_t code, int has_param) + { + struct key * kw; + +@@ -64,7 +67,7 @@ + } + + int +-add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)) ++add_handler (uint32_t fp, int (*fn)(void *, char **, int *, void *)) + { + struct handler * h; + +@@ -86,11 +89,14 @@ + } + + static struct handler * +-find_handler (uint64_t fp) ++find_handler (uint32_t fp) + { + int i; + struct handler *h; + ++ if (fp == INVALID_FINGERPRINT) ++ return NULL; ++ + vector_foreach_slot (handlers, h, i) + if (h->fingerprint == fp) + return h; +@@ -99,10 +105,12 @@ + } + + int +-set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)) ++set_handler_callback (uint32_t fp, int (*fn)(void *, char **, int *, void *)) + { + struct handler * h = find_handler(fp); + ++ assert(fp != INVALID_FINGERPRINT); ++ + if (!h) + return 1; + h->fn = fn; +@@ -111,10 +119,12 @@ + } + + int +-set_unlocked_handler_callback (uint64_t fp,int (*fn)(void *, char **, int *, void *)) ++set_unlocked_handler_callback (uint32_t fp,int (*fn)(void *, char **, int *, void *)) + { + struct handler * h = find_handler(fp); + ++ assert(fp != INVALID_FINGERPRINT); ++ + if (!h) + return 1; + h->fn = fn; +@@ -168,56 +178,55 @@ + if (!keys) + return 1; + +- r += add_key(keys, "list", LIST, 0); +- r += add_key(keys, "show", LIST, 0); +- r += add_key(keys, "add", ADD, 0); +- r += add_key(keys, "remove", DEL, 0); +- r += add_key(keys, "del", DEL, 0); +- r += add_key(keys, "switch", SWITCH, 0); +- r += add_key(keys, "switchgroup", SWITCH, 0); +- r += add_key(keys, "suspend", SUSPEND, 0); +- r += add_key(keys, "resume", RESUME, 0); +- r += add_key(keys, "reinstate", REINSTATE, 0); +- r += add_key(keys, "fail", FAIL, 0); +- r += add_key(keys, "resize", RESIZE, 0); +- r += add_key(keys, "reset", RESET, 0); +- r += add_key(keys, "reload", RELOAD, 0); +- r += add_key(keys, "forcequeueing", FORCEQ, 0); +- r += add_key(keys, "disablequeueing", DISABLEQ, 0); +- r += add_key(keys, "restorequeueing", RESTOREQ, 0); +- r += add_key(keys, "paths", PATHS, 0); +- r += add_key(keys, "maps", MAPS, 0); +- r += add_key(keys, "multipaths", MAPS, 0); +- r += add_key(keys, "path", PATH, 1); +- r += add_key(keys, "map", MAP, 1); +- r += add_key(keys, "multipath", MAP, 1); +- r += add_key(keys, "group", GROUP, 1); +- r += add_key(keys, "reconfigure", RECONFIGURE, 0); +- r += add_key(keys, "daemon", DAEMON, 0); +- r += add_key(keys, "status", STATUS, 0); +- r += add_key(keys, "stats", STATS, 0); +- r += add_key(keys, "topology", TOPOLOGY, 0); +- r += add_key(keys, "config", CONFIG, 0); +- r += add_key(keys, "blacklist", BLACKLIST, 0); +- r += add_key(keys, "devices", DEVICES, 0); +- r += add_key(keys, "raw", RAW, 0); +- r += add_key(keys, "wildcards", WILDCARDS, 0); +- r += add_key(keys, "quit", QUIT, 0); +- r += add_key(keys, "exit", QUIT, 0); +- r += add_key(keys, "shutdown", SHUTDOWN, 0); +- r += add_key(keys, "getprstatus", GETPRSTATUS, 0); +- r += add_key(keys, "setprstatus", SETPRSTATUS, 0); +- r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0); +- r += add_key(keys, "format", FMT, 1); +- r += add_key(keys, "json", JSON, 0); +- r += add_key(keys, "getprkey", GETPRKEY, 0); +- r += add_key(keys, "setprkey", SETPRKEY, 0); +- r += add_key(keys, "unsetprkey", UNSETPRKEY, 0); +- r += add_key(keys, "key", KEY, 1); +- r += add_key(keys, "local", LOCAL, 0); +- r += add_key(keys, "setmarginal", SETMARGINAL, 0); +- r += add_key(keys, "unsetmarginal", UNSETMARGINAL, 0); +- ++ r += add_key(keys, "list", VRB_LIST, 0); ++ r += add_key(keys, "show", VRB_LIST, 0); ++ r += add_key(keys, "add", VRB_ADD, 0); ++ r += add_key(keys, "remove", VRB_DEL, 0); ++ r += add_key(keys, "del", VRB_DEL, 0); ++ r += add_key(keys, "switch", VRB_SWITCH, 0); ++ r += add_key(keys, "switchgroup", VRB_SWITCH, 0); ++ r += add_key(keys, "suspend", VRB_SUSPEND, 0); ++ r += add_key(keys, "resume", VRB_RESUME, 0); ++ r += add_key(keys, "reinstate", VRB_REINSTATE, 0); ++ r += add_key(keys, "fail", VRB_FAIL, 0); ++ r += add_key(keys, "resize", VRB_RESIZE, 0); ++ r += add_key(keys, "reset", VRB_RESET, 0); ++ r += add_key(keys, "reload", VRB_RELOAD, 0); ++ r += add_key(keys, "forcequeueing", VRB_FORCEQ, 0); ++ r += add_key(keys, "disablequeueing", VRB_DISABLEQ, 0); ++ r += add_key(keys, "restorequeueing", VRB_RESTOREQ, 0); ++ r += add_key(keys, "paths", KEY_PATHS, 0); ++ r += add_key(keys, "maps", KEY_MAPS, 0); ++ r += add_key(keys, "multipaths", KEY_MAPS, 0); ++ r += add_key(keys, "path", KEY_PATH, 1); ++ r += add_key(keys, "map", KEY_MAP, 1); ++ r += add_key(keys, "multipath", KEY_MAP, 1); ++ r += add_key(keys, "group", KEY_GROUP, 1); ++ r += add_key(keys, "reconfigure", VRB_RECONFIGURE, 0); ++ r += add_key(keys, "daemon", KEY_DAEMON, 0); ++ r += add_key(keys, "status", KEY_STATUS, 0); ++ r += add_key(keys, "stats", KEY_STATS, 0); ++ r += add_key(keys, "topology", KEY_TOPOLOGY, 0); ++ r += add_key(keys, "config", KEY_CONFIG, 0); ++ r += add_key(keys, "blacklist", KEY_BLACKLIST, 0); ++ r += add_key(keys, "devices", KEY_DEVICES, 0); ++ r += add_key(keys, "raw", KEY_RAW, 0); ++ r += add_key(keys, "wildcards", KEY_WILDCARDS, 0); ++ r += add_key(keys, "quit", VRB_QUIT, 0); ++ r += add_key(keys, "exit", VRB_QUIT, 0); ++ r += add_key(keys, "shutdown", VRB_SHUTDOWN, 0); ++ r += add_key(keys, "getprstatus", VRB_GETPRSTATUS, 0); ++ r += add_key(keys, "setprstatus", VRB_SETPRSTATUS, 0); ++ r += add_key(keys, "unsetprstatus", VRB_UNSETPRSTATUS, 0); ++ r += add_key(keys, "format", KEY_FMT, 1); ++ r += add_key(keys, "json", KEY_JSON, 0); ++ r += add_key(keys, "getprkey", VRB_GETPRKEY, 0); ++ r += add_key(keys, "setprkey", VRB_SETPRKEY, 0); ++ r += add_key(keys, "unsetprkey", VRB_UNSETPRKEY, 0); ++ r += add_key(keys, "key", KEY_KEY, 1); ++ r += add_key(keys, "local", KEY_LOCAL, 0); ++ r += add_key(keys, "setmarginal", VRB_SETMARGINAL, 0); ++ r += add_key(keys, "unsetmarginal", VRB_UNSETMARGINAL, 0); + + if (r) { + free_keys(keys); +@@ -326,19 +335,21 @@ + return r; + } + +-static uint64_t ++static uint32_t + fingerprint(vector vec) + { + int i; +- uint64_t fp = 0; ++ uint32_t fp = 0; + struct key * kw; + +- if (!vec) +- return 0; +- +- vector_foreach_slot(vec, kw, i) +- fp += kw->code; ++ if (!vec || VECTOR_SIZE(vec) > 4) ++ return INVALID_FINGERPRINT; + ++ vector_foreach_slot(vec, kw, i) { ++ if (i >= 4) ++ break; ++ fp |= (uint32_t)kw->code << (8 * i); ++ } + return fp; + } + +@@ -375,8 +386,8 @@ + static int + do_genhelp(char *reply, int maxlen, const char *cmd, int error) { + int len = 0; +- int i, j; +- uint64_t fp; ++ int i, j, k; ++ uint32_t fp; + struct handler * h; + struct key * kw; + +@@ -403,29 +414,40 @@ + if (len >= maxlen) + goto out; + ++ + vector_foreach_slot (handlers, h, i) { + fp = h->fingerprint; +- vector_foreach_slot (keys, kw, j) { +- if ((kw->code & fp)) { +- fp -= kw->code; +- len += snprintf(reply + len , maxlen - len, +- " %s", kw->str); +- if (len >= maxlen) +- goto out; +- len += genhelp_sprint_aliases(reply + len, +- maxlen - len, +- keys, kw); +- if (len >= maxlen) +- goto out; +- +- if (kw->has_param) { +- len += snprintf(reply + len, +- maxlen - len, +- " $%s", kw->str); ++ for (k = 0; k < 4; k++, fp >>= 8) { ++ uint32_t code = fp & 0xff; ++ ++ if (!code) ++ break; ++ ++ vector_foreach_slot (keys, kw, j) { ++ if ((uint32_t)kw->code == code) { ++ len += snprintf(reply + len , maxlen - len, ++ " %s", kw->str); ++ + if (len >= maxlen) + goto out; +- } ++ ++ len += genhelp_sprint_aliases(reply + len, ++ maxlen - len, ++ keys, kw); ++ if (len >= maxlen) ++ goto out; ++ ++ ++ if (kw->has_param) { ++ len += snprintf(reply + len, ++ maxlen - len, ++ " $%s", kw->str); ++ if (len >= maxlen) ++ goto out; ++ } ++ break; + } ++ } + } + len += snprintf(reply + len, maxlen - len, "\n"); + if (len >= maxlen) +@@ -519,7 +541,7 @@ + } + + char * +-get_keyparam (vector v, uint64_t code) ++get_keyparam (vector v, uint8_t code) + { + struct key * kw; + int i; +@@ -539,63 +561,62 @@ + if (alloc_handlers()) + return 1; + +- add_handler(LIST+PATHS, NULL); +- add_handler(LIST+PATHS+FMT, NULL); +- add_handler(LIST+PATHS+RAW+FMT, NULL); +- add_handler(LIST+PATH, NULL); +- add_handler(LIST+STATUS, NULL); +- add_handler(LIST+DAEMON, NULL); +- add_handler(LIST+MAPS, NULL); +- add_handler(LIST+MAPS+STATUS, NULL); +- add_handler(LIST+MAPS+STATS, NULL); +- add_handler(LIST+MAPS+FMT, NULL); +- add_handler(LIST+MAPS+RAW+FMT, NULL); +- add_handler(LIST+MAPS+TOPOLOGY, NULL); +- add_handler(LIST+MAPS+JSON, NULL); +- add_handler(LIST+TOPOLOGY, NULL); +- add_handler(LIST+MAP+TOPOLOGY, NULL); +- add_handler(LIST+MAP+JSON, NULL); +- add_handler(LIST+MAP+FMT, NULL); +- add_handler(LIST+MAP+RAW+FMT, NULL); +- add_handler(LIST+CONFIG, NULL); +- add_handler(LIST+CONFIG+LOCAL, NULL); +- add_handler(LIST+BLACKLIST, NULL); +- add_handler(LIST+DEVICES, NULL); +- add_handler(LIST+WILDCARDS, NULL); +- add_handler(RESET+MAPS+STATS, NULL); +- add_handler(RESET+MAP+STATS, NULL); +- add_handler(ADD+PATH, NULL); +- add_handler(DEL+PATH, NULL); +- add_handler(ADD+MAP, NULL); +- add_handler(DEL+MAP, NULL); +- add_handler(DEL+MAPS, NULL); +- add_handler(SWITCH+MAP+GROUP, NULL); +- add_handler(RECONFIGURE, NULL); +- add_handler(SUSPEND+MAP, NULL); +- add_handler(RESUME+MAP, NULL); +- add_handler(RESIZE+MAP, NULL); +- add_handler(RESET+MAP, NULL); +- add_handler(RELOAD+MAP, NULL); +- add_handler(DISABLEQ+MAP, NULL); +- add_handler(RESTOREQ+MAP, NULL); +- add_handler(DISABLEQ+MAPS, NULL); +- add_handler(RESTOREQ+MAPS, NULL); +- add_handler(REINSTATE+PATH, NULL); +- add_handler(FAIL+PATH, NULL); +- add_handler(QUIT, NULL); +- add_handler(SHUTDOWN, NULL); +- add_handler(GETPRSTATUS+MAP, NULL); +- add_handler(SETPRSTATUS+MAP, NULL); +- add_handler(UNSETPRSTATUS+MAP, NULL); +- add_handler(GETPRKEY+MAP, NULL); +- add_handler(SETPRKEY+MAP+KEY, NULL); +- add_handler(UNSETPRKEY+MAP, NULL); +- add_handler(FORCEQ+DAEMON, NULL); +- add_handler(RESTOREQ+DAEMON, NULL); +- add_handler(SETMARGINAL+PATH, NULL); +- add_handler(UNSETMARGINAL+PATH, NULL); +- add_handler(UNSETMARGINAL+MAP, NULL); +- ++ add_handler(VRB_LIST | Q1_PATHS, NULL); ++ add_handler(VRB_LIST | Q1_PATHS | Q2_FMT, NULL); ++ add_handler(VRB_LIST | Q1_PATHS | Q2_RAW | Q3_FMT, NULL); ++ add_handler(VRB_LIST | Q1_PATH, NULL); ++ add_handler(VRB_LIST | Q1_STATUS, NULL); ++ add_handler(VRB_LIST | Q1_DAEMON, NULL); ++ add_handler(VRB_LIST | Q1_MAPS, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_STATUS, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_STATS, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_FMT, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_RAW | Q3_FMT, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_TOPOLOGY, NULL); ++ add_handler(VRB_LIST | Q1_MAPS | Q2_JSON, NULL); ++ add_handler(VRB_LIST | Q1_TOPOLOGY, NULL); ++ add_handler(VRB_LIST | Q1_MAP | Q2_TOPOLOGY, NULL); ++ add_handler(VRB_LIST | Q1_MAP | Q2_JSON, NULL); ++ add_handler(VRB_LIST | Q1_MAP | Q2_FMT, NULL); ++ add_handler(VRB_LIST | Q1_MAP | Q2_RAW | Q3_FMT, NULL); ++ add_handler(VRB_LIST | Q1_CONFIG, NULL); ++ add_handler(VRB_LIST | Q1_CONFIG | Q2_LOCAL, NULL); ++ add_handler(VRB_LIST | Q1_BLACKLIST, NULL); ++ add_handler(VRB_LIST | Q1_DEVICES, NULL); ++ add_handler(VRB_LIST | Q1_WILDCARDS, NULL); ++ add_handler(VRB_RESET | Q1_MAPS | Q2_STATS, NULL); ++ add_handler(VRB_RESET | Q1_MAP | Q2_STATS, NULL); ++ add_handler(VRB_ADD | Q1_PATH, NULL); ++ add_handler(VRB_DEL | Q1_PATH, NULL); ++ add_handler(VRB_ADD | Q1_MAP, NULL); ++ add_handler(VRB_DEL | Q1_MAP, NULL); ++ add_handler(VRB_DEL | Q1_MAPS, NULL); ++ add_handler(VRB_SWITCH | Q1_MAP | Q2_GROUP, NULL); ++ add_handler(VRB_RECONFIGURE, NULL); ++ add_handler(VRB_SUSPEND | Q1_MAP, NULL); ++ add_handler(VRB_RESUME | Q1_MAP, NULL); ++ add_handler(VRB_RESIZE | Q1_MAP, NULL); ++ add_handler(VRB_RESET | Q1_MAP, NULL); ++ add_handler(VRB_RELOAD | Q1_MAP, NULL); ++ add_handler(VRB_DISABLEQ | Q1_MAP, NULL); ++ add_handler(VRB_RESTOREQ | Q1_MAP, NULL); ++ add_handler(VRB_DISABLEQ | Q1_MAPS, NULL); ++ add_handler(VRB_RESTOREQ | Q1_MAPS, NULL); ++ add_handler(VRB_REINSTATE | Q1_PATH, NULL); ++ add_handler(VRB_FAIL | Q1_PATH, NULL); ++ add_handler(VRB_QUIT, NULL); ++ add_handler(VRB_SHUTDOWN, NULL); ++ add_handler(VRB_GETPRSTATUS | Q1_MAP, NULL); ++ add_handler(VRB_SETPRSTATUS | Q1_MAP, NULL); ++ add_handler(VRB_UNSETPRSTATUS | Q1_MAP, NULL); ++ add_handler(VRB_GETPRKEY | Q1_MAP, NULL); ++ add_handler(VRB_SETPRKEY | Q1_MAP | Q2_KEY, NULL); ++ add_handler(VRB_UNSETPRKEY | Q1_MAP, NULL); ++ add_handler(VRB_FORCEQ | Q1_DAEMON, NULL); ++ add_handler(VRB_RESTOREQ | Q1_DAEMON, NULL); ++ add_handler(VRB_SETMARGINAL | Q1_PATH, NULL); ++ add_handler(VRB_UNSETMARGINAL | Q1_PATH, NULL); ++ add_handler(VRB_UNSETMARGINAL | Q1_MAP, NULL); + return 0; + } + +@@ -607,7 +628,7 @@ + } + + static int +-key_match_fingerprint (struct key * kw, uint64_t fp) ++key_match_fingerprint (struct key * kw, uint32_t fp) + { + if (!fp) + return 0; +@@ -622,7 +643,7 @@ + key_generator (const char * str, int state) + { + static int index, len, has_param; +- static uint64_t rlfp; ++ static uint32_t rlfp; + struct key * kw; + int i; + struct handler *h; +@@ -692,7 +713,7 @@ + * nfp is the candidate fingerprint we try to + * validate against all known command fingerprints. + */ +- uint64_t nfp = rlfp | kw->code; ++ uint32_t nfp = rlfp | kw->code; + vector_foreach_slot(handlers, h, i) { + if (!rlfp || ((h->fingerprint & nfp) == nfp)) { + /* +--- a/multipathd/cli.h ++++ b/multipathd/cli.h +@@ -3,96 +3,97 @@ + + #include + ++/* ++ * CLI commands consist of 4 bytes, a verb (byte 0) and up to ++ * 3 qualifiers (byte 1 - 3). ++ */ ++ + enum { +- __LIST, +- __ADD, +- __DEL, +- __SWITCH, +- __SUSPEND, +- __RESUME, +- __REINSTATE, +- __FAIL, +- __RESIZE, +- __RESET, +- __RELOAD, +- __FORCEQ, +- __DISABLEQ, +- __RESTOREQ, +- __PATHS, +- __MAPS, +- __PATH, +- __MAP, +- __GROUP, +- __RECONFIGURE, +- __DAEMON, +- __STATUS, +- __STATS, +- __TOPOLOGY, +- __CONFIG, +- __BLACKLIST, +- __DEVICES, +- __RAW, +- __WILDCARDS, +- __QUIT, +- __SHUTDOWN, +- __GETPRSTATUS, +- __SETPRSTATUS, +- __UNSETPRSTATUS, +- __FMT, +- __JSON, +- __GETPRKEY, +- __SETPRKEY, +- __UNSETPRKEY, +- __KEY, +- __LOCAL, +- __SETMARGINAL, +- __UNSETMARGINAL, ++ /* See INVALID_FINGERPRINT in cli.c */ ++ KEY_INVALID = 0, ++ ++ /* Verbs */ ++ VRB_LIST = 1, ++ VRB_ADD = 2, ++ VRB_DEL = 3, ++ VRB_RESET = 4, ++ VRB_SWITCH = 5, ++ VRB_RECONFIGURE = 6, ++ VRB_SUSPEND = 7, ++ VRB_RESUME = 8, ++ VRB_RESIZE = 9, ++ VRB_RELOAD = 10, ++ VRB_FAIL = 11, ++ VRB_REINSTATE = 12, ++ VRB_DISABLEQ = 13, ++ VRB_RESTOREQ = 14, ++ VRB_FORCEQ = 15, ++ VRB_GETPRSTATUS = 16, ++ VRB_SETPRSTATUS = 17, ++ VRB_UNSETPRSTATUS = 18, ++ VRB_GETPRKEY = 19, ++ VRB_SETPRKEY = 20, ++ VRB_UNSETPRKEY = 21, ++ VRB_SETMARGINAL = 22, ++ VRB_UNSETMARGINAL = 23, ++ VRB_SHUTDOWN = 24, ++ VRB_QUIT = 25, ++ ++ /* Qualifiers, values must be different from verbs */ ++ KEY_PATH = 65, ++ KEY_PATHS = 66, ++ KEY_MAP = 67, ++ KEY_MAPS = 68, ++ KEY_TOPOLOGY = 69, ++ KEY_CONFIG = 70, ++ KEY_BLACKLIST = 71, ++ KEY_DEVICES = 72, ++ KEY_WILDCARDS = 73, ++ KEY_ALL = 74, ++ KEY_DAEMON = 75, ++ KEY_FMT = 76, ++ KEY_RAW = 77, ++ KEY_STATUS = 78, ++ KEY_STATS = 79, ++ KEY_JSON = 80, ++ KEY_LOCAL = 81, ++ KEY_GROUP = 82, ++ KEY_KEY = 83, + }; ++/* ++ * The shifted qualifiers determine valid positions of the ++ * keywords in the known commands. E.g. the only qualifier ++ * that's valid in position 3 is "fmt", e.g. "list maps raw fmt". ++ */ ++enum { ++ /* byte 1: qualifier 1 */ ++ Q1_PATH = KEY_PATH << 8, ++ Q1_PATHS = KEY_PATHS << 8, ++ Q1_MAP = KEY_MAP << 8, ++ Q1_MAPS = KEY_MAPS << 8, ++ Q1_TOPOLOGY = KEY_TOPOLOGY << 8, ++ Q1_CONFIG = KEY_CONFIG << 8, ++ Q1_BLACKLIST = KEY_BLACKLIST << 8, ++ Q1_DEVICES = KEY_DEVICES << 8, ++ Q1_WILDCARDS = KEY_WILDCARDS << 8, ++ Q1_ALL = KEY_ALL << 8, ++ Q1_DAEMON = KEY_DAEMON << 8, ++ Q1_STATUS = KEY_STATUS << 8, ++ ++ /* byte 2: qualifier 2 */ ++ Q2_FMT = KEY_FMT << 16, ++ Q2_RAW = KEY_RAW << 16, ++ Q2_STATUS = KEY_STATUS << 16, ++ Q2_STATS = KEY_STATS << 16, ++ Q2_TOPOLOGY = KEY_TOPOLOGY << 16, ++ Q2_JSON = KEY_JSON << 16, ++ Q2_LOCAL = KEY_LOCAL << 16, ++ Q2_GROUP = KEY_GROUP << 16, ++ Q2_KEY = KEY_KEY << 16, + +-#define LIST (1 << __LIST) +-#define ADD (1 << __ADD) +-#define DEL (1 << __DEL) +-#define SWITCH (1 << __SWITCH) +-#define SUSPEND (1 << __SUSPEND) +-#define RESUME (1 << __RESUME) +-#define REINSTATE (1 << __REINSTATE) +-#define FAIL (1 << __FAIL) +-#define RESIZE (1 << __RESIZE) +-#define RESET (1 << __RESET) +-#define RELOAD (1 << __RELOAD) +-#define FORCEQ (1 << __FORCEQ) +-#define DISABLEQ (1 << __DISABLEQ) +-#define RESTOREQ (1 << __RESTOREQ) +-#define PATHS (1 << __PATHS) +-#define MAPS (1 << __MAPS) +-#define PATH (1 << __PATH) +-#define MAP (1 << __MAP) +-#define GROUP (1 << __GROUP) +-#define RECONFIGURE (1 << __RECONFIGURE) +-#define DAEMON (1 << __DAEMON) +-#define STATUS (1 << __STATUS) +-#define STATS (1 << __STATS) +-#define TOPOLOGY (1 << __TOPOLOGY) +-#define CONFIG (1 << __CONFIG) +-#define BLACKLIST (1 << __BLACKLIST) +-#define DEVICES (1 << __DEVICES) +-#define RAW (1 << __RAW) +-#define COUNT (1 << __COUNT) +-#define WILDCARDS (1 << __WILDCARDS) +-#define QUIT (1 << __QUIT) +-#define SHUTDOWN (1 << __SHUTDOWN) +-#define GETPRSTATUS (1ULL << __GETPRSTATUS) +-#define SETPRSTATUS (1ULL << __SETPRSTATUS) +-#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS) +-#define FMT (1ULL << __FMT) +-#define JSON (1ULL << __JSON) +-#define GETPRKEY (1ULL << __GETPRKEY) +-#define SETPRKEY (1ULL << __SETPRKEY) +-#define UNSETPRKEY (1ULL << __UNSETPRKEY) +-#define KEY (1ULL << __KEY) +-#define LOCAL (1ULL << __LOCAL) +-#define SETMARGINAL (1ULL << __SETMARGINAL) +-#define UNSETMARGINAL (1ULL << __UNSETMARGINAL) ++ /* byte 3: qualifier 3 */ ++ Q3_FMT = KEY_FMT << 24, ++}; + + #define INITIAL_REPLY_LEN 1200 + +@@ -120,23 +121,23 @@ + struct key { + char * str; + char * param; +- uint64_t code; ++ uint8_t code; + int has_param; + }; + + struct handler { +- uint64_t fingerprint; ++ uint32_t fingerprint; + int locked; + int (*fn)(void *, char **, int *, void *); + }; + + int alloc_handlers (void); +-int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)); +-int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); +-int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); ++int add_handler (uint32_t fp, int (*fn)(void *, char **, int *, void *)); ++int set_handler_callback (uint32_t fp, int (*fn)(void *, char **, int *, void *)); ++int set_unlocked_handler_callback (uint32_t fp, int (*fn)(void *, char **, int *, void *)); + int parse_cmd (char * cmd, char ** reply, int * len, void *, int); + int load_keys (void); +-char * get_keyparam (vector v, uint64_t code); ++char * get_keyparam (vector v, uint8_t code); + void free_keys (vector vec); + void free_handlers (void); + int cli_init (void); +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -318,7 +318,7 @@ + cli_list_paths_fmt (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * fmt = get_keyparam(v, FMT); ++ char * fmt = get_keyparam(v, KEY_FMT); + + condlog(3, "list paths (operator)"); + +@@ -329,7 +329,7 @@ + cli_list_paths_raw (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * fmt = get_keyparam(v, FMT); ++ char * fmt = get_keyparam(v, KEY_FMT); + + condlog(3, "list paths (operator)"); + +@@ -340,7 +340,7 @@ + cli_list_path (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path *pp; + + param = convert_dev(param, 1); +@@ -358,7 +358,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -387,7 +387,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -552,7 +552,7 @@ + cli_list_maps_fmt (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * fmt = get_keyparam(v, FMT); ++ char * fmt = get_keyparam(v, KEY_FMT); + + condlog(3, "list maps (operator)"); + +@@ -563,7 +563,7 @@ + cli_list_maps_raw (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * fmt = get_keyparam(v, FMT); ++ char * fmt = get_keyparam(v, KEY_FMT); + + condlog(3, "list maps (operator)"); + +@@ -575,8 +575,8 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); +- char * fmt = get_keyparam(v, FMT); ++ char * param = get_keyparam(v, KEY_MAP); ++ char * fmt = get_keyparam(v, KEY_FMT); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -595,8 +595,8 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); +- char * fmt = get_keyparam(v, FMT); ++ char * param = get_keyparam(v, KEY_MAP); ++ char * fmt = get_keyparam(v, KEY_FMT); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -678,7 +678,7 @@ + { + struct vectors * vecs = (struct vectors *)data; + struct multipath * mpp; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + mpp = find_mp_by_str(vecs->mpvec, param); +@@ -695,7 +695,7 @@ + cli_add_path (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path *pp; + int r; + struct config *conf; +@@ -802,7 +802,7 @@ + cli_del_path (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path *pp; + + param = convert_dev(param, 1); +@@ -819,7 +819,7 @@ + cli_add_map (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + int major = -1, minor = -1; + char dev_path[PATH_SIZE]; + char *refwwid, *alias = NULL; +@@ -880,7 +880,7 @@ + cli_del_map (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + int major, minor; + char *alias; + int rc; +@@ -925,7 +925,7 @@ + cli_reload(void *v, char **reply, int *len, void *data) + { + struct vectors * vecs = (struct vectors *)data; +- char * mapname = get_keyparam(v, MAP); ++ char * mapname = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + int minor; + +@@ -978,7 +978,7 @@ + cli_resize(void *v, char **reply, int *len, void *data) + { + struct vectors * vecs = (struct vectors *)data; +- char * mapname = get_keyparam(v, MAP); ++ char * mapname = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + int minor; + unsigned long long size; +@@ -1070,7 +1070,7 @@ + cli_restore_queueing(void *v, char **reply, int *len, void *data) + { + struct vectors * vecs = (struct vectors *)data; +- char * mapname = get_keyparam(v, MAP); ++ char * mapname = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + int minor; + struct config *conf; +@@ -1133,7 +1133,7 @@ + cli_disable_queueing(void *v, char **reply, int *len, void *data) + { + struct vectors * vecs = (struct vectors *)data; +- char * mapname = get_keyparam(v, MAP); ++ char * mapname = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + int minor; + +@@ -1180,8 +1180,8 @@ + int + cli_switch_group(void * v, char ** reply, int * len, void * data) + { +- char * mapname = get_keyparam(v, MAP); +- int groupnum = atoi(get_keyparam(v, GROUP)); ++ char * mapname = get_keyparam(v, KEY_MAP); ++ int groupnum = atoi(get_keyparam(v, KEY_GROUP)); + + mapname = convert_dev(mapname, 0); + condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum); +@@ -1210,7 +1210,7 @@ + cli_suspend(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + int r; + struct multipath * mpp; + +@@ -1240,7 +1240,7 @@ + cli_resume(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + int r; + struct multipath * mpp; + uint16_t udev_flags; +@@ -1272,7 +1272,7 @@ + cli_reinstate(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path * pp; + + param = convert_dev(param, 1); +@@ -1295,7 +1295,7 @@ + cli_reassign (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + + param = convert_dev(param, 0); +@@ -1319,7 +1319,7 @@ + cli_fail(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path * pp; + int r; + +@@ -1450,7 +1450,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -1475,7 +1475,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -1498,7 +1498,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, MAP); ++ char * param = get_keyparam(v, KEY_MAP); + + param = convert_dev(param, 0); + get_path_layout(vecs->pathvec, 0); +@@ -1520,7 +1520,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char *mapname = get_keyparam(v, MAP); ++ char *mapname = get_keyparam(v, KEY_MAP); + char *flagstr = ""; + + mapname = convert_dev(mapname, 0); +@@ -1553,7 +1553,7 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char *mapname = get_keyparam(v, MAP); ++ char *mapname = get_keyparam(v, KEY_MAP); + int ret; + struct config *conf; + +@@ -1577,8 +1577,8 @@ + { + struct multipath * mpp; + struct vectors * vecs = (struct vectors *)data; +- char *mapname = get_keyparam(v, MAP); +- char *keyparam = get_keyparam(v, KEY); ++ char *mapname = get_keyparam(v, KEY_MAP); ++ char *keyparam = get_keyparam(v, KEY_KEY); + uint64_t prkey; + uint8_t flags; + int ret; +@@ -1607,7 +1607,7 @@ + int cli_set_marginal(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path * pp; + + param = convert_dev(param, 1); +@@ -1634,7 +1634,7 @@ + int cli_unset_marginal(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * param = get_keyparam(v, PATH); ++ char * param = get_keyparam(v, KEY_PATH); + struct path * pp; + + param = convert_dev(param, 1); +@@ -1661,7 +1661,7 @@ + int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +- char * mapname = get_keyparam(v, MAP); ++ char * mapname = get_keyparam(v, KEY_MAP); + struct multipath *mpp; + struct pathgroup * pgp; + struct path * pp; +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1604,62 +1604,66 @@ + /* Tell main thread that thread has started */ + post_config_state(DAEMON_CONFIGURE); + +- set_handler_callback(LIST+PATHS, cli_list_paths); +- set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); +- set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); +- set_handler_callback(LIST+PATH, cli_list_path); +- set_handler_callback(LIST+MAPS, cli_list_maps); +- set_handler_callback(LIST+STATUS, cli_list_status); +- set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon); +- set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); +- set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); +- set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); +- set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); +- set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); +- set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); +- set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json); +- set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); +- set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt); +- set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt); +- set_handler_callback(LIST+MAP+JSON, cli_list_map_json); +- set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local); +- set_handler_callback(LIST+CONFIG, cli_list_config); +- set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); +- set_handler_callback(LIST+DEVICES, cli_list_devices); +- set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); +- set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats); +- set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats); +- set_handler_callback(ADD+PATH, cli_add_path); +- set_handler_callback(DEL+PATH, cli_del_path); +- set_handler_callback(ADD+MAP, cli_add_map); +- set_handler_callback(DEL+MAP, cli_del_map); +- set_handler_callback(DEL+MAPS, cli_del_maps); +- set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); +- set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); +- set_handler_callback(SUSPEND+MAP, cli_suspend); +- set_handler_callback(RESUME+MAP, cli_resume); +- set_handler_callback(RESIZE+MAP, cli_resize); +- set_handler_callback(RELOAD+MAP, cli_reload); +- set_handler_callback(RESET+MAP, cli_reassign); +- set_handler_callback(REINSTATE+PATH, cli_reinstate); +- set_handler_callback(FAIL+PATH, cli_fail); +- set_handler_callback(DISABLEQ+MAP, cli_disable_queueing); +- set_handler_callback(RESTOREQ+MAP, cli_restore_queueing); +- set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing); +- set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing); +- set_unlocked_handler_callback(QUIT, cli_quit); +- set_unlocked_handler_callback(SHUTDOWN, cli_shutdown); +- set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus); +- set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus); +- set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus); +- set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q); +- set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q); +- set_handler_callback(GETPRKEY+MAP, cli_getprkey); +- set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey); +- set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); +- set_handler_callback(SETMARGINAL+PATH, cli_set_marginal); +- set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal); +- set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal); ++ // part of CVE-2022-41973 ++ // backported commit f812466f68b8e020818c6454d7b7a7e278bc99f6 ++ #define HANDLER(x) x ++ ++ set_handler_callback(VRB_LIST | Q1_PATHS, HANDLER(cli_list_paths)); ++ set_handler_callback(VRB_LIST | Q1_PATHS | Q2_FMT, HANDLER(cli_list_paths_fmt)); ++ set_handler_callback(VRB_LIST | Q1_PATHS | Q2_RAW | Q3_FMT, HANDLER(cli_list_paths_raw)); ++ set_handler_callback(VRB_LIST | Q1_PATH, HANDLER(cli_list_path)); ++ set_handler_callback(VRB_LIST | Q1_MAPS, HANDLER(cli_list_maps)); ++ set_handler_callback(VRB_LIST | Q1_STATUS, HANDLER(cli_list_status)); ++ set_unlocked_handler_callback(VRB_LIST | Q1_DAEMON, HANDLER(cli_list_daemon)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_STATUS, HANDLER(cli_list_maps_status)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_STATS, HANDLER(cli_list_maps_stats)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_FMT, HANDLER(cli_list_maps_fmt)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_RAW | Q3_FMT, HANDLER(cli_list_maps_raw)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_TOPOLOGY, HANDLER(cli_list_maps_topology)); ++ set_handler_callback(VRB_LIST | Q1_TOPOLOGY, HANDLER(cli_list_maps_topology)); ++ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_JSON, HANDLER(cli_list_maps_json)); ++ set_handler_callback(VRB_LIST | Q1_MAP | Q2_TOPOLOGY, HANDLER(cli_list_map_topology)); ++ set_handler_callback(VRB_LIST | Q1_MAP | Q2_FMT, HANDLER(cli_list_map_fmt)); ++ set_handler_callback(VRB_LIST | Q1_MAP | Q2_RAW | Q3_FMT, HANDLER(cli_list_map_fmt)); ++ set_handler_callback(VRB_LIST | Q1_MAP | Q2_JSON, HANDLER(cli_list_map_json)); ++ set_handler_callback(VRB_LIST | Q1_CONFIG | Q2_LOCAL, HANDLER(cli_list_config_local)); ++ set_handler_callback(VRB_LIST | Q1_CONFIG, HANDLER(cli_list_config)); ++ set_handler_callback(VRB_LIST | Q1_BLACKLIST, HANDLER(cli_list_blacklist)); ++ set_handler_callback(VRB_LIST | Q1_DEVICES, HANDLER(cli_list_devices)); ++ set_handler_callback(VRB_LIST | Q1_WILDCARDS, HANDLER(cli_list_wildcards)); ++ set_handler_callback(VRB_RESET | Q1_MAPS | Q2_STATS, HANDLER(cli_reset_maps_stats)); ++ set_handler_callback(VRB_RESET | Q1_MAP | Q2_STATS, HANDLER(cli_reset_map_stats)); ++ set_handler_callback(VRB_ADD | Q1_PATH, HANDLER(cli_add_path)); ++ set_handler_callback(VRB_DEL | Q1_PATH, HANDLER(cli_del_path)); ++ set_handler_callback(VRB_ADD | Q1_MAP, HANDLER(cli_add_map)); ++ set_handler_callback(VRB_DEL | Q1_MAP, HANDLER(cli_del_map)); ++ set_handler_callback(VRB_DEL | Q1_MAPS, HANDLER(cli_del_maps)); ++ set_handler_callback(VRB_SWITCH | Q1_MAP | Q2_GROUP, HANDLER(cli_switch_group)); ++ set_unlocked_handler_callback(VRB_RECONFIGURE, HANDLER(cli_reconfigure)); ++ set_handler_callback(VRB_SUSPEND | Q1_MAP, HANDLER(cli_suspend)); ++ set_handler_callback(VRB_RESUME | Q1_MAP, HANDLER(cli_resume)); ++ set_handler_callback(VRB_RESIZE | Q1_MAP, HANDLER(cli_resize)); ++ set_handler_callback(VRB_RELOAD | Q1_MAP, HANDLER(cli_reload)); ++ set_handler_callback(VRB_RESET | Q1_MAP, HANDLER(cli_reassign)); ++ set_handler_callback(VRB_REINSTATE | Q1_PATH, HANDLER(cli_reinstate)); ++ set_handler_callback(VRB_FAIL | Q1_PATH, HANDLER(cli_fail)); ++ set_handler_callback(VRB_DISABLEQ | Q1_MAP, HANDLER(cli_disable_queueing)); ++ set_handler_callback(VRB_RESTOREQ | Q1_MAP, HANDLER(cli_restore_queueing)); ++ set_handler_callback(VRB_DISABLEQ | Q1_MAPS, HANDLER(cli_disable_all_queueing)); ++ set_handler_callback(VRB_RESTOREQ | Q1_MAPS, HANDLER(cli_restore_all_queueing)); ++ set_unlocked_handler_callback(VRB_QUIT, HANDLER(cli_quit)); ++ set_unlocked_handler_callback(VRB_SHUTDOWN, HANDLER(cli_shutdown)); ++ set_handler_callback(VRB_GETPRSTATUS | Q1_MAP, HANDLER(cli_getprstatus)); ++ set_handler_callback(VRB_SETPRSTATUS | Q1_MAP, HANDLER(cli_setprstatus)); ++ set_handler_callback(VRB_UNSETPRSTATUS | Q1_MAP, HANDLER(cli_unsetprstatus)); ++ set_handler_callback(VRB_FORCEQ | Q1_DAEMON, HANDLER(cli_force_no_daemon_q)); ++ set_handler_callback(VRB_RESTOREQ | Q1_DAEMON, HANDLER(cli_restore_no_daemon_q)); ++ set_handler_callback(VRB_GETPRKEY | Q1_MAP, HANDLER(cli_getprkey)); ++ set_handler_callback(VRB_SETPRKEY | Q1_MAP | Q2_KEY, HANDLER(cli_setprkey)); ++ set_handler_callback(VRB_UNSETPRKEY | Q1_MAP, HANDLER(cli_unsetprkey)); ++ set_handler_callback(VRB_SETMARGINAL | Q1_PATH, HANDLER(cli_set_marginal)); ++ set_handler_callback(VRB_UNSETMARGINAL | Q1_PATH, HANDLER(cli_unset_marginal)); ++ set_handler_callback(VRB_UNSETMARGINAL | Q1_MAP, HANDLER(cli_unset_all_marginal)); + + umask(077); + uxsock_listen(&uxsock_trigger, ux_sock, ap); diff -Nru multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit2.patch multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit2.patch --- multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit2.patch 1970-01-01 00:00:00.000000000 +0000 +++ multipath-tools-0.8.5/debian/patches/CVE-2022-41974-commit2.patch 2022-12-27 08:48:45.000000000 +0000 @@ -0,0 +1,385 @@ +Description: CVE-2022-41974 part 2 + Backport upstream commit to address CVE: + The command completion must look at possible combinations of + keywords now. This makes it much more useful, actually. +Origin: https://github.com/opensvc/multipath-tools/commit/d139bcf0842bc0a16beab86e1349ed65b150bf0c +Bug-Debian: https://bugs.debian.org/1022742 +Forwarded: not needed +Applied-Upstream: https://github.com/opensvc/multipath-tools/commit/c4912a639b7ff527aa11d665944594926ff94a7a +Last-Update: 2022-12-23 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -23,6 +23,16 @@ + static vector keys; + static vector handlers; + ++vector get_keys(void) ++{ ++ return keys; ++} ++ ++vector get_handlers(void) ++{ ++ return handlers; ++} ++ + static struct key * + alloc_key (void) + { +@@ -132,8 +142,7 @@ + return 0; + } + +-static void +-free_key (struct key * kw) ++void free_key (struct key * kw) + { + if (kw->str) + FREE(kw->str); +@@ -262,16 +271,31 @@ + return foundkw; + } + ++static void cleanup_strvec(vector *arg) ++{ ++ free_strvec(*arg); ++} ++ ++static void cleanup_keys(vector *arg) ++{ ++ free_keys(*arg); ++} ++ ++ + /* +- * get_cmdvec ++ * get_cmdvec() - parse input ++ * ++ * @cmd: a command string to be parsed ++ * @v: a vector of keywords with parameters + * + * returns: + * ENOMEM: not enough memory to allocate command +- * EAGAIN: command not found ++ * ESRCH: keyword not found at end of input ++ * ENOENT: keyword not found somewhere else + * EINVAL: argument missing for command + */ +-static int +-get_cmdvec (char * cmd, vector *v) ++int ++get_cmdvec (char * cmd, vector *v, bool allow_incomplete) + { + int i; + int r = 0; +@@ -279,19 +303,12 @@ + char * buff; + struct key * kw = NULL; + struct key * cmdkw = NULL; +- vector cmdvec, strvec; ++ vector cmdvec __attribute__((cleanup(cleanup_keys))) = vector_alloc(); ++ vector strvec __attribute__((cleanup(cleanup_strvec))) = alloc_strvec(cmd); + +- strvec = alloc_strvec(cmd); +- if (!strvec) ++ if (!strvec || !cmdvec) + return ENOMEM; + +- cmdvec = vector_alloc(); +- +- if (!cmdvec) { +- free_strvec(strvec); +- return ENOMEM; +- } +- + vector_foreach_slot(strvec, buff, i) { + if (is_quote(buff)) + continue; +@@ -302,18 +319,18 @@ + } + kw = find_key(buff); + if (!kw) { +- r = EAGAIN; +- goto out; ++ r = i == VECTOR_SIZE(strvec) - 1 ? ESRCH : ENOENT; ++ break; + } + cmdkw = alloc_key(); + if (!cmdkw) { + r = ENOMEM; +- goto out; ++ break; + } + if (!vector_alloc_slot(cmdvec)) { + FREE(cmdkw); + r = ENOMEM; +- goto out; ++ break; + } + vector_set_slot(cmdvec, cmdkw); + cmdkw->code = kw->code; +@@ -321,17 +338,14 @@ + if (kw->has_param) + get_param = 1; + } +- if (get_param) { ++ if (get_param) + r = EINVAL; +- goto out; +- } +- *v = cmdvec; +- free_strvec(strvec); +- return 0; + +-out: +- free_strvec(strvec); +- free_keys(cmdvec); ++ if (r && !allow_incomplete) ++ return r; ++ ++ *v = cmdvec; ++ cmdvec = NULL; + return r; + } + +@@ -487,7 +501,7 @@ + vector cmdvec = NULL; + struct timespec tmo; + +- r = get_cmdvec(cmd, &cmdvec); ++ r = get_cmdvec(cmd, &cmdvec, false); + + if (r) { + *reply = genhelp_handler(cmd, r); +@@ -642,27 +656,62 @@ + char * + key_generator (const char * str, int state) + { +- static int index, len, has_param; +- static uint32_t rlfp; +- struct key * kw; +- int i; +- struct handler *h; +- vector v = NULL; ++ static vector completions; ++ static int index; ++ char *word; + + if (!state) { ++ uint32_t rlfp = 0, mask = 0; ++ int len = strlen(str), vlen = 0, i, j; ++ struct key * kw; ++ struct handler *h; ++ vector handlers = get_handlers(); ++ vector keys = get_keys(); ++ vector v = NULL; ++ int r = get_cmdvec(rl_line_buffer, &v, true); ++ + index = 0; +- has_param = 0; +- rlfp = 0; +- len = strlen(str); +- int r = get_cmdvec(rl_line_buffer, &v); ++ if (completions) ++ vector_free(completions); ++ ++ completions = vector_alloc(); ++ ++ if (!completions || r == ENOMEM) { ++ if (v) ++ vector_free(v); ++ return NULL; ++ } ++ ++ /* ++ * Special case: get_cmdvec() ignores trailing whitespace, ++ * readline doesn't. get_cmdvec() will return "[show]" and ++ * ESRCH for both "show bogus\t" and "show bogus \t". ++ * The former case will fail below. In the latter case, ++ * We shouldn't offer completions. ++ */ ++ if (r == ESRCH && !len) ++ r = ENOENT; ++ + /* + * If a word completion is in progress, we don't want + * to take an exact keyword match in the fingerprint. + * For ex "show map[tab]" would validate "map" and discard + * "maps" as a valid candidate. + */ +- if (v && len) +- vector_del_slot(v, VECTOR_SIZE(v) - 1); ++ if (r != ESRCH && VECTOR_SIZE(v) && len) { ++ kw = VECTOR_SLOT(v, VECTOR_SIZE(v) - 1); ++ /* ++ * If kw->param is set, we were already parsing a ++ * parameter, not the keyword. Don't delete it. ++ */ ++ if (!kw->param) { ++ free_key(kw); ++ vector_del_slot(v, VECTOR_SIZE(v) - 1); ++ if (r == EINVAL) ++ r = 0; ++ } ++ } ++ + /* + * Clean up the mess if we dropped the last slot of a 1-slot + * vector +@@ -671,64 +720,84 @@ + vector_free(v); + v = NULL; + } +- /* +- * If last keyword takes a param, don't even try to guess +- */ +- if (r == EINVAL) { +- has_param = 1; +- return (strdup("(value)")); +- } ++ + /* + * Compute a command fingerprint to find out possible completions. + * Once done, the vector is useless. Free it. + */ + if (v) { + rlfp = fingerprint(v); ++ vlen = VECTOR_SIZE(v); ++ if (vlen >= 4) ++ mask = ~0; ++ else ++ mask = (uint32_t)(1U << (8 * vlen)) - 1; + free_keys(v); + } +- } +- /* +- * No more completions for parameter placeholder. +- * Brave souls might try to add parameter completion by walking paths and +- * multipaths vectors. +- */ +- if (has_param) +- return ((char *)NULL); +- /* +- * Loop through keywords for completion candidates +- */ +- vector_foreach_slot_after (keys, kw, index) { +- if (!strncmp(kw->str, str, len)) { +- /* +- * Discard keywords already in the command line +- */ +- if (key_match_fingerprint(kw, rlfp)) { +- struct key * curkw = find_key(str); +- if (!curkw || (curkw != kw)) ++ condlog(4, "%s: line=\"%s\" str=\"%s\" r=%d fp=%08x mask=%08x", ++ __func__, rl_line_buffer, str, r, rlfp, mask); ++ ++ /* ++ * If last keyword takes a param, don't even try to guess ++ * Brave souls might try to add parameter completion by walking ++ * paths and multipaths vectors. ++ */ ++ if (r == EINVAL) { ++ if (len == 0 && vector_alloc_slot(completions)) ++ vector_set_slot(completions, ++ strdup("VALUE")); ++ ++ goto init_done; ++ } ++ ++ if (r == ENOENT) ++ goto init_done; ++ ++ vector_foreach_slot(handlers, h, i) { ++ uint8_t code; ++ ++ if (rlfp != (h->fingerprint & mask)) ++ continue; ++ ++ if (vlen >= 4) ++ /* ++ * => mask == ~0 => rlfp == h->fingerprint ++ * Complete command. This must be the only match. ++ */ ++ goto init_done; ++ else if (rlfp == h->fingerprint && r != ESRCH && ++ !strcmp(str, "") && ++ vector_alloc_slot(completions)) ++ /* just completed */ ++ vector_set_slot(completions, strdup("")); ++ else { ++ /* vlen must be 1, 2, or 3 */ ++ code = (h->fingerprint >> vlen * 8); ++ ++ if (code == KEY_INVALID) + continue; +- } +- /* +- * Discard keywords making syntax errors. +- * +- * nfp is the candidate fingerprint we try to +- * validate against all known command fingerprints. +- */ +- uint32_t nfp = rlfp | kw->code; +- vector_foreach_slot(handlers, h, i) { +- if (!rlfp || ((h->fingerprint & nfp) == nfp)) { +- /* +- * At least one full command is +- * possible with this keyword : +- * Consider it validated +- */ +- index++; +- return (strdup(kw->str)); ++ ++ vector_foreach_slot(keys, kw, j) { ++ if (kw->code != code || ++ strncmp(kw->str, str, len)) ++ continue; ++ if (vector_alloc_slot(completions)) ++ vector_set_slot(completions, ++ strdup(kw->str)); + } + } ++ + } ++ vector_foreach_slot(completions, word, i) ++ condlog(4, "%s: %d -> \"%s\"", __func__, i, word); ++ + } +- /* +- * No more candidates +- */ +- return ((char *)NULL); ++ ++init_done: ++ vector_foreach_slot_after(completions, word, index) { ++ index++; ++ return word; ++ } ++ ++ return NULL; + } +--- a/multipathd/cli.h ++++ b/multipathd/cli.h +@@ -3,6 +3,8 @@ + + #include + ++#include ++ + /* + * CLI commands consist of 4 bytes, a verb (byte 0) and up to + * 3 qualifiers (byte 1 - 3). +@@ -135,9 +137,11 @@ + int add_handler (uint32_t fp, int (*fn)(void *, char **, int *, void *)); + int set_handler_callback (uint32_t fp, int (*fn)(void *, char **, int *, void *)); + int set_unlocked_handler_callback (uint32_t fp, int (*fn)(void *, char **, int *, void *)); ++int get_cmdvec (char *cmd, vector *v, bool allow_incomplete); + int parse_cmd (char * cmd, char ** reply, int * len, void *, int); + int load_keys (void); + char * get_keyparam (vector v, uint8_t code); ++void free_key (struct key * kw); + void free_keys (vector vec); + void free_handlers (void); + int cli_init (void); diff -Nru multipath-tools-0.8.5/debian/patches/series multipath-tools-0.8.5/debian/patches/series --- multipath-tools-0.8.5/debian/patches/series 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/patches/series 2023-02-01 20:45:00.000000000 +0000 @@ -6,5 +6,8 @@ 0007-11-dm-mpath-fix-DM_UDEV_RULES_VSN-check.patch 0008-Bug-916521-FTCBFS-uses-the-wrong-pkg-config.patch 0009-kpartx-rules-use-Debian-specific-partx-path.patch -0010-multipath.rules-do-not-assume-usrmerged-paths.patch +#0010-multipath.rules-do-not-assume-usrmerged-paths.patch 0010-Fix-bashism-in-kpartx_id-script.patch +CVE-2022-41974-commit1.patch +CVE-2022-41974-commit2.patch +CVE-2022-41973.patch diff -Nru multipath-tools-0.8.5/debian/rules multipath-tools-0.8.5/debian/rules --- multipath-tools-0.8.5/debian/rules 2021-04-28 17:10:55.000000000 +0000 +++ multipath-tools-0.8.5/debian/rules 2023-02-02 15:01:50.000000000 +0000 @@ -22,14 +22,15 @@ build-stamp: clean-tree dh_testdir - + + dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib SYSTEMDPATH=/lib USE_SYSTEMD=1 + [ ! -f kpartx/del-part-nodes.rules ] || cp kpartx/del-part-nodes.rules debian/kpartx.del-part-nodes.udev [ ! -f kpartx/dm-parts.rules ] || cp kpartx/dm-parts.rules debian/kpartx.dm-parts.udev [ ! -f kpartx/kpartx.rules ] || cp kpartx/kpartx.rules debian/kpartx.udev [ ! -f multipath/multipath.rules ] || cp multipath/multipath.rules debian/multipath.udev [ ! -f multipath/11-dm-mpath.rules ] || cp multipath/11-dm-mpath.rules debian/dm-mpath.udev - dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib SYSTEMDPATH=/lib USE_SYSTEMD=1 touch $@ @@ -37,12 +38,15 @@ build-multipath-udeb-stamp: dh_testdir - DEB_CFLAGS_MAINT_APPEND="-static-libgcc" dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib USE_SYSTEMD=0 + # runtimedir: for udeb, set to /dev/shm + DEB_CFLAGS_MAINT_APPEND="-static-libgcc" dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib USE_SYSTEMD=0 runtimedir=/dev/shm # store files for install target mkdir -p $(CURDIR)/debian/tmp-multipath-udeb/sbin $(MAKE) -j1 install DESTDIR=$(CURDIR)/debian/tmp-multipath-udeb LIB=/lib USE_SYSTEMD=0 + rm debian/tmp-multipath-udeb/usr/lib/tmpfiles.d/multipath.conf + touch $@ clean-tree: