Skip to content

Exception (9) when using strncpy #2685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
timlag1305 opened this issue Nov 13, 2016 · 1 comment
Closed

Exception (9) when using strncpy #2685

timlag1305 opened this issue Nov 13, 2016 · 1 comment

Comments

@timlag1305
Copy link

timlag1305 commented Nov 13, 2016

Sorry in advanced for the really long program. I will try to come up with a simpler case which still triggers my error.
Here's the stacktrace:

Exception (9):
epc1=0x402204e2 epc2=0x00000000 epc3=0x00000000 excvaddr=0x40209dfa depc=0x00000000

ctx: sys
sp: 3ffffd80 end: 3fffffb0 offset: 01a0

>>>stack>>>
3fffff20:  4020e42a 887fcf5c 8574b2c2 28cf6a2a
3fffff30:  4020c0a0 3ffee358 3fff1d84 00000008
3fffff40:  40226b3a 3ffee358 3ffee358 3ffeb810
3fffff50:  3ffeb810 00000060 00000000 0000002f
3fffff60:  00000002 0000001a 4020c3f3 40211e09
3fffff70:  3ffef818 3ffeed30 03a71f63 60000600
3fffff80:  40211e4e 3fffdab0 00000000 3fffdcb0
3fffff90:  3ffeed40 3fffdab0 00000000 40201a13
3fffffa0:  40000f49 40000f49 3fffdab0 40000f49
<<<stack<<<

 ets Jan  8 2013,rst cause:4, boot mode:(1,7)

wdt reset

And here it is decoded:

decoding 10 results
0x4020e42a: wDev_Get_Next_TBTT at ?? line ?
0x4020c0a0: wifi_fpm_do_wakeup at ?? line ?
0x40226b3a: send_server_hello_done at /Users/igrokhotkov/e/axtls/e1/ssl/tls1_svr.c line 363
:  (inlined by) send_server_hello_sequence at /Users/igrokhotkov/e/axtls/e1/ssl/tls1_svr.c line 281
:  (inlined by) do_svr_handshake at /Users/igrokhotkov/e/axtls/e1/ssl/tls1_svr.c line 80
0x4020c3f3: pp_noise_test at ?? line ?
0x40211e09: hostap_input at ?? line ?
0x40211e4e: hostap_input at ?? line ?
0x40201a13: uart_init at /home/tim/.arduino15/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266/uart.c line 336


Basic Infos

Hardware

Hardware: ESP-12
Core Version: 2.3.0

Description

I get an Exception (9) error (LoadStoreAlignment) when I use strncpy(groupNames[nameIdx], pos + 8, nameLen). Workarounds that I have found include declaring groupNames[][] in the global scope, or copying the characters one by one in a for loop.
I've also had issues using calloc on groupNames[i] when I tried making it a dynamic 2D array, but I just wanted to focus on this error. Like I said, if I can figure out a smaller program that triggers this same error, I will either edit this post or upload the smaller program in the comments.

Settings in IDE

Module: Adafruit HUZZAH ESP8266
Flash Size: 4MB/1MB
CPU Frequency: 80Mhz
Flash Mode: qio
Flash Frequency: 40Mhz
Upload Using: SERIAL
Reset Method: nodemcu

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <FS.h>
#include <SPI.h>
#include <WiFiClientSecure.h>
#include <climits>
#include <ctype.h>
#include <string>
#include <algorithm>
#include <cctype>

// Hardware button press definitions
#define SINGLE_CLICK 1
#define DOUBLE_CLICK 2
#define HOLD_CLICK 3
#define CLICK_DELAY 500         // Max delay between double clicks in ms
#define HOLD_LENGTH 1000        // Button hold length in ms

// GroupMe definitions
#define KEY_LEN 41
#define SERVER "api.groupme.com"
#define HTTPS_PORT 443
#define GROUPME_INDEX 20
#define GROUPME_CREATE 21
#define GROUPME_ADD 22
#define GROUPME_MESSAGE 23
#define CONTACT_ID 24
#define CONTACT_NUM 25
#define CONTACT_EMAIL 26
// Fingerprint found using https://www.grc.com/fingerprints.htm
#define FINGERPRINT "ED:22:CB:A5:30:A8:BB:B0:C2:27:93:90:65:CD:64:EA:EA:18:3F:0E"

using namespace std;

WiFiClientSecure client;
uint32_t startTime = 0;
uint32_t prevStartTime = 0;
uint32_t currentTime;
uint8_t clickType = 0;
uint8_t userIdType;
uint32_t randNumber;
char singleClickMessage[141];
char doubleClickMessage[141];
char holdClickMessage[141];
char key[41];
string groupmeKey;
string groupName;
string groupDescription;
string groupUID;
string nickname;
string message;
string userId;
bool hasDescription;
bool shareGroup = false;

