This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129ENCPDT: NIMUPPP.C Code Issue

Part Number: TM4C129ENCPDT

I have come across a double freeing of memory in the example hdlcif.c code which calls the nimuppp.c timer function.

The timer function is called from the hdlcif

/*-------------------------------------------------------------------- */
/* pppTimer() */
/* Timer function called once per second */
/*-------------------------------------------------------------------- */
void pppTimer( HANDLE hPPP )
{
    PPP_SESSION *p = (PPP_SESSION *)hPPP;

    if( !p || p->Type != HTYPE_PPP )
    {
        DbgPrintf(DBG_ERROR,"pppTimer: Bad Handle %08x",p);
        return;
    }

    /* Add reference and execute */
    if( p->InUse < (INUSE_LOCKED-1) )
    {
        p->InUse++;

        lcpTimer( p );
        authTimer( p );
        ipcpTimer( p );

        /* Remove reference */
        if( p->InUse == INUSE_IDLE )
            pppFree( p );
        else
            p->InUse--;
    }
}

The lcptimer function can call back to the hdlc event function

//--------------------------------------------------------------------
// SI Control Function
//--------------------------------------------------------------------
static void hdlcSI( HANDLE hSI, uint Msg, UINT32 Aux, PBM_Handle hPkt )
{
	HDLC_INSTANCE       *pi = (HDLC_INSTANCE *)hSI;
	uint                Offset, Size;
	UINT8               *pBuf;

	switch( Msg )
	{
			case SI_MSG_CALLSTATUS:
					// Update Connection Status
					pi->Status = (uint)Aux;
					if ( pi->Status >= SI_CSTATUS_DISCONNECT )
					{
						// Close PPP - we clear the handle to make sure we
						// only call pppFree() once. (We may get multiple
						// disconnect messages - one from each protocol.)
						if ( pi->hPPP )
						{
							HANDLE hTmp = pi->hPPP;
							pi->hPPP = 0;
							pppFree( hTmp );
						}
					}
					break;

			case SI_MSG_PEERCMAP:
					// Update CMAP for Transmit
					pi->cmap_out = Aux;
					comHDLCPeerMap( pi->port, Aux );
					break;

			case SI_MSG_SENDPACKET:
					if( !hPkt )
					{
							DbgPrintf(DBG_ERROR,"hdlcSI: No packet");
							break;
					}

					Offset = PBM_getDataOffset(hPkt);
					Size = PBM_getValidLen(hPkt);

					// Make sure packet is valid, room for protocol, room for checksum
					if ( (Offset < 4) || ((Offset + Size + 2) > PBM_getBufferLen(hPkt)) )
					{
							DbgPrintf(DBG_ERROR, "hdlcSI: Bad packet");
							PBM_free( hPkt );
							break;
					}

					// Add in 2 byte Protocol and 2 byte header. Also add in size for
					// 2 byte checksum. Note that the outgoing checksum is corrected
					// (calculated) by the serial driver.
					Offset -= 4;
					Size += 6;
					PBM_setDataOffset(hPkt, Offset );
					PBM_setValidLen(hPkt, Size );
					pBuf = PBM_getDataBuffer(hPkt) + Offset;
					*pBuf++ = 0xFF;
					*pBuf++ = 0x03;
					*pBuf++ = (UINT8)(Aux / 256);							// Upper Byte
					*pBuf++ = (UINT8)(Aux % 256);							// Lower Byte

					// Send the buffer to the serial driver
					// If LCP and the command is <= 7, then make sure we
					// use the default CMAP
					if( Aux == 0xc021 && *pBuf <= 7 )
					{
							comHDLCPeerMap( pi->port, 0xFFFFFFFF );
							comHDLCSendPkt( pi->port, hPkt );
							comHDLCPeerMap( pi->port, pi->cmap_out );
					}
					else
					{
						comHDLCSendPkt( pi->port, hPkt );
					}
					break;
	}
}

This code can then free the PPP structure on a disconnect, however the ppptimer function continues to use the original pointer and then tries to free this pointer at the end giving a double free.

My work around for this was to simply add this code to the HDLC timer function

//--------------------------------------------------------------
// hdlcTimer()
// Dispatches timer ticks to the stack's PPP module
//--------------------------------------------------------------
static void hdlcTimer( HANDLE hHDLC )
{
	HDLC_INSTANCE *pi = (HDLC_INSTANCE *)hHDLC;

	if( pi && (pi->Type == HDLC_INST_CLIENT || pi->Type == HDLC_INST_SERVER) && pi->hPPP )
	{
		pppTimer( pi->hPPP );
		if ( pi->Status >= SI_CSTATUS_DISCONNECT )
		{
			// Close PPP - we clear the handle to make sure we
			// only call pppFree() once. (We may get multiple
			// disconnect messages - one from each protocol.)
			if ( pi->hPPP )
			{
				HANDLE hTmp = pi->hPPP;
				pi->hPPP = 0;
				pppFree( hTmp );
			}
		}
	}
}

As well as the stop and free functions:

