diff --git a/abbterra/abbterra-registers.json b/abbterra/abbterra-registers.json index 4883cad..a2b385c 100644 --- a/abbterra/abbterra-registers.json +++ b/abbterra/abbterra-registers.json @@ -1,6 +1,7 @@ { "className": "AbbTerra", "protocol": "BOTH", + "checkReachableRegister": "serialNumber", "endianness": "BigEndian", "stringEndianness": "BigEndian", "errorLimitUntilNotReachable": 3, diff --git a/abbterra/abbterratcpdiscovery.cpp b/abbterra/abbterratcpdiscovery.cpp index e0077f1..dd104ef 100644 --- a/abbterra/abbterratcpdiscovery.cpp +++ b/abbterra/abbterratcpdiscovery.cpp @@ -92,7 +92,24 @@ void AbbTerraTcpDiscovery::checkNetworkDevice(const QHostAddress &address) void AbbTerraTcpDiscovery::cleanupConnection(AbbTerraModbusTcpConnection *connection) { - m_connections.removeAll(connection); + // A single reachability failure fans out into several signals + // (reachableChanged, checkReachabilityFailed, connectionErrorOccurred), each + // wired to this method. removeAll() returns the number of entries actually + // removed, so the first call tears the connection down and every subsequent + // (possibly re-entrant) call becomes a no-op. + if (m_connections.removeAll(connection) == 0) + return; + + // Stop reacting to any further signals from this connection (and its Modbus + // master) before tearing it down. Otherwise disconnectDevice() below can + // synchronously emit reachableChanged()/connectionErrorOccurred() and + // re-enter this method while we are still inside one of the connection's own + // signal handlers (e.g. the QModbusReply "finished" lambda that keeps + // dereferencing the reply after the emit) — a use-after-free that crashes + // nymead with SIGSEGV during network discovery. + disconnect(connection, nullptr, this, nullptr); + disconnect(connection->modbusTcpMaster(), nullptr, this, nullptr); + connection->disconnectDevice(); connection->deleteLater(); }