void groupmeRequest(uint8_t requestType)
{
        char uid[11];
        client.stop();
        string request;
        string requestBody;
        bool getGroups = false;

        if (client.connect(SERVER, HTTPS_PORT))
        {
                Serial.println("Connected!");

                // Don't send information if the certificates don't match!
                if (client.verify(FINGERPRINT, SERVER)) {
                        switch(requestType)
                        {
                                case GROUPME_INDEX:
                                        request = request + "GET /v3/groups?per_page=1";
                                        getGroups = true;
                                        break;

                                // Create a new group:
                                // Required values: "name" - The name of this group
                                // Optional values: "description" - The description of this group
                                //                                      "share" - Returns share link if true
                                // Need to save the group uid
                                case GROUPME_CREATE:
                                        request = request + "POST /v3/groups";
                                        requestBody = requestBody +
                                                "{" +
                                                "       \"name\":\"" + groupName + "\"";

                                        if (hasDescription)
                                        {
                                                requestBody = requestBody + "," +
                                                "       \"description\":\"" + groupDescription + "\"";
                                        }

                                        if (shareGroup)
                                        {
                                                requestBody = requestBody + "," +
                                                "       \"share\":true";
                                        }
                                        requestBody = requestBody +
                                                "}";
                                        client.println(requestBody.c_str());

                                        break;
                                case GROUPME_ADD:
                                        request = request + "POST /v3/groups/" + groupUID + "/members/add/";
                                        requestBody = requestBody +
                                                "{" +
                                                "       \"nickname\":\"" + nickname +"\"";
                                                switch(userIdType)
                                                {
                                                        case CONTACT_ID:
                                                                requestBody = requestBody + "\"user_id\":\"" + userId + "\"";
                                                                break;
                                                        case CONTACT_NUM:
                                                                requestBody = requestBody + "\"phone_number\":\"" + userId + "\"";
                                                                break;
                                                        case CONTACT_EMAIL:
                                                                requestBody = requestBody + "\"email\":\"" + userId + "\"";
                                                                break;
                                                }
                                        break;
                                case GROUPME_MESSAGE:
                                        request = request + "POST /v3/groups/" + groupUID + "/messages";
                                        sprintf(uid, "%d", random(UINT_MAX));
                                        requestBody = requestBody +
                                                "{" +
                                                "       \"message\": {\"" +
                                                "               \"source_guid\":\"" + uid + "\"" +
                                                "               \"text\": " + message + "\"" +
                                                "       }" +
                                                "}";
                                        // Remove all whitespace
                                        //postData.erase(remove_if(postData.begin(), postData.end(), (int(*)(int))isspace), postData.end());
                                        // Insert the message
                                        //postData.replace(postData.rfind("%s"), 2, message);
                                        // Insert the uid
                                        //postData.replace(postData.find("%s"), 2, uid);
                                        break;
                        }

                        uint8_t pageNum = 1;
                        char pageNumStr[2];
                        long timeOut = 4000; //capture response from the server
                        long lastTime = millis();
                        uint32_t bytesAvailable;
                        int readLen;
                        char c;
                        char response[1025] = {'\0'};
                        // By default, the request gets 1 "page" of results with 10 groups
                        // on the page. If we want more groups, we can either have multiple
                        // requests or increase the number of groups per page.
                        char *pos;
                        uint8_t idIdx = 0;
                        uint8_t nameIdx = 0;
                        uint8_t nameLen;
                        Serial.println("certificate matches");
                        while (getGroups)
                        {
                                if (getGroups)
                                {
                                        sprintf(pageNumStr, "%d", pageNum);
                                        Serial.println((request + "&page=" + pageNumStr + "&token=" + groupmeKey + " HTTP/1.1").c_str());
                                        client.println((request + "&page=" + pageNumStr + "&token=" + groupmeKey + " HTTP/1.1").c_str());
                                        pageNum++;
                                }

                                client.println("Host: api.groupme.com");

                                if (requestBody.size() > 0)
                                {
                                        client.println("Content-Type: application/json");
                                        client.print("Content-Length: ");
                                        client.println(requestBody.size());
                                        client.println();
                                        client.println(requestBody.c_str());
                                }

                                client.println();
                                lastTime = millis();
                                // Get a maximum of 20 groups and group IDs
                                char groupIds[20][9] = {'\0'};
                                // Group names can be up to 255 characters in length
                                char groupNames[20][256] = {'\0'};

                                // There's some sort of dangling pointer or something...
                                while ((millis() - lastTime) < timeOut)  //wait for incoming response
                                {
                                        // Need to read in chunks because it's gonna be UUUUGGGEEEE
                                        while ((bytesAvailable = client.available()))           //characters incoming from server
                                        {
                                                if (bytesAvailable < 1024)
                                                {
                                                        client.readBytes(response, bytesAvailable);
                                                }
                                                else
                                                {
                                                        client.readBytes(response, 1024);          //read characters
                                                }
                                                //Serial.print(response);
                                                if (strstr(response, "Content-Length: 36"))
                                                {
                                                        getGroups = false;
                                                }
                                                else
                                                {
                                                        // Search the current string chunk for
                                                        // "group_id":"xxxxxxxx","name":"xxxxxxxxxxx"
                                                        // The problem we need to worry about is that this chunk
                                                        // might split up some of the needed data

                                                        // Find the pointer to the beginning of the group id key.
                                                        if (idIdx < 20)
                                                        {
                                                                pos = strstr(response, "\"group_id\":\"");
                                                                if (pos != NULL)// && pos + 12 + 8 < response + bytesAvailable)
                                                                {
                                                                        strncpy(groupIds[idIdx], pos + 12, 8);
                                                                        // Since the group ids may be less than 8 digits,
                                                                        // remove any extraneous characters
                                                                        for (uint8_t i = 0; i < 8; i++)
                                                                        {
                                                                                // Replace non digits with null terminators
                                                                                if (!isdigit(groupIds[idIdx][i]))
                                                                                {
                                                                                        groupIds[idIdx][i] = '\0';
                                                                                        break;
                                                                                }
                                                                        }

                                                                        Serial.println(groupIds[idIdx]);
                                                                        idIdx++;
                                                                }
                                                        }

                                                        // Find the pointer to the beginning of the group name
                                                        if (nameIdx < 20)
                                                        {
                                                                pos = strstr(response, "\"name\":\"");

                                                                if (pos != NULL && pos + 9 < response + bytesAvailable)
                                                                {
                                                                        if (strstr(pos + 8, "\"") != NULL)
                                                                        {
                                                                                nameLen = (uint8_t) (strstr(pos + 8, "\"") - (pos + 8));
                                                                                if (pos + 8 + nameLen < response + bytesAvailable)
                                                                                {
                                                                                       // NOTE: THIS LINE IS CAUSING THE ERROR
                                                                                        strncpy(groupNames[nameIdx], pos + 8, nameLen);
                                                                                        for (int i = 0; i < nameLen; i++)
                                                                                        {
                                                                                                //groupNames[nameIdx][i] = pos[8 + i];
                                                                                                //Serial.print(groupNames[nameIdx][i]);
                                                                                        }
                                                                                        Serial.println(groupNames[nameIdx]);
                                                                                }
                                                                                nameIdx++;
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                } else {
                        Serial.println("certificate doesn't match");
                }
        }
        else
        {
                Serial.println("Connection Failed");
        }

        Serial.println();
        Serial.println("Request Complete!!");
}

void readPin()
{
        // Button up
        if (digitalRead(0) == HIGH)
        {
                if (prevStartTime != 0 && startTime - prevStartTime <= CLICK_DELAY)
                {
                        clickType = DOUBLE_CLICK;
                }
                else if (millis() - startTime <= CLICK_DELAY)
                {
                        clickType = SINGLE_CLICK;
                }
        }
        // Button down
        else
        {
                clickType = HOLD_CLICK;
                prevStartTime = startTime;
                startTime = millis();
        }
}

void setup() {
        // put your setup code here, to run once:
        Serial.begin(115200);
        delay(1000);
        //WiFiManager wifiManager;
        WiFi.disconnect(true);
        WiFi.begin("ssid", "password");

        while (WiFi.status() != WL_CONNECTED)
        {
                delay(500);
                Serial.print(".");
        }
        Serial.println();
        string ssid("");
        char id[10];
        if (SPIFFS.begin()) {
                File config = SPIFFS.open("/groupme", "r");
                File js;

                if (config)
                {
                        Serial.println(config.size());
                        groupmeKey = config.readStringUntil('\n').c_str();
                        Serial.print("GroupMe API Key: ");
                        Serial.println(groupmeKey.c_str());
                        groupmeRequest(GROUPME_INDEX);
                }

                config.close();
                SPIFFS.end();
                attachInterrupt(digitalPinToInterrupt(0), readPin, CHANGE);
        }
}

void loop() {
        // put your main code here, to run repeatedly:
        currentTime = millis();
        if (clickType == DOUBLE_CLICK)
        {
                Serial.println("Double press");
                clickType = 0;
        }
        else if (clickType == SINGLE_CLICK && currentTime - startTime > CLICK_DELAY)
        {
                Serial.println("Single press");
                clickType = 0;
        }
        else if (clickType == HOLD_CLICK && currentTime - startTime > HOLD_LENGTH)
        {
                Serial.println("Button Hold");
                clickType = 0;
        }

}
@devyte
Copy link
Collaborator

devyte commented Oct 10, 2017

@timlag1305 strncpy may not null-terminate the result of the copy. I don't know if that is your case, your sketch is way too big to analyze, but it's the most common mistake in its use.
In any case, this is not the right place to ask for help with your particular code.
Closing per #3655 .

@devyte devyte closed this as completed Oct 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants