Package: multipath-tools
Version: 0.4.9-3
Followup-For: Bug #757508
User: ubuntu-de...@lists.ubuntu.com
Usertags: origin-ubuntu utopic ubuntu-patch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

In Ubuntu, the attached patch was applied to achieve the following:

[Impact]

 * Multipath can cause segmentation fault due to wrong code and can
   possibly cause user to loose access to multipath devices.

[Test Case]

 * To use multipath and wait for the problem to occur sometime (inevitable).

[Regression Potential]

 * Patch 1/4 tries to fix the issue. Patch 2/4 fixes the 1/4.
 * Patch 3/4 discovers 1/4 was no good. Patch 4/4 fixes 3/4.

 * Fix based on upstream code (96f8146) + subsequent patches.
 * Followed this code development until the issue was addressed.

[Changelog]

  * Added 0001-libmultipath-update-waiter-handling.patch (LP: #1354114)
  * Added 0002-Race-condition-when-calling-stop_waiter_thread.patch (LP: 
#1354114)
  * Added 0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch (LP: 
#1354114)
  * Added 0004-Fix-race-condition-in-stop_waiter_thread.patch (LP: #1354114)

[Fix]

Fix from upstream: 

The current 'waiter' structure accesses fields which belong
to the main 'mpp' structure, which has a totally different
lifetime.

Thanks for considering the patch.

- -- System Information:
Debian Release: jessie/sid
  APT prefers utopic-updates
  APT policy: (500, 'utopic-updates'), (500, 'utopic')
Architecture: amd64 (x86_64)

Kernel: Linux 3.13.0-32-generic (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL 
set to en_US.UTF-8)
Shell: /bin/sh linked to /bin/dash

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4

iQEcBAEBCgAGBQJT5TQCAAoJEAynk4KHaD/A37EH/1D3la+rTd0Ag2hYMqh5LI+M
q+inhcIuu57LmLKLbOYdgyBeQp+2A8YdYSlQ2qYd02KmamBJdI/4ZQDgv6VWaGkW
uOES4ZBOI58lVHhjZS+KtYNvHgVDM4pW7uByOYVD6bF5hH8K4HgtLOzrl8rWZ6AS
4LRSGqA2YBj9QsS9abevlJt6MiRrxWR5iNleyY+mmoWMzjXlmiBWh8m2mTj7FdlG
vGiWlPb1joC8Dm7Vji7ra3tCB3tHKj/PA1LgoZ+/naAm/V7eBTl/JSuuXNZUOehF
0uI1CGdVtLWmXeAiinntcR7CWlqX4hwUxUfO7yj9d8JuykECVvdNr88jkndKF7Y=
=mxnI
-----END PGP SIGNATURE-----
diff -Nru multipath-tools-0.4.9/debian/changelog multipath-tools-0.4.9/debian/changelog
diff -Nru multipath-tools-0.4.9/debian/control multipath-tools-0.4.9/debian/control
--- multipath-tools-0.4.9/debian/control	2013-01-29 22:55:25.000000000 -0200
+++ multipath-tools-0.4.9/debian/control	2014-08-08 17:31:37.000000000 -0300
@@ -1,8 +1,7 @@
 Source: multipath-tools
 Section: admin
 Priority: extra
-Maintainer: Ubuntu Developers <ubuntu-devel-disc...@lists.ubuntu.com>
-XSBC-Original-Maintainer: Debian LVM Team <pkg-lvm-maintain...@lists.alioth.debian.org>
+Maintainer: Debian LVM Team <pkg-lvm-maintain...@lists.alioth.debian.org>
 Uploaders: Guido Günther <a...@sigxcpu.org>, Ritesh Raj Sarraf <r...@debian.org>
 Build-Depends: debhelper (>= 7.0.17ubuntu2), po-debconf, libdevmapper-dev (>= 2:1.02.20), libreadline-dev, libaio-dev
 Vcs-Git: git://git.debian.org/git/pkg-lvm/multipath-tools.git
diff -Nru multipath-tools-0.4.9/debian/patches/0001-libmultipath-update-waiter-handling.patch multipath-tools-0.4.9/debian/patches/0001-libmultipath-update-waiter-handling.patch
--- multipath-tools-0.4.9/debian/patches/0001-libmultipath-update-waiter-handling.patch	1969-12-31 21:00:00.000000000 -0300
+++ multipath-tools-0.4.9/debian/patches/0001-libmultipath-update-waiter-handling.patch	2014-08-08 17:23:18.000000000 -0300
@@ -0,0 +1,285 @@
+Description: [PATCH 1/4] libmultipath: update waiter handling
+
+The current 'waiter' structure accesses fields which belong
+to the main 'mpp' structure, which has a totally different
+lifetime. With this patch most of these dependencies are
+removed and the 'waiter' structure can run independently
+of the main 'mpp' structure, reducing the risk of
+use-after-free faults.
+
+Signed-off-by: Hannes Reinecke <h...@suse.de>
+
+Origin: upstream, commit: 96f8146
+Author: Hannes Reinecke <h...@suse.de>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1354114
+Last-Update: 2014-08-08
+---
+ libmultipath/structs.c |    7 ----
+ libmultipath/structs.h |    6 ++--
+ libmultipath/waiter.c  |   87 +++++++++++++++++++++++++++---------------------
+ libmultipath/waiter.h  |    5 ++-
+ 4 files changed, 54 insertions(+), 51 deletions(-)
+
+diff --git a/libmultipath/structs.c b/libmultipath/structs.c
+index a4b86d2..58e82e0 100644
+--- a/libmultipath/structs.c
++++ b/libmultipath/structs.c
+@@ -15,7 +15,6 @@
+ #include "debug.h"
+ #include "structs_vec.h"
+ #include "blacklist.h"
+-#include "waiter.h"
+ #include "prio.h"
+ 
+ struct path *
+@@ -175,12 +174,6 @@ free_multipath (struct multipath * mpp, int free_paths)
+ 		mpp->dmi = NULL;
+ 	}
+ 
+-	/*
+-	 * better own vecs->lock here
+-	 */
+-	if (mpp->waiter)
+-		((struct event_thread *)mpp->waiter)->mpp = NULL;
+-
+ 	free_pathvec(mpp->paths, free_paths);
+ 	free_pgvec(mpp->pg, free_paths);
+ 	FREE_PTR(mpp->mpcontext);
+diff --git a/libmultipath/structs.h b/libmultipath/structs.h
+index c559838..e6faff0 100644
+--- a/libmultipath/structs.h
++++ b/libmultipath/structs.h
+@@ -4,9 +4,9 @@
+ #include <sys/types.h>
+ 
+ #define WWID_SIZE		128
+-#define SERIAL_SIZE		64
+-#define NODE_NAME_SIZE		19
+-#define PATH_STR_SIZE  		16
++#define SERIAL_SIZE		65
++#define NODE_NAME_SIZE		65
++#define PATH_STR_SIZE		16
+ #define PARAMS_SIZE		1024
+ #define FILE_NAME_SIZE		256
+ #define CALLOUT_MAX_SIZE	128
+diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c
+index 4fb2cff..0be5d21 100644
+--- a/libmultipath/waiter.c
++++ b/libmultipath/waiter.c
+@@ -28,38 +28,24 @@ struct event_thread *alloc_waiter (void)
+ 	struct event_thread *wp;
+ 
+ 	wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
++	memset(wp, 0, sizeof(struct event_thread));
++	pthread_mutex_init(&wp->lock, NULL);
+ 
+ 	return wp;
+ }
+ 
+-void free_waiter (void *data)
++void signal_waiter (void *data)
+ {
+-	sigset_t old;
+ 	struct event_thread *wp = (struct event_thread *)data;
+ 
+-	/*
+-	 * indicate in mpp that the wp is already freed storage
+-	 */
+-	block_signal(SIGHUP, &old);
+-	lock(wp->vecs->lock);
+-
+-	if (wp->mpp)
+-		/*
+-		 * be careful, mpp may already be freed -- null if so
+-		 */
+-		wp->mpp->waiter = NULL;
+-	else
+-		/*
+-		* This is OK condition during shutdown.
+-		*/
+-		condlog(3, "free_waiter, mpp freed before wp=%p (%s).", wp, wp->mapname);
+-
+-	unlock(wp->vecs->lock);
+-	pthread_sigmask(SIG_SETMASK, &old, NULL);
+-
+-	if (wp->dmt)
+-		dm_task_destroy(wp->dmt);
++	pthread_mutex_lock(&wp->lock);
++	memset(wp->mapname, 0, WWID_SIZE);
++	pthread_mutex_unlock(&wp->lock);
++}
+ 
++void free_waiter (struct event_thread *wp)
++{
++	pthread_mutex_destroy(&wp->lock);
+ 	FREE(wp);
+ }
+ 
+@@ -72,9 +58,16 @@ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 		condlog(3, "%s: no waiter thread", mpp->alias);
+ 		return;
+ 	}
++	if (wp->thread == (pthread_t)0) {
++		condlog(3, "%s: event checker thread already stopped",
++			mpp->alias);
++		return;
++	}
+ 	thread = wp->thread;
+-	condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread);
++	wp->thread = (pthread_t)0;
++	mpp->waiter = NULL;
+ 
++	condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread);
+ 	pthread_kill(thread, SIGUSR1);
+ }
+ 
+@@ -96,49 +89,61 @@ static sigset_t unblock_signals(void)
+ int waiteventloop (struct event_thread *waiter)
+ {
+ 	sigset_t set;
++	struct dm_task *dmt = NULL;
+ 	int event_nr;
+ 	int r;
+ 
++	pthread_mutex_lock(&waiter->lock);
+ 	if (!waiter->event_nr)
+ 		waiter->event_nr = dm_geteventnr(waiter->mapname);
+ 
+-	if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
++	if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
+ 		condlog(0, "%s: devmap event #%i dm_task_create error",
+ 				waiter->mapname, waiter->event_nr);
++		pthread_mutex_unlock(&waiter->lock);
+ 		return 1;
+ 	}
+ 
+-	if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
++	if (!dm_task_set_name(dmt, waiter->mapname)) {
+ 		condlog(0, "%s: devmap event #%i dm_task_set_name error",
+ 				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(waiter->dmt);
++		dm_task_destroy(dmt);
++		pthread_mutex_unlock(&waiter->lock);
+ 		return 1;
+ 	}
+ 
+-	if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
++	if (waiter->event_nr && !dm_task_set_event_nr(dmt,
+ 						      waiter->event_nr)) {
+ 		condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
+ 				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(waiter->dmt);
++		dm_task_destroy(dmt);
++		pthread_mutex_unlock(&waiter->lock);
+ 		return 1;
+ 	}
++	pthread_mutex_unlock(&waiter->lock);
+ 
+-	dm_task_no_open_count(waiter->dmt);
++	dm_task_no_open_count(dmt);
+ 
+ 	/* accept wait interruption */
+ 	set = unblock_signals();
+ 
+ 	/* wait */
+-	r = dm_task_run(waiter->dmt);
++	r = dm_task_run(dmt);
+ 
+ 	/* wait is over : event or interrupt */
+ 	pthread_sigmask(SIG_SETMASK, &set, NULL);
+ 
+-	if (!r) /* wait interrupted by signal */
++	dm_task_destroy(dmt);
++
++	if (!r)	/* wait interrupted by signal */
+ 		return -1;
+ 
+-	dm_task_destroy(waiter->dmt);
+-	waiter->dmt = NULL;
++	pthread_mutex_lock(&waiter->lock);
++	if (!strlen(waiter->mapname)) {
++		/* waiter should exit */
++		pthread_mutex_unlock(&waiter->lock);
++		return -1;
++	}
+ 	waiter->event_nr++;
+ 
+ 	/*
+@@ -167,16 +172,20 @@ int waiteventloop (struct event_thread *waiter)
+ 		if (r) {
+ 			condlog(2, "%s: event checker exit",
+ 				waiter->mapname);
++			pthread_mutex_unlock(&waiter->lock);
+ 			return -1; /* stop the thread */
+ 		}
+ 
+ 		event_nr = dm_geteventnr(waiter->mapname);
+ 
+-		if (waiter->event_nr == event_nr)
++		if (waiter->event_nr == event_nr) {
++			pthread_mutex_unlock(&waiter->lock);
+ 			return 1; /* upon problem reschedule 1s later */
++		}
+ 
+ 		waiter->event_nr = event_nr;
+ 	}
++	pthread_mutex_unlock(&waiter->lock);
+ 	return -1; /* never reach there */
+ }
+ 
+@@ -188,7 +197,7 @@ void *waitevent (void *et)
+ 	mlockall(MCL_CURRENT | MCL_FUTURE);
+ 
+ 	waiter = (struct event_thread *)et;
+-	pthread_cleanup_push(free_waiter, et);
++	pthread_cleanup_push(signal_waiter, et);
+ 
+ 	block_signal(SIGUSR1, NULL);
+ 	block_signal(SIGHUP, NULL);
+@@ -202,6 +211,7 @@ void *waitevent (void *et)
+ 	}
+ 
+ 	pthread_cleanup_pop(1);
++	free_waiter(waiter);
+ 	return NULL;
+ }
+ 
+@@ -217,10 +227,11 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 	if (!wp)
+ 		goto out;
+ 
++	pthread_mutex_lock(&wp->lock);
+ 	mpp->waiter = (void *)wp;
+ 	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+ 	wp->vecs = vecs;
+-	wp->mpp = mpp;
++	pthread_mutex_unlock(&wp->lock);
+ 
+ 	if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
+ 		condlog(0, "%s: cannot create event checker", wp->mapname);
+diff --git a/libmultipath/waiter.h b/libmultipath/waiter.h
+index ab362d1..28a6974 100644
+--- a/libmultipath/waiter.h
++++ b/libmultipath/waiter.h
+@@ -4,16 +4,15 @@
+ extern pthread_attr_t waiter_attr;
+ 
+ struct event_thread {
+-	struct dm_task *dmt;
+ 	pthread_t thread;
++	pthread_mutex_t lock;
+ 	int event_nr;
+ 	char mapname[WWID_SIZE];
+ 	struct vectors *vecs;
+-	struct multipath *mpp;
+ };
+ 
+ struct event_thread * alloc_waiter (void);
+-void free_waiter (void *data);
++void signal_waiter (void *data);
+ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs);
+ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs);
+ int waiteventloop (struct event_thread *waiter);
+-- 
+1.7.9.5
+
diff -Nru multipath-tools-0.4.9/debian/patches/0002-Race-condition-when-calling-stop_waiter_thread.patch multipath-tools-0.4.9/debian/patches/0002-Race-condition-when-calling-stop_waiter_thread.patch
--- multipath-tools-0.4.9/debian/patches/0002-Race-condition-when-calling-stop_waiter_thread.patch	1969-12-31 21:00:00.000000000 -0300
+++ multipath-tools-0.4.9/debian/patches/0002-Race-condition-when-calling-stop_waiter_thread.patch	2014-08-08 17:23:26.000000000 -0300
@@ -0,0 +1,95 @@
+Description: [PATCH 2/4] Race condition when calling stop_waiter_thread()
+
+We cannot access the waiter structure from other threads as
+the lifetime is totally different and it might be deleted
+at any time.
+So we better store the pthread id in the calling thread and
+just send a signal to the thread.
+
+References: bnc#642846
+
+Signed-off-by: Hannes Reinecke <h...@suse.de>
+
+Origin: upstream, commit: c301a3f
+Author: Hannes Reinecke <h...@suse.de>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1354114
+Last-Update: 2014-08-08
+
+---
+ libmultipath/structs.h |    2 +-
+ libmultipath/waiter.c  |   23 +++++++----------------
+ 2 files changed, 8 insertions(+), 17 deletions(-)
+
+diff --git a/libmultipath/structs.h b/libmultipath/structs.h
+index e6faff0..b44926a 100644
+--- a/libmultipath/structs.h
++++ b/libmultipath/structs.h
+@@ -193,7 +193,7 @@ struct multipath {
+ 	struct hwentry * hwe;
+ 
+ 	/* threads */
+-	void * waiter;
++	pthread_t waiter;
+ 
+ 	/* stats */
+ 	unsigned int stat_switchgroup;
+diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c
+index 0be5d21..1a54d93 100644
+--- a/libmultipath/waiter.c
++++ b/libmultipath/waiter.c
+@@ -51,24 +51,15 @@ void free_waiter (struct event_thread *wp)
+ 
+ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ {
+-	struct event_thread *wp = (struct event_thread *)mpp->waiter;
+-	pthread_t thread;
+-
+-	if (!wp) {
+-		condlog(3, "%s: no waiter thread", mpp->alias);
+-		return;
+-	}
+-	if (wp->thread == (pthread_t)0) {
++	if (mpp->waiter == (pthread_t)0) {
+ 		condlog(3, "%s: event checker thread already stopped",
+ 			mpp->alias);
+ 		return;
+ 	}
+-	thread = wp->thread;
+-	wp->thread = (pthread_t)0;
+-	mpp->waiter = NULL;
+-
+-	condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread);
+-	pthread_kill(thread, SIGUSR1);
++	condlog(2, "%s: stop event checker thread (%lu)", mpp->alias,
++		mpp->waiter);
++	pthread_kill(mpp->waiter, SIGUSR1);
++	mpp->waiter = (pthread_t)0;
+ }
+ 
+ static sigset_t unblock_signals(void)
+@@ -228,7 +219,6 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 		goto out;
+ 
+ 	pthread_mutex_lock(&wp->lock);
+-	mpp->waiter = (void *)wp;
+ 	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+ 	wp->vecs = vecs;
+ 	pthread_mutex_unlock(&wp->lock);
+@@ -237,12 +227,13 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 		condlog(0, "%s: cannot create event checker", wp->mapname);
+ 		goto out1;
+ 	}
++	mpp->waiter = wp->thread;
+ 	condlog(2, "%s: event checker started", wp->mapname);
+ 
+ 	return 0;
+ out1:
+ 	free_waiter(wp);
+-	mpp->waiter = NULL;
++	mpp->waiter = (pthread_t)0;
+ out:
+ 	condlog(0, "failed to start waiter thread");
+ 	return 1;
+-- 
+1.7.9.5
+
diff -Nru multipath-tools-0.4.9/debian/patches/0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch multipath-tools-0.4.9/debian/patches/0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch
--- multipath-tools-0.4.9/debian/patches/0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch	1969-12-31 21:00:00.000000000 -0300
+++ multipath-tools-0.4.9/debian/patches/0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch	2014-08-08 17:23:35.000000000 -0300
@@ -0,0 +1,243 @@
+Description: [PATCH 3/4] multipath: clean up code for stopping the waiter threads
+
+The way multipathd currently stops the waiter threads needs some work.
+Right now they are stopped by being sent the SIGUSR1 signal. However their
+cleanup code assumes that they are being cancelled, just like all the other
+threads are.  There's no reason for them to be so unnecessarily
+complicated and different from the other threads
+
+This patch does a couple of things.  First, it removes the mutex from
+the event_thread.  This wasn't doing anything. It was designed to protect
+the wp->mapname variable, which the waiter threads were checking to see
+if they should quit. However, the mutex was only ever being used by the
+thread itself, and it clearly didn't need to serialize with itself.  Also,
+the function to clear the mapname, signal_waiter(), was set with
+pthread_cleanup_push(), which never got called early, since the threads
+weren't being cancelled.  Thus, the mapname never got cleared
+until the pthreads were about to shut down.
+
+The patch also rips out all the signal stopping code, and just uses
+pthread_cancel.  There already are cancellation points in the waiter
+thread code. Between the cancellation points, both explicit and implicit,
+and the fact that the waiter threads will never be killed except when the
+killer is holding the vecs lock, there shouldn't be any place where the
+waiter thread can access freed data.
+
+To make sure the waiter thread cleans itself up properly, the dmt
+has been moved into the event_thread structure, and is destroyed in
+free_waiter() if necessary.
+
+Signed-off-by: Benjamin Marzinski <bmarz...@redhat.com>
+
+Origin: upstream, commit: e1fcc59
+Author: Benjamin Marzinski <bmarz...@redhat.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1354114
+Last-Update: 2014-08-08
+
+---
+ libmultipath/waiter.c |   74 ++++++++++++-------------------------------------
+ libmultipath/waiter.h |    4 +--
+ 2 files changed, 19 insertions(+), 59 deletions(-)
+
+diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c
+index 1a54d93..2323b68 100644
+--- a/libmultipath/waiter.c
++++ b/libmultipath/waiter.c
+@@ -29,23 +29,17 @@ struct event_thread *alloc_waiter (void)
+ 
+ 	wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
+ 	memset(wp, 0, sizeof(struct event_thread));
+-	pthread_mutex_init(&wp->lock, NULL);
+ 
+ 	return wp;
+ }
+ 
+-void signal_waiter (void *data)
++void free_waiter (void *data)
+ {
+ 	struct event_thread *wp = (struct event_thread *)data;
+ 
+-	pthread_mutex_lock(&wp->lock);
+-	memset(wp->mapname, 0, WWID_SIZE);
+-	pthread_mutex_unlock(&wp->lock);
+-}
++	if (wp->dmt)
++		dm_task_destroy(wp->dmt);
+ 
+-void free_waiter (struct event_thread *wp)
+-{
+-	pthread_mutex_destroy(&wp->lock);
+ 	FREE(wp);
+ }
+ 
+@@ -58,83 +52,56 @@ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 	}
+ 	condlog(2, "%s: stop event checker thread (%lu)", mpp->alias,
+ 		mpp->waiter);
+-	pthread_kill(mpp->waiter, SIGUSR1);
++	pthread_cancel(mpp->waiter);
+ 	mpp->waiter = (pthread_t)0;
+ }
+ 
+-static sigset_t unblock_signals(void)
+-{
+-	sigset_t set, old;
+-
+-	sigemptyset(&set);
+-	sigaddset(&set, SIGHUP);
+-	sigaddset(&set, SIGUSR1);
+-	pthread_sigmask(SIG_UNBLOCK, &set, &old);
+-	return old;
+-}
+-
+ /*
+  * returns the reschedule delay
+  * negative means *stop*
+  */
+ int waiteventloop (struct event_thread *waiter)
+ {
+-	sigset_t set;
+-	struct dm_task *dmt = NULL;
+ 	int event_nr;
+ 	int r;
+ 
+-	pthread_mutex_lock(&waiter->lock);
+ 	if (!waiter->event_nr)
+ 		waiter->event_nr = dm_geteventnr(waiter->mapname);
+ 
+-	if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
++	if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
+ 		condlog(0, "%s: devmap event #%i dm_task_create error",
+ 				waiter->mapname, waiter->event_nr);
+-		pthread_mutex_unlock(&waiter->lock);
+ 		return 1;
+ 	}
+ 
+-	if (!dm_task_set_name(dmt, waiter->mapname)) {
++	if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
+ 		condlog(0, "%s: devmap event #%i dm_task_set_name error",
+ 				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(dmt);
+-		pthread_mutex_unlock(&waiter->lock);
++		dm_task_destroy(waiter->dmt);
++		waiter->dmt = NULL;
+ 		return 1;
+ 	}
+ 
+-	if (waiter->event_nr && !dm_task_set_event_nr(dmt,
++	if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
+ 						      waiter->event_nr)) {
+ 		condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
+ 				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(dmt);
+-		pthread_mutex_unlock(&waiter->lock);
++		dm_task_destroy(waiter->dmt);
++		waiter->dmt = NULL;
+ 		return 1;
+ 	}
+-	pthread_mutex_unlock(&waiter->lock);
+ 
+-	dm_task_no_open_count(dmt);
+-
+-	/* accept wait interruption */
+-	set = unblock_signals();
++	dm_task_no_open_count(waiter->dmt);
+ 
+ 	/* wait */
+-	r = dm_task_run(dmt);
+-
+-	/* wait is over : event or interrupt */
+-	pthread_sigmask(SIG_SETMASK, &set, NULL);
++	r = dm_task_run(waiter->dmt);
+ 
+-	dm_task_destroy(dmt);
++	dm_task_destroy(waiter->dmt);
++	waiter->dmt = NULL;
+ 
+ 	if (!r)	/* wait interrupted by signal */
+ 		return -1;
+ 
+-	pthread_mutex_lock(&waiter->lock);
+-	if (!strlen(waiter->mapname)) {
+-		/* waiter should exit */
+-		pthread_mutex_unlock(&waiter->lock);
+-		return -1;
+-	}
+ 	waiter->event_nr++;
+ 
+ 	/*
+@@ -163,20 +130,16 @@ int waiteventloop (struct event_thread *waiter)
+ 		if (r) {
+ 			condlog(2, "%s: event checker exit",
+ 				waiter->mapname);
+-			pthread_mutex_unlock(&waiter->lock);
+ 			return -1; /* stop the thread */
+ 		}
+ 
+ 		event_nr = dm_geteventnr(waiter->mapname);
+ 
+-		if (waiter->event_nr == event_nr) {
+-			pthread_mutex_unlock(&waiter->lock);
++		if (waiter->event_nr == event_nr)
+ 			return 1; /* upon problem reschedule 1s later */
+-		}
+ 
+ 		waiter->event_nr = event_nr;
+ 	}
+-	pthread_mutex_unlock(&waiter->lock);
+ 	return -1; /* never reach there */
+ }
+ 
+@@ -188,7 +151,7 @@ void *waitevent (void *et)
+ 	mlockall(MCL_CURRENT | MCL_FUTURE);
+ 
+ 	waiter = (struct event_thread *)et;
+-	pthread_cleanup_push(signal_waiter, et);
++	pthread_cleanup_push(free_waiter, et);
+ 
+ 	block_signal(SIGUSR1, NULL);
+ 	block_signal(SIGHUP, NULL);
+@@ -202,7 +165,6 @@ void *waitevent (void *et)
+ 	}
+ 
+ 	pthread_cleanup_pop(1);
+-	free_waiter(waiter);
+ 	return NULL;
+ }
+ 
+@@ -218,10 +180,8 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 	if (!wp)
+ 		goto out;
+ 
+-	pthread_mutex_lock(&wp->lock);
+ 	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+ 	wp->vecs = vecs;
+-	pthread_mutex_unlock(&wp->lock);
+ 
+ 	if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
+ 		condlog(0, "%s: cannot create event checker", wp->mapname);
+diff --git a/libmultipath/waiter.h b/libmultipath/waiter.h
+index 28a6974..a1f57fb 100644
+--- a/libmultipath/waiter.h
++++ b/libmultipath/waiter.h
+@@ -4,15 +4,15 @@
+ extern pthread_attr_t waiter_attr;
+ 
+ struct event_thread {
++	struct dm_task *dmt;
+ 	pthread_t thread;
+-	pthread_mutex_t lock;
+ 	int event_nr;
+ 	char mapname[WWID_SIZE];
+ 	struct vectors *vecs;
+ };
+ 
+ struct event_thread * alloc_waiter (void);
+-void signal_waiter (void *data);
++void free_waiter (void *data);
+ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs);
+ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs);
+ int waiteventloop (struct event_thread *waiter);
+-- 
+1.7.9.5
+
diff -Nru multipath-tools-0.4.9/debian/patches/0004-Fix-race-condition-in-stop_waiter_thread.patch multipath-tools-0.4.9/debian/patches/0004-Fix-race-condition-in-stop_waiter_thread.patch
--- multipath-tools-0.4.9/debian/patches/0004-Fix-race-condition-in-stop_waiter_thread.patch	1969-12-31 21:00:00.000000000 -0300
+++ multipath-tools-0.4.9/debian/patches/0004-Fix-race-condition-in-stop_waiter_thread.patch	2014-08-08 17:23:43.000000000 -0300
@@ -0,0 +1,43 @@
+Description: [PATCH 4/4] Fix race condition in stop_waiter_thread()
+
+The signal handler might run before we had a chance to
+set the 'waiter' context to '0', so better do it previously.
+
+Signed-off-by: Hannes Reinecke <h...@suse.de>
+
+Origin: upstream, commit: af4fd6d
+Author: Hannes Reinecke <h...@suse.de>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1354114
+Last-Update: 2014-08-08
+
+---
+ libmultipath/waiter.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c
+index 2323b68..05050f5 100644
+--- a/libmultipath/waiter.c
++++ b/libmultipath/waiter.c
+@@ -45,6 +45,8 @@ void free_waiter (void *data)
+ 
+ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ {
++	pthread_t thread;
++
+ 	if (mpp->waiter == (pthread_t)0) {
+ 		condlog(3, "%s: event checker thread already stopped",
+ 			mpp->alias);
+@@ -52,8 +54,9 @@ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+ 	}
+ 	condlog(2, "%s: stop event checker thread (%lu)", mpp->alias,
+ 		mpp->waiter);
+-	pthread_cancel(mpp->waiter);
++	thread = mpp->waiter;
+ 	mpp->waiter = (pthread_t)0;
++	pthread_cancel(thread);
+ }
+ 
+ /*
+-- 
+1.7.9.5
+
diff -Nru multipath-tools-0.4.9/debian/patches/series multipath-tools-0.4.9/debian/patches/series
--- multipath-tools-0.4.9/debian/patches/series	2013-01-29 23:02:49.000000000 -0200
+++ multipath-tools-0.4.9/debian/patches/series	2014-08-08 16:46:23.000000000 -0300
@@ -10,3 +10,7 @@
 1001--fix-linking-command.patch
 0009-fix-delim.patch
 0010-fix-extended-partitions.patch
+0001-libmultipath-update-waiter-handling.patch
+0002-Race-condition-when-calling-stop_waiter_thread.patch
+0003-multipath-clean-up-code-for-stopping-the-waiter-thre.patch
+0004-Fix-race-condition-in-stop_waiter_thread.patch

Reply via email to