//--------------------------------------------------------------
// hdlcFree()  USER FUNCTIION
// Close HDLC Client
//--------------------------------------------------------------
void hdlcFree( HANDLE hHDLC )
{
	HDLC_INSTANCE *pi = (HDLC_INSTANCE *)hHDLC;

	if (pi)
	{
		// Enter kernel mode
		llEnter();

		if( pi && (pi->Type==HDLC_INST_CLIENT || pi->Type==HDLC_INST_SERVER)  )
		{
			// This message will close PPP
			hdlcSI( hHDLC, SI_MSG_CALLSTATUS, SI_CSTATUS_DISCONNECT, 0 );

			if ( pi->Status >= SI_CSTATUS_DISCONNECT )
			{
				// Close PPP - we clear the handle to make sure we
				// only call pppFree() once. (We may get multiple
				// disconnect messages - one from each protocol.)
				if ( pi->hPPP )
				{
					HANDLE hTmp = pi->hPPP;
					pi->hPPP = 0;
					pppFree( hTmp );
				}
			}

			// Zap the type
			pi->Type = 0;

			// Close the serial driver
			comCloseHDLC( pi->port );

			// Free the instance
			free( pi );
		}

		// Exit kernel mode
		llExit();
	}
}
void hdlcStop(HANDLE hHDLC)
{
	HDLC_INSTANCE *pi = (HDLC_INSTANCE *)hHDLC;

	if (pi)
	{
		// Enter kernel mode
		llEnter();

		if( pi && (pi->Type == HDLC_INST_CLIENT || pi->Type == HDLC_INST_SERVER)  )
		{
			// This message will close PPP
			hdlcSI( hHDLC, SI_MSG_CALLSTATUS, SI_CSTATUS_DISCONNECT, 0 );
			if ( pi->Status >= SI_CSTATUS_DISCONNECT )
			{
				// Close PPP - we clear the handle to make sure we
				// only call pppFree() once. (We may get multiple
				// disconnect messages - one from each protocol.)
				if ( pi->hPPP )
				{
					HANDLE hTmp = pi->hPPP;
					pi->hPPP = 0;
					pppFree( hTmp );
				}
			}
		}

		// Exit kernel mode
		llExit();
	}
}

This ensures the PPP pointer isn't freed by the hdlc interface until it has control of the PPP pointer itself.

Just wanted to check if this is the best way to handle this, or if really the nimuppp.c file should do a check of the hdlc PPP pointer within each step of the timer function and exit if this has cleared.

  • Hi Barry,

    We'll take a look at this and confirm the best solution. It may take a day or two.

    Todd

  • Barry,

    Which version of TI-RTOS for TivaC are you using? This was a problem with the NDK, but it looked like we fixed it in the NDK 2.24 series.

    Todd

  • Hello Todd

    Thanks for the response.

    We are using "tirtos_tivac_2_16_01_14" this contains the NDK version "ndk_2_25_00_09"

    This is showing as the latest release for TM4C129ENCPDT, it hasn't been updated in years.

    Regards

    Barry

  • Barry,

    Thanks. We're still looking into this. Again, it might take a couple days.

    Todd

  • Hi Barry,

    It is a bug. We've opened this jira: https://sir.ext.ti.com/jira/browse/EXT_EP-9825

    For now, you should stick with your fix. It may take a bit (a month or two) before we get to this ticket. Once we get it resolved you can check to see if we addressed the bug in a different manner.

    I'm going to mark this thread as resolved since the external jira is now present and will be used to communicate status.

    Todd

  • Hi Barry,

    I'm looking into fixing this issue now.

    The root of the problem looks to me to be in the pppTimer() function itself:

    void pppTimer( HANDLE hPPP )
    {
        ...
            lcpTimer( p ); // problem: this might free p!
            authTimer( p ); // problem: this might free p! Also accessing/operating on p after it's been freed!
            ipcpTimer( p ); // problem: this might free p! Also accessing/operating on p after it's been freed!
            /* Remove reference */
            if( p->InUse == INUSE_IDLE ) // operating on p after it's been freed!
                pppFree( p ); // problem: double free of p!
            else
                p->InUse--;

    As noted above in the bolded comments, any of those timer functions can free p. From that point on, although 'p' isn't actually NULL in this code, it can still be dereferenced. I suspect that in your app you've just been "getting lucky" when this issue happens, because nothing else has called mmAlloc again and gotten the same memory where the PPP session was stored. So, the memory where 'p' points wouldn't have actually been overwritten yet. (and so the code within that dereferences 'p' can still get its old values).

    But, if 'p' is ever freed due to a disconnect, then any of the above statements that follow that event are no longer valid (due to above reasons).

    Since the problem is rooted in pppTimer(), I'm thinking it's best to handle it there. I'm proposing the following changes:

    visual diff:

    nimuppp.c.html

    patch diff:

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/908/nimuppp.c.patch

  • And of course, let me know if you disagree with any of the above.

    Lastly, I was not able to test the above due to the current covid + WFH situation and will be a while before I can get into the office to to a PPP set up.

    If you agree with the above, and it's easy for you to reproduce this issue, you might give it a try in the mean time.

    Steve

  • Hello Steven

    Thanks for the update.

    These changes look like they will do the job.

    I have my own work around at the moment, I can look to include the PPP file into my source at some stage to verify, not sure when I will be able to do this.

    I detected the issue originally because of an issue with a modem which has now been fixed, so not sure how best to test this as my test case has been eliminated.