Valgrind видит delete в строке с надписью new

Когда я проверял свое приложение на предмет утечек памяти, я обнаружил очень своеобразное поведение valgrind. Насколько я понимаю, он видит следующий код

virtual ServiceHandler* createServiceHandler(StreamSocket& socket)
    /// Create and initialize a new ServiceHandler instance.
    ///
    /// Subclasses can override this method.
{
    return new ServiceHandler(socket, *_pReactor);
}

как место, где я и выделяю и освобождаю память. Этот код является частью библиотеки POCO, и его можно найти здесь. Ниже приведен пример журнала из valgrind, в котором говорится, что предоставленный оператор возврата приводит как к новому, так и к удалению.

Invalid read of size 8
==9764==    at 0x1C63DC: Poco::Net::Socket::operator!=(Poco::Net::Socket const&) const (Socket.h:340)
==9764==    by 0x1C2A5C: SocketHandler::onReadableNotification(Poco::AutoPtr<Poco::Net::ReadableNotification> const&) (SocketHandler.cpp:67)
==9764==    by 0x1CA0DF: Poco::NObserver<SocketHandler, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (NObserver.h:88)
==9764==    by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78)
==9764==    by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80)
==9764==    by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282)
==9764==    by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258)
==9764==    by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114)
==9764==    by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57)
==9764==    by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349)
==9764==    by 0x6D20493: start_thread (pthread_create.c:333)
==9764==    by 0x78BBAFE: clone (clone.S:97)
==9764==  Address 0x10c8f908 is 40 bytes inside a block of size 352 free'd
==9764==    at 0x4C2D2DB: operator delete(void*) (vg_replace_malloc.c:576)
==9764==    --> by 0x19D1CD: Poco::Net::SocketAcceptor<SocketHandler>::createServiceHandler(Poco::Net::StreamSocket&) (SocketAcceptor.h:166) 
==9764==    by 0x19B3E3: Poco::Net::SocketAcceptor<SocketHandler>::onAccept(Poco::Net::ReadableNotification*) (SocketAcceptor.h:157)
==9764==    by 0x1A1385: Poco::Observer<Poco::Net::SocketAcceptor<SocketHandler>, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (Observer.h:86)
==9764==    by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78)
==9764==    by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80)
==9764==    by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282)
==9764==    by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258)
==9764==    by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114)
==9764==    by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57)
==9764==    by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349)
==9764==    by 0x6D20493: start_thread (pthread_create.c:333)
==9764==  Block was alloc'd at
==9764==    at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334)
==9764==    --> by 0x19D19E: Poco::Net::SocketAcceptor<SocketHandler>::createServiceHandler(Poco::Net::StreamSocket&) (SocketAcceptor.h:166)
==9764==    by 0x19B3E3: Poco::Net::SocketAcceptor<SocketHandler>::onAccept(Poco::Net::ReadableNotification*) (SocketAcceptor.h:157)
==9764==    by 0x1A1385: Poco::Observer<Poco::Net::SocketAcceptor<SocketHandler>, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (Observer.h:86)
==9764==    by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78)
==9764==    by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80)
==9764==    by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282)
==9764==    by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258)
==9764==    by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114)
==9764==    by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57)
==9764==    by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349)
==9764==    by 0x6D20493: start_thread (pthread_create.c:333)

я добавил --> в интересную часть.

Как только это происходит, я наблюдаю больше подобных журналов, поскольку объект кажется не распределенным. Приложение работает корректно в течение некоторого времени, прежде чем я вижу эту ошибку, и оно происходит первым. Я думаю, что эта ошибка может возникнуть, когда сокет действует не очень хорошо.

Я valgrind приложение, используя следующую команду

valgrind --vgdb=yes --vgdb-error=0 app

и использовать GDB для дальнейшей проверки кода.

Мой вопрос:

  • Почему valgrind говорит мне, что предоставленный код вызывает как новый, так и удаление?
  • Это поведение вызвано этим кодом локально, или я должен искать проблему где-то еще?

@редактировать

т е р

SocketHandler::SocketHandler(StreamSocket &socket, SocketReactor &reactor) :
    _socket(socket) /* copy */, _reactor(reactor) /* reference */ {

    _socket.setBlocking(false);

    _reactor.addEventHandler(_socket, NObserver<SocketHandler, ReadableNotification>(*this,
                                                                                 &SocketHandler::onReadableNotification));
    _reactor.addEventHandler(_socket,
                         NObserver<SocketHandler, ErrorNotification>(*this, &SocketHandler::onErrorNotification));
    _reactor.addEventHandler(_socket, NObserver<SocketHandler, ShutdownNotification>(*this,
                                                                                 &SocketHandler::onShutdownNotification));

    _addr = _socket.peerAddress().toString();
}

2 ответа

Решение

Как предложил Бо Перссон в комментариях, удаление было вызвано необработанным исключением в конструкторе. После перехвата соответствующих исключений в ctor и перемещения выражения, которое выдает исключения, до строк, ответственных за регистрацию обработчиков событий

SocketHandler::SocketHandler(Poco::Net::StreamSocket &socket, SocketReactor &reactor) :
        _socket(socket), _reactor(reactor) {
    try {
        _socket.setBlocking(false);
        _addr = _socket.peerAddress().toString();

        _reactor.addEventHandler(_socket, NObserver<SocketHandler, ReadableNotification>(*this,
                                                                                         &SocketHandler::onReadableNotification));
        _reactor.addEventHandler(_socket,
                                 NObserver<SocketHandler, ErrorNotification>(*this,
                                                                             &SocketHandler::onErrorNotification));
        _reactor.addEventHandler(_socket, NObserver<SocketHandler, ShutdownNotification>(*this,
                                                                                         &SocketHandler::onShutdownNotification));
    } catch (Exception& e) {
        // Handle POCO exception
    } catch (...) {
        // Handle ellipsis
    }
}

неверный доступ к памяти больше не Исключение выдается _socket.peerAddress() при попытке получить адрес сокета, который больше не подключен. Сначала я подумал, что любое исключение в ctor приведет к быстрому падению приложения, но в Poco::Net::SocketReactor каждое необработанное исключение передается ErrorHandler, Именно поэтому приложение продолжало работать даже после создания необработанных исключений. Указанный код можно найти здесь:126.

Сообщение Valgrind не означает, что у вас есть новая и удаляемая в одной строке кода.

Сообщение об ошибке valgrind содержит 3 трассировки стека.

Первая трассировка стека - это место, где обнаружена ошибка. Эта ошибка состоит в чтении фрагмента памяти, который был освобожден.

Затем Valgrind пытается дать вам больше информации об этом фрагменте памяти. Для этого он показывает вам 2 дополнительных следа стека:

Вторая трассировка стека указывает, где этот фрагмент памяти был освобожден. Третья трассировка стека указывает, где был выделен этот фрагмент памяти.

Объясняется другими словами: ваш код сначала выделил часть памяти (при трассировке стека № 3). Ваш код освободил этот фрагмент памяти (при трассировке стека № 2). И тогда ваш код ошибочно обращается к этой части памяти (при трассировке стека № 1).

Итак, все это выглядит так, как будто у вас есть настоящая ошибка: вы используете висячий указатель, указывающий на освобожденную память.

Возможно, вам придется посмотреть на сгенерированный код, чтобы увидеть, какие именно вызовы new / delete генерируются для вызова createServiceHandler. Ясно, что в трассировках стека 'new' и 'delete' есть две разные инструкции (0x19D1CD и 0x19D19E).

Другие вопросы по тегам