mbed official WiflyInterface (interface for Roving Networks Wifly modules)

Dependents:   Wifly_HelloWorld Websocket_Wifly_HelloWorld RPC_Wifly_HelloWorld HTTPClient_Wifly_HelloWorld ... more

Issue: Wifly exit( ) method can cause recursive loop to stack exhaustion (lib ver 8)

I discovered this recursion to stack exhaustion while trying to crank up the baud rate of the Wifly module, and the corresponding serial interface in the mbed module.

To understand this path, we'll start with a call to exit( ) assuming we are in cmd_mode from some prior command. So, the code path will attempt to send "exit\r" to the wifly module. It does this by calling sendCommand from the stack frame of exit( ). Now, in sendCommand, we attempt the send( ) and if it fails to see the "EXIT" acknowledge, it will call exit on the stack frame of sendCommand( ). This is where the deadly loop begins.

I have tested and use the proposed mods in my derived WiflyInterface, which incorporates some other changes unrelated to this. Things can and do still go wrong, but now it should be reporting success and failure via the return values, and not exhausting the stack-frame until watchdog reset.

Wifly.cpp - recursive path in lib ver 8

bool Wifly::exit()
{
    flush();
    if (!state.cmd_mode)
        return true;
    if (!sendCommand("exit\r", "EXIT"))    // exit( ) will now call SendCommand( )
        return false;
    state.cmd_mode = false;
    flush();
    return true;
}

bool Wifly::sendCommand(const char * cmd, const char * ack, char * res, int timeout)
{
    if (!state.cmd_mode) {
        cmdMode();
    }
    if (send(cmd, strlen(cmd), ack, res, timeout) == -1) {     // Assume something went wrong here
        ERR("sendCommand: cannot %s\r\n", cmd);
        exit();        // Call exit, but in this thread, we're already on exit( ) stack frame so this is recursion
        return false;
    }
    return true;
}

bool Wifly::cmdMode()
{
    // if already in cmd mode, return
    if (state.cmd_mode)
        return true;

    if (send("$$$", 3, "CMD") == -1) {
        ERR("cannot enter in cmd mode\r\n");
        exit();             // Here's another path to exit, which could call sendCommand and then cmdMode
        return false;
    }
    state.cmd_mode = true;
    return true;
}

Wifly.cpp - recursion prevented proposal

bool Wifly::exit()               // Exit is unaltered...
{
    flush();
    if (!state.cmd_mode)
        return true;
    if (!sendCommand("exit\r", "EXIT"))
        return false;
    state.cmd_mode = false;
    flush();
    return true;
}

bool Wifly::sendCommand(const char * cmd, const char * ack, char * res, int timeout)
{
    int tries = 1;

    while (tries <= 2) {
        if (cmdMode()) {
            if (send(cmd, strlen(cmd), ack, res, timeout) >= 0) {    // Reversed the logic here to test for success
                return true;
            }
        }
        state.cmd_mode = false;           // apparently it is not really in command mode
        ERR("sendCommand: failure %d when sending: %s", tries, cmd);
        tries++;
    }
    return false;
}

bool Wifly::cmdMode()
{
    // if already in cmd mode, return
    if (state.cmd_mode) {
        // verify we really are in cmd mode
        flushIn(0);
        if (send("\r", 1, ">") == 1) {
            return true;
        } else {
            state.cmd_mode = false;
        }
    }
    wait_ms(260);   // manual 1.2.1 (250 msec before and after)
    if (send("$$$", 3, "CMD") == -1) {  // the module controls the 'after' delay while we wait
        ERR("cannot enter in cmd mode");
        return false;                       // DO NOT call exit( ) prior to return to block recursion
    }
    state.cmd_mode = true;
    return true;
}