Opened 9 years ago

Closed 9 years ago

#9535 closed Bugs (fixed)

Missing exception safety might result in crash

Reported by: anonymous Owned by: viboes
Milestone: Boost 1.56.0 Component: thread
Version: Boost 1.55.0 Severity: Problem
Keywords: Cc:

Description

As I was curious as to how boost::wait_for_any is implemented, and I think I found a little flaw in the future_waiter class in boost/thread/future.hpp ( which the documentation incorrectly lists as <boost/thread/futures.hpp> ):

The future_waiter class registers it's condition variable in the add function

            template<typename F>
            void add(F& f)
            {
                if(f.future_)
                {
                    futures.push_back(registered_waiter(f.future_,f.future_->register_external_waiter(cv),future_count));
                }
                ++future_count;
            }

and unregisters it in the destructor

            ~future_waiter()
            {
                for(count_type i=0;i<futures.size();++i)
                {
                    futures[i].future_->remove_external_waiter(futures[i].wait_iterator);
                }
            }

However, if the program encounters an out-of-memory condition during it's call to push_back in add(), the registered_waiter is not stored in the vector und thus is not unregistered in the destructor, so as soon as the future readies up, it calls notify_all on a dangling pointer, resulting in a crash. I was able to successfully construct this scenario by overloading operator new() using GCC on a unix-like OS.

I suggest adding a mechanism to unregister the waiter in case of an exception. Changing the add function to the following fixed it for me:

            template<typename F>
            void add(F& f)
            {
                if(f.future_)
                {
                    registered_waiter waiter(f.future_,f.future_->register_external_waiter(cv),future_count);
                    try {
                        futures.push_back(waiter);
                    } catch(...) {f.future_->remove_external_waiter(waiter.wait_iterator); throw;}
                }
                ++future_count;
            }

As you see, I have just introduced a local variable and a try-catch block. There could be more elegant solutions, but this was the first one that came to mind.

Change History (4)

comment:1 by viboes, 9 years ago

Owner: changed from Anthony Williams to viboes
Status: newassigned

Thanks for catching this.

comment:2 by viboes, 9 years ago

Milestone: To Be DeterminedBoost 1.56.0
Note: See TracTickets for help on using tickets.