vendredi 21 juin 2019

Member Variables in Class Get Blown Away When Using std::thread

I have defined a base class using std::thread. For the child class, I perform some initialization of member variables and then start the thread using m_thread.reset(new std::thread(&MyClass::ThreadMain, this)); where m_thread is a member of MyClass. The purpose of the class is to read data from a serial port and report to a parent. The posix message queue handle of the parent is passed to MyClass during initialization before the thread is created. On running I get exceptions and I see that member variables that were initialized before the thread started appear to be no longer valid using the watch in GDB.

I tried running through Valgrind and got these errors:

==10108== Syscall param msgsnd(msgp->mtext) points to uninitialised byte(s) ==10108== at 0x4A7C512: msgsnd (syscall-template.S:81) ==10108== by 0x19C3B: QxRadio::SendToParent(unsigned char*, unsigned char) (QxRadio.cpp:201) ==10108== by 0x19A9F: QxRadio::ThreadMain() (QxRadio.cpp:158) ==10108== by 0x1AB5D: void std::_Mem_fn::operator()<, void>(QxRadio*) const (in /root/ESSE/sourcecode/Firmware/QxxToolRadioDriver/Output/BIN/QXRD) ==10108== by 0x1AADF: void std::_Bind_simple (QxRadio*)>::_M_invoke<0u>(std::_Index_tuple<0u>) (functional:1700) ==10108== by 0x1A9EF: std::_Bind_simple (QxRadio*)>::operator()() (functional:1688) ==10108== by 0x1A991: std::thread::_Impl (QxRadio*)> >::_M_run() (thread:115) ==10108== by 0x4917117: ??? (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.20) ==10108== Address 0x56d6bbc is on thread 2's stack ==10108== in frame #1, created by QxRadio::SendToParent(unsigned char*, unsigned char) (QxRadio.cpp:188) ==10108== Uninitialised value was created by a stack allocation ==10108== at 0x19B5E: QxRadio::SendToParent(unsigned char*, unsigned char) (QxRadio.cpp:188)

==10108== Thread 2: ==10108== Syscall param read(fd) contains uninitialised byte(s) ==10108== at 0x4864580: read (syscall-template.S:81) ==10108== by 0x1994D: QxRadio::ThreadMain() (QxRadio.cpp:113) ==10108== by 0x1AB5D: void std::_Mem_fn::operator()<, void>(QxRadio*) const (in /root/ESSE/sourcecode/Firmware/QxxToolRadioDriver/Output/BIN/QXRD) ==10108== by 0x1AADF: void std::_Bind_simple (QxRadio*)>::_M_invoke<0u>(std::_Index_tuple<0u>) (functional:1700) ==10108== by 0x1A9EF: std::_Bind_simple (QxRadio*)>::operator()() (functional:1688) ==10108== by 0x1A991: std::thread::_Impl (QxRadio*)> >::_M_run() (thread:115) ==10108== by 0x4917117: ??? (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.20) ==10108== Uninitialised value was created by a stack allocation ==10108== at 0x4A0BF40: __libc_sigaction (sigaction.c:62)

==10108== ==10108== Warning: invalid file descriptor 67266232 in syscall read() ==10108== at 0x4864580: read (syscall-template.S:81) ==10108== by 0x1994D: QxRadio::ThreadMain() (QxRadio.cpp:113) ==10108== by 0x1AB5D: void std::_Mem_fn::operator()<, void>(QxRadio*) const (in /root/ESSE/sourcecode/Firmware/QxxToolRadioDriver/Output/BIN/QXRD) ==10108== by 0x1AADF: void std::_Bind_simple (QxRadio*)>::_M_invoke<0u>(std::_Index_tuple<0u>) (functional:1700) ==10108== by 0x1A9EF: std::_Bind_simple (QxRadio*)>::operator()() (functional:1688) ==10108== by 0x1A991: std::thread::_Impl (QxRadio*)> >::_M_run() (thread:115) ==10108== by 0x4917117: ??? (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.20)

