Opened 8 years ago

Closed 8 years ago

Last modified 7 years ago

#4457 closed defect (fixed)

Assertion at startup on Windows 7 when socketpair fails

Reported by: Vigdis Owned by:
Priority: High Milestone: Tor: 0.2.2.x-final
Component: Core Tor/Tor Version: Tor: 0.2.2.34
Severity: Keywords: tor-client
Cc: Actual Points:
Parent ID: Points:
Reviewer: Sponsor:

Description

Hello,
I talked to "Sebastian" and "helix" on the IRC, and they advised me to report my bug.
I used to run vidalia-bundle-0.2.1.28-0.2.10 but since a security problem was known I had to update it. I installed vidalia-bundle-0.2.2.33-0.2.15 and tor quit at the begining.
The error message are
-[Warning] Warning from libevent: evsig_init: socketpair: Permission denied [WSAEACCES ]
-[Warning] Warning from libevent: evthread_make_base_notifiable: socketpair: Permission denied [WSAEACCES ]
-[Error] Error from libevent: event.c:1413: Assertion base failed in event_base_get_method
The new alpha version is working correctly.
If you need to contact me, send me a mail, it's in my info :)

Child Tickets

Change History (14)

comment:1 Changed 8 years ago by Sebastian

Component: Tor BridgeTor Client
Milestone: Vidalia: 0.2.15Tor: 0.2.2.x-final
Priority: normalmajor
Summary: tor quitAssertion at startup on Windows 7 when socketpair fails
Version: Tor: 0.2.2.33Tor: 0.2.2.34

If I understand it correctly, what happens here is the following:

We call tor_libevent_initialize(), where we have
the_event_base = event_base_new_with_config(cfg);.

A little below, we call

 log(LOG_NOTICE, LD_GENERAL,
     "Initialized libevent version %s using method %s. Good.",
     event_get_version(), tor_libevent_get_method());

with the call to tor_libevent_get_method(), which calls
event_base_get_method(the_event_base);.

The call asserts, because the_event_base is NULL. It is NULL because
inside of event_base_new_with_config(), we have this section of code:

#ifndef _EVENT_DISABLE_THREAD_SUPPORT
       if (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK)) {
               int r;
               EVTHREAD_ALLOC_LOCK(base->th_base_lock,
                   EVTHREAD_LOCKTYPE_RECURSIVE);
               base->defer_queue.lock = base->th_base_lock;
               EVTHREAD_ALLOC_COND(base->current_event_cond);
               r = evthread_make_base_notifiable(base);
               if (r<0) {
                       event_base_free(base);
                       return NULL;
               }
       }
#endif

evthread_make_base_notifiable() returns -1 here, because the
evutil_socketpair() call fails.

The first part would be checking that we actually get an event base back
from event_base_new_with_config(), and telling the user about the cause
of error. But maybe we can do even better with a new libevent version
that doesn't require the socketpair to work, or we can get away with not
requiring it from Tor?

Thanks frosty_un for looking at this with me.

comment:2 Changed 8 years ago by nickm

Hm. Several possibilities here, not all conflicting.

  • Set event_base_flag_nolock on newly constructed bases for situations where we know we don't need to access a base from multiple threads. Right now that's "every situation", but that won't be the case forever (maybe).
  • Or maybe start with trying to create it regularly, and retry with nolock if that fails.
  • Detect failures from event_base_new_with_config.
  • event_base_new_with_config() should log in this case.
  • Libevent should decline to "enable threading" on a base in the case when no threading callbacks have been registered with Libevent.

Information:

  • With nearly all event backends, the only way to make the poll function wake up is to make one of the sockets become active. On Linux, you do this with a eventfd() ; everyplace else, you use a socketpair.
  • Windows doesn't have real socketpairs, so you've got to fake them by connecting to localhost... which some firewalls get annoyed by.
  • Libevent needs to be able to wake up the poll function to handle inter-thread communication and to implement signals (on non-kqueue, non-signalfd backends). Tor uses this ability for cpuworkers and for signals.
  • The alternative methods of waking up a poll function are "Use a different poll function" (not 100% possible on Windows with current libevents, though we're moving in that direction), "Don't let your poll functions wait longer than some small amount; accept that amount of time as acceptable latency in inter-thread communication" (ugly, cpu-intensive), and "and then a miracle occurs".

comment:3 in reply to:  2 Changed 8 years ago by nickm

Status: newneeds_review

Replying to nickm:

Hm. Several possibilities here, not all conflicting.

  • Set event_base_flag_nolock on newly constructed bases for situations where we know we don't need to access a base from multiple threads. Right now that's "every situation", but that won't be the case forever (maybe).

Done in 7363eae13cb8fe in my public repository, branch bug4457_022

Actually, this is only workable for 0.2.2; for 0.2.3 it will sometimes fail, and we'll need to do this one instead:

  • Or maybe start with trying to create it regularly, and retry with nolock if that fails.

Done as 7be50c26e8b2ec0bd in my public repository, branch bug4457_master.

  • Detect failures from event_base_new_with_config.

Done in 0f6c02161793fa1022fe7 in my public repository, branch bug4457_022

  • event_base_new_with_config() should log in this case.

Done in libevent in 4e797f388f736756b1531ce47ef210bd2119515f. Should be in libevent 2.0.16-stable when it comes out (like, within 48 hours I expect).

  • Libevent should decline to "enable threading" on a base in the case when no threading callbacks have been registered with Libevent.

Done in libevent in e78741332985be96f2a44e71226b4adfe55aee7e. Should be in libevent 2.0.16-stable when it comes out (like, within 48 hours I expect).

comment:4 Changed 8 years ago by nickm

By the way, you can test this out by forcing evutil_socketpair() to always fail: just add "if (1) return -1;" at the top of the evutil_socketpair() function in the file "evutil.c" before building libevent.

comment:5 Changed 8 years ago by Sebastian

Hrm. I can't get this to fail on OS X using your suggested addition in evutil_socketpair(). Why not eludes me currently...

comment:6 Changed 8 years ago by nickm

Try putting a breakpoint in evutil_socketpair to make sure it's getting called, and step through it to see what happens?

comment:7 Changed 8 years ago by Sebastian

Aha, here we go. It's not getting called,

        if (base->th_notify_fd[0] < 0) {

is false and we don't ever get to the

                if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0,
                        base->th_notify_fd) == -1) {

. Fun.

comment:8 Changed 8 years ago by nickm

Oh, and we don't get there because we're using pipe(), not socketpair(), on mac. You can override that by replacing "#if defined(_EVENT_HAVE_PIPE)" with "#if 0" in event_base_make_notifiable, if you're looking for a way to test.

comment:9 Changed 8 years ago by Sebastian

woot! That works.

comment:10 Changed 8 years ago by Sebastian

Looks good to me. Maybe we can get erinn to do a test windows build before we release the next version, to be sure this is fixed for at least someone actually seeing the issue on Windows.

comment:11 Changed 8 years ago by nickm

Resolution: fixed
Status: needs_reviewclosed

Merged to 0.2.2 and master.

comment:12 Changed 8 years ago by arma

Did you know there's a trac ticket with an identical name ("Assertion at startup on Windows 7 when socketpair fails") at #3464?

comment:13 Changed 7 years ago by nickm

Keywords: tor-client added

comment:14 Changed 7 years ago by nickm

Component: Tor ClientTor
Note: See TracTickets for help on using tickets.