espalexa robustness improvements

* prevent string buffer overflows (stack corruption)
* avoid division by zero (program might crash)
* avoid log(0) which is undefined, too
* use faster math routines for float (logf, powf)
This commit is contained in:
Frank 2023-05-21 13:21:38 +02:00
parent 1c8f349a62
commit a717238f76
2 changed files with 35 additions and 21 deletions

View File

@ -10,7 +10,7 @@
*/ */
/* /*
* @title Espalexa library * @title Espalexa library
* @version 2.7.0 * @version 2.7.1
* @author Christian Schwinne * @author Christian Schwinne
* @license MIT * @license MIT
* @contributors d-999 * @contributors d-999
@ -50,7 +50,7 @@
#include "../network/Network.h" #include "../network/Network.h"
#ifdef ESPALEXA_DEBUG #ifdef ESPALEXA_DEBUG
#pragma message "Espalexa 2.7.0 debug mode" #pragma message "Espalexa 2.7.1 debug mode"
#define EA_DEBUG(x) Serial.print (x) #define EA_DEBUG(x) Serial.print (x)
#define EA_DEBUGLN(x) Serial.println (x) #define EA_DEBUGLN(x) Serial.println (x)
#else #else
@ -142,7 +142,7 @@ private:
} }
//device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001) //device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
void deviceJsonString(EspalexaDevice* dev, char* buf) void deviceJsonString(EspalexaDevice* dev, char* buf, size_t maxBuf) // softhack007 "size" parameter added, to avoid buffer overrun
{ {
char buf_lightid[27]; char buf_lightid[27];
encodeLightId(dev->getId() + 1, buf_lightid); encodeLightId(dev->getId() + 1, buf_lightid);
@ -153,19 +153,19 @@ private:
//TODO: %f is not working for some reason on ESP8266 in v0.11.0 (was fine in 0.10.2). Need to investigate //TODO: %f is not working for some reason on ESP8266 in v0.11.0 (was fine in 0.10.2). Need to investigate
//sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%f,%f]") //sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%f,%f]")
// ,dev->getHue(), dev->getSat(), dev->getX(), dev->getY()); // ,dev->getHue(), dev->getSat(), dev->getX(), dev->getY());
sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%s,%s]"),dev->getHue(), dev->getSat(), snprintf_P(buf_col, sizeof(buf_col), PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%s,%s]"),dev->getHue(), dev->getSat(),
((String)dev->getX()).c_str(), ((String)dev->getY()).c_str()); ((String)dev->getX()).c_str(), ((String)dev->getY()).c_str());
char buf_ct[16] = ""; char buf_ct[16] = "";
//white spectrum support //white spectrum support
if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color) if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color)
sprintf(buf_ct, ",\"ct\":%u", dev->getCt()); snprintf(buf_ct, sizeof(buf_ct), ",\"ct\":%u", dev->getCt());
char buf_cm[20] = ""; char buf_cm[20] = "";
if (static_cast<uint8_t>(dev->getType()) > 1) if (static_cast<uint8_t>(dev->getType()) > 1)
sprintf(buf_cm,PSTR("\",\"colormode\":\"%s"), modeString(dev->getColorMode())); snprintf(buf_cm, sizeof(buf_cm), PSTR("\",\"colormode\":\"%s"), modeString(dev->getColorMode()));
sprintf_P(buf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true}," snprintf_P(buf, maxBuf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true},"
"\"type\":\"%s\",\"name\":\"%s\",\"modelid\":\"%s\",\"manufacturername\":\"Philips\",\"productname\":\"E%u" "\"type\":\"%s\",\"name\":\"%s\",\"modelid\":\"%s\",\"manufacturername\":\"Philips\",\"productname\":\"E%u"
"\",\"uniqueid\":\"%s\",\"swversion\":\"espalexa-2.7.0\"}") "\",\"uniqueid\":\"%s\",\"swversion\":\"espalexa-2.7.0\"}")
@ -219,10 +219,10 @@ private:
EA_DEBUGLN("# Responding to description.xml ... #\n"); EA_DEBUGLN("# Responding to description.xml ... #\n");
IPAddress localIP = Network.localIP(); IPAddress localIP = Network.localIP();
char s[16]; char s[16];
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); snprintf(s, sizeof(s), "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
char buf[1024]; char buf[1024];
sprintf_P(buf,PSTR("<?xml version=\"1.0\" ?>" snprintf_P(buf, sizeof(buf), PSTR("<?xml version=\"1.0\" ?>"
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
"<specVersion><major>1</major><minor>0</minor></specVersion>" "<specVersion><major>1</major><minor>0</minor></specVersion>"
"<URLBase>http://%s:80/</URLBase>" "<URLBase>http://%s:80/</URLBase>"
@ -297,7 +297,7 @@ private:
char buf[1024]; char buf[1024];
sprintf_P(buf,PSTR("HTTP/1.1 200 OK\r\n" snprintf_P(buf, sizeof(buf), PSTR("HTTP/1.1 200 OK\r\n"
"EXT:\r\n" "EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL "CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
"LOCATION: http://%s:80/description.xml\r\n" "LOCATION: http://%s:80/description.xml\r\n"
@ -493,7 +493,7 @@ public:
unsigned idx = decodeLightKey(devId); unsigned idx = decodeLightKey(devId);
EA_DEBUGLN(idx); EA_DEBUGLN(idx);
char buf[50]; char buf[50];
sprintf_P(buf,PSTR("[{\"success\":{\"/lights/%u/state/\": true}}]"),devId); snprintf_P(buf,sizeof(buf),PSTR("[{\"success\":{\"/lights/%u/state/\": true}}]"),devId);
server->send(200, "application/json", buf); server->send(200, "application/json", buf);
if (idx >= currentDeviceCount) return true; //return if invalid ID if (idx >= currentDeviceCount) return true; //return if invalid ID
EspalexaDevice* dev = devices[idx]; EspalexaDevice* dev = devices[idx];
@ -571,7 +571,7 @@ public:
jsonTemp += ':'; jsonTemp += ':';
char buf[512]; char buf[512];
deviceJsonString(devices[i], buf); deviceJsonString(devices[i], buf, sizeof(buf)-1);
jsonTemp += buf; jsonTemp += buf;
if (i < currentDeviceCount-1) jsonTemp += ','; if (i < currentDeviceCount-1) jsonTemp += ',';
} }
@ -588,7 +588,7 @@ public:
return true; return true;
} }
char buf[512]; char buf[512];
deviceJsonString(devices[idx], buf); deviceJsonString(devices[idx], buf, sizeof(buf)-1);
server->send(200, "application/json", buf); server->send(200, "application/json", buf);
} }

View File

@ -2,6 +2,15 @@
#include "EspalexaDevice.h" #include "EspalexaDevice.h"
// debug macros
#ifdef ESPALEXA_DEBUG
#define EA_DEBUG(x) Serial.print (x)
#define EA_DEBUGLN(x) Serial.println (x)
#else
#define EA_DEBUG(x)
#define EA_DEBUGLN(x)
#endif
EspalexaDevice::EspalexaDevice(){} EspalexaDevice::EspalexaDevice(){}
EspalexaDevice::EspalexaDevice(String deviceName, BrightnessCallbackFunction gnCallback, uint8_t initialValue) { //constructor for dimmable device EspalexaDevice::EspalexaDevice(String deviceName, BrightnessCallbackFunction gnCallback, uint8_t initialValue) { //constructor for dimmable device
@ -124,18 +133,21 @@ uint32_t EspalexaDevice::getRGB()
{ {
//TODO tweak a bit to match hue lamp characteristics //TODO tweak a bit to match hue lamp characteristics
//based on https://gist.github.com/paulkaplan/5184275 //based on https://gist.github.com/paulkaplan/5184275
float temp = 10000/ _ct; //kelvins = 1,000,000/mired (and that /100) float temp = (_ct != 0) ? (10000/ _ct) : 2; //kelvins = 1,000,000/mired (and that /100) softhack007: avoid division by zero - using "2" as substitute
float r, g, b; float r, g, b;
#ifdef ESPALEXA_DEBUG
if (_ct == 0) {EA_DEBUGLN(F("EspalexaDevice::getRGB() Warning: ct = 0!"));}
#endif
if (temp <= 66) { if (temp <= 66) {
r = 255; r = 255;
g = temp; g = temp;
g = 99.470802 * log(g) - 161.119568; g = 99.470802 * logf(g) - 161.119568;
if (temp <= 19) { if (temp <= 19) {
b = 0; b = 0;
} else { } else {
b = temp-10; b = temp-10;
b = 138.517731 * log(b) - 305.044793; b = 138.517731 * logf(b) - 305.044793;
} }
} else { } else {
r = temp - 60; r = temp - 60;
@ -192,9 +204,9 @@ uint32_t EspalexaDevice::getRGB()
b = 1.0f; b = 1.0f;
} }
// Apply gamma correction // Apply gamma correction
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f; r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * powf(r, (1.0f / 2.4f)) - 0.055f;
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f; g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * powf(g, (1.0f / 2.4f)) - 0.055f;
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f; b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * powf(b, (1.0f / 2.4f)) - 0.055f;
if (r > b && r > g) { if (r > b && r > g) {
// red is biggest // red is biggest
@ -328,8 +340,10 @@ void EspalexaDevice::setColor(uint8_t r, uint8_t g, uint8_t b)
float X = r * 0.664511f + g * 0.154324f + b * 0.162028f; float X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f; float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f; float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;
if ((r+g+b) > 0) { // softhack007: avoid division by zero
_x = X / (X + Y + Z); _x = X / (X + Y + Z);
_y = Y / (X + Y + Z); _y = Y / (X + Y + Z);
} else { _x = _y = 0.5f;} // softhack007: use default values in case of "black"
_rgb = ((r << 16) | (g << 8) | b); _rgb = ((r << 16) | (g << 8) | b);
_mode = EspalexaColorMode::xy; _mode = EspalexaColorMode::xy;
} }