2014/07/15 3:39、Daniel Stenberg <dan...@haxx.se> のメール: > On Sun, 13 Jul 2014, yousuke.kim...@gmail.com wrote: > >> This issue was reported in http://curl.haxx.se/mail/lib-2014-02/0184.html. >> Unfortunately the reporter didn't suggest his patch. >> >> This issue is caused by locking CURL_LOCK_DATA_COOKIE with specified cookie >> files. In this case, CURL_LOCK_DATA_COOKIE is locked before executing >> "FLUSH", and then Curl_cookie_loadfiles locks CURL_LOCK_DATA_COOKIE. That is >> deadlock. > > Thanks for the patch. My reading of it says it is incorrect though: > > - for FLUSH you (still) take the lock before Curl_flush_cookies() is called > and then within Curl_flush_cookies() it locks the same mutex again. > > Shouldn't the patch then just have those lines removed, like this?
To understand this issue more deeply, I'd like to explain about the deadlock issue. The deadlock sequence is as follows: 1) curl share object is being used. 2) To load cookie files, CURLOPT_COOKIEFILE is set to specify the cookie by using curl_easy_setopt() 3) To flush cookie, CURLOPT_COOKIELIST and "FLUSH" is set by using curl_easy_setopt(). Then, in Curl_setopt() : url.c Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); is invoked, and CURL_LOCK_DATA_COOKIE is locked. Next, "FLUSH", Curl_flush_cookies() is invoked. The function is written as follows: void Curl_flush_cookies(struct SessionHandle *data, int cleanup) : cookie.c { if(data->set.str[STRING_COOKIEJAR]) { if(data->change.cookielist) { /* If there is a list of cookie files to read, do it first so that we have all the told files read before we write the new jar. Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ Curl_cookie_loadfiles(data); } .... As CURLOPT_COOKIEFILE is specified, Curl_cookie_loadfiles() is invoked. Curl_cookie_loadfiles() locks CURL_LOCK_DATA_COOKIE by itself. Then CURL_LOCK_DATA_COOKIE is locked twice in this situation. void Curl_cookie_loadfiles(struct SessionHandle *data) { struct curl_slist *list = data->change.cookielist; if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); // <--- The cookie mutex is locked twice here. while(list) { data->cookies = Curl_cookie_init(data, list->data, data->cookies, data->set.cookiesession); list = list->next; } curl_slist_free_all(data->change.cookielist); /* clean up list */ data->change.cookielist = NULL; /* don't do this again! */ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } } I guess there are other patches to fix this issue. In my patch, only url.c will be modified. If the mutex is created with recursive option like PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, my patch is not necessary. Does libcurl expect that the mutex for CURL_LOCK_DATA_COOKIE is created as recursive mutex? I think libcurl should consider where CURL_LOCK_DATA_COOKIE is locked to avoid like this issue. Thanks, Yousuke > > From fcf80e75c39a54b8be53d7040fe8970c3b13a542 Mon Sep 17 00:00:00 2001 > From: Daniel Stenberg <dan...@haxx.se> > Date: Mon, 14 Jul 2014 20:38:18 +0200 > Subject: [PATCH] cookie: avoid mutex deadlock > > ... by removing the extra mutex locks around th call to > Curl_flush_cookies() which takes care of the locking itself already. > --- > lib/url.c | 11 +++++++---- > 1 file changed, 7 insertions(+), 4 deletions(-) > > diff --git a/lib/url.c b/lib/url.c > index 87ebe00..1d05975 100644 > --- a/lib/url.c > +++ b/lib/url.c > @@ -1167,22 +1167,24 @@ CURLcode Curl_setopt(struct SessionHandle *data, > CURLoption option, > argptr = va_arg(param, char *); > > if(argptr == NULL) > break; > > - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); > - > if(Curl_raw_equal(argptr, "ALL")) { > /* clear all cookies */ > + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); > Curl_cookie_clearall(data->cookies); > + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); > } > else if(Curl_raw_equal(argptr, "SESS")) { > /* clear session cookies */ > + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); > Curl_cookie_clearsess(data->cookies); > + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); > } > else if(Curl_raw_equal(argptr, "FLUSH")) { > - /* flush cookies to file */ > + /* flush cookies to file, takes care of the locking */ > Curl_flush_cookies(data, 0); > } > else { > if(!data->cookies) > /* if cookie engine was not running, activate it */ > @@ -1191,23 +1193,24 @@ CURLcode Curl_setopt(struct SessionHandle *data, > CURLoption option, > argptr = strdup(argptr); > if(!argptr) { > result = CURLE_OUT_OF_MEMORY; > } > else { > + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, > CURL_LOCK_ACCESS_SINGLE); > > if(checkprefix("Set-Cookie:", argptr)) > /* HTTP Header format line */ > Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); > > else > /* Netscape format line */ > Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); > > + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); > free(argptr); > } > } > - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); > > break; > #endif /* CURL_DISABLE_COOKIES */ > > case CURLOPT_HTTPGET: > -- > 2.0.1 > > > -- > > / daniel.haxx.se > ------------------------------------------------------------------- > List admin: http://cool.haxx.se/list/listinfo/curl-library > Etiquette: http://curl.haxx.se/mail/etiquette.html ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html