It appears as if the first message on the serial port is received and passed validation in order to get to the SendToParent call. At this call, it appears that I lose the stack. I tried running cppcheck to see if I have any memory leaks or buffer overflows and found nothing.

void MyClass::ThreadMain(void)
{
   ssize_t bytesRead = 0;
   UINT8 buffer[256];
   UINT8 message[256];
   BOOL partialMessage = FALSE;
   UINT8 messageIndex = 0;
   UINT8 payloadLength = 0;

   // read data from the UART
   while(1)
   {
      // the UART is setup to pend until data is available
      bytesRead = read(m_radioFileDescriptor, buffer, sizeof(buffer));
      if (FAIL == bytesRead)
      {
         LOG_SYSTEM_INFO("UART Read interrupted by a system call");
      }
      else if (bytesRead > 0)
      {
         // build the message
         for(ssize_t i = 0 ; i < bytesRead ; i++)
         {
            if (FALSE == partialMessage)
            {
               // have we found the start of the message?
               if(START_BYTE == buffer[i])
               {
                  // start of new message
                  messageIndex = 0;
                  message[messageIndex] = buffer[i];
                  partialMessage = TRUE;
                  messageIndex++;
               }
            }
            else
            {
               // keep building the message until the expected length is reached
               if(LENGTH_POSITION == messageIndex)
               {
                  // capture the expected message length
                  message[messageIndex] = buffer[i];
                  messageIndex++;
                  payloadLength = buffer[i];
               }
               else
               {
                  message[messageIndex] = buffer[i];
                  messageIndex++;

                  // check for expected length and end byte
                  if((messageIndex == payloadLength) && (END_BYTE == buffer[i]))
                  {
                     // this should be a valid message but need to confirm by checking for a valid checksum
                     UINT8 messageChecksum = message[messageIndex - CHKSUM_POS_FROM_END];
                     UINT8 calculatedChecksum = RadioProtocol::Instance().GenerateRadioChecksum(message, (payloadLength - CHKSUM_POS_FROM_END));
                     if (messageChecksum == calculatedChecksum)
                     {
                        SendToParent(message, payloadLength);
                     }
                     else
                     {
                        LOG_SYSTEM_ERROR("Checksum FAILURE");
                     }

                     // reset for the next message
                     partialMessage = FALSE;
                     messageIndex = 0;
                  }
                  else if((messageIndex == payloadLength) && (END_BYTE != buffer[i]))
                  {
                     // malformed message - throw out and look for start of next message
                     LOG_SYSTEM_ERROR("Bytes read exceeded expected message length");
                     partialMessage = FALSE;
                     messageIndex = 0;
                  }
               }
            }
         } // end for loop of bytes read on the port
      }
      else
      {
         LOG_SYSTEM_INFO("Read returned 0 bytes which is unexpected");
      }
   }
}

void MyClass::SendToParent(UINT8* pMsg, UINT8 size)
{
   if ((pMsg != NULL) && (m_parentQueueHandle > 0))
   {
      // message is valid - pass up for processing
      MsgQueueMessage msgToSend;

      msgToSend.m_msgHeader = UART_MESSASGE;
      bzero(msgToSend.m_msgData, sizeof(msgToSend.m_msgData));
      for (UINT8 i = 0; i < size; i++)
      {
         msgToSend.m_msgData[i] = pMsg[i];
      }

      if (FAIL == msgsnd(m_parentQueueHandle, &msgToSend, sizeof(msgToSend), IPC_NOWAIT))
      {
         LOG_SYSTEM_ERROR("FAILED to send message on queue");
      }
   }
}

This acts like I am performing a buffer overflow but I just can't see it. When I set a breakpoint at the line UINT8 messageChecksum = message[messageIndex - CHKSUM_POS_FROM_END]; all data in the watch window appear valid. If I step over to the next line then the data, m_parentQueueHandle as an example, gets blown away.

This is my first time working with c++11 threads and particularly with c++. Any help or insights would be appreciated.

Aucun commentaire:

Enregistrer un commentaire