You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
WiFiManagerParameter class does not properly handle the C++ move constructor resulting in a use after free error.
Reproduction Example
#include<vector>
#include<WiFiManager.h>// https://github.com/tzapu/WiFiManager
WiFiManager wm;
std::vector<WiFiManagerParameter> parameters;
voidsetup() {
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP // put your setup code here, to run once:
Serial.begin(115200);
// Allocate new space and construct parameter in it
parameters.emplace_back("id1", "field1", "", 40);
// Allocate new space, move "id1" and construct parameter after it
parameters.emplace_back("id2", "field2", "", 40);
// Add parameters to WiFiManagerfor (auto& param : parameters)
{
wm.addParameter(¶m);
}
wm.setConfigPortalBlocking(false);
//automatically connect using saved credentials if they exist//If connection fails it starts an access point with the specified nameif(wm.autoConnect("AutoConnectAP")){
Serial.println("connected...yeey :)");
}
else {
Serial.println("Configportal running");
}
wm.startConfigPortal();
}
voidloop() {
wm.process();
}
I added a log message to record the alloc/free's:
new char allocated id1
new char allocated id2
char freed id1
*wm:[2] Added Parameter: id1
*wm:[2] Added Parameter: id2
This constructor doesn't correctly handle moving the _value data which is then freed. A similar issue is presumably why the copy constructor is made private.
There are three fixes that I see.
Delete the move constructor. My example doesn't build if I add: WiFiManagerParameter(const WiFiManagerParameter&&) = delete;
Implement a move (and optionally a copy) constructor. This is fairly straightforward and just needs to correctly propagate the pointer to the new instance and set the old instance to NULL so it isn't freed.
Switch to using smart pointers. I took this approach for a fork I made for testing. Here's the code:
classWiFiManagerParameter {
public:/** Create custom parameters that can be added to the WiFiManager setup web page @id is used for HTTP queries and must not contain spaces nor other special characters*/WiFiManagerParameter() = default;
WiFiManagerParameter(constchar *custom);
WiFiManagerParameter(constchar *id, constchar *label);
WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length);
WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom);
WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom, int labelPlacement);
constchar *getID() const;
constchar *getValue() const;
constchar *getLabel() const;
constchar *getPlaceholder() const; // @deprecated, use getLabelintgetValueLength() const;
intgetLabelPlacement() const;
virtualconstchar *getCustomHTML() const;
voidsetValue(constchar *defaultValue, int length);
protected:voidinit(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom, int labelPlacement);
private:constchar *_id = nullptr;
constchar *_label = nullptr;
std::unique_ptr<char[]> _value;
int _length = 1;
int _labelPlacement = WFM_LABEL_BEFORE;
protected:constchar *_customHTML = "";
friendclassWiFiManager;
};
WiFiManagerParameter::WiFiManagerParameter(constchar *custom) {
_customHTML = custom;
}
WiFiManagerParameter::WiFiManagerParameter(constchar *id, constchar *label) {
init(id, label, "", 0, "", WFM_LABEL_BEFORE);
}
WiFiManagerParameter::WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length) {
init(id, label, defaultValue, length, "", WFM_LABEL_BEFORE);
}
WiFiManagerParameter::WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom) {
init(id, label, defaultValue, length, custom, WFM_LABEL_BEFORE);
}
WiFiManagerParameter::WiFiManagerParameter(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom, int labelPlacement) {
init(id, label, defaultValue, length, custom, labelPlacement);
}
voidWiFiManagerParameter::init(constchar *id, constchar *label, constchar *defaultValue, int length, constchar *custom, int labelPlacement) {
_id = id;
_label = label;
_labelPlacement = labelPlacement;
_customHTML = custom;
setValue(defaultValue,length);
}
// WiFiManagerParameter& WiFiManagerParameter::operator=(const WiFiManagerParameter& rhs){// Serial.println("copy assignment op called");// (*this->_value) = (*rhs._value);// return *this;// }// @note debug is not available in wmparameter classvoidWiFiManagerParameter::setValue(constchar *defaultValue, int length) {
if(!_id){
// Serial.println("cannot set value of this parameter");return;
}
// if(strlen(defaultValue) > length){// // Serial.println("defaultValue length mismatch");// // return false; //@todo bail // }
_length = length;
_value = std::make_unique<char[]>(_length + 1);
memset(_value.get(), 0, _length + 1); // explicit nullif (defaultValue != NULL) {
strncpy(_value.get(), defaultValue, _length);
}
}
constchar* WiFiManagerParameter::getValue() const {
// Serial.println(printf("Address of _value is %p\n", (void *)_value)); return _value.get();
}
constchar* WiFiManagerParameter::getID() const {
return _id;
}
constchar* WiFiManagerParameter::getPlaceholder() const {
return _label;
}
constchar* WiFiManagerParameter::getLabel() const {
return _label;
}
intWiFiManagerParameter::getValueLength() const {
return _length;
}
intWiFiManagerParameter::getLabelPlacement() const {
return _labelPlacement;
}
constchar* WiFiManagerParameter::getCustomHTML() const {
return _customHTML;
}
Note: the copy constructor is implicitly deleted by the inclusion of the unique_ptr.
A simple copy constructor could be implemented with:
Hardware
WiFimanager Branch/Release: Master commit 810f144
Esp8266/Esp32:
Hardware: D1-mini lite
Description
WiFiManagerParameter class does not properly handle the C++ move constructor resulting in a use after free error.
Reproduction Example
I added a log message to record the alloc/free's:
Solution
What's happening here is that the
std::vector
is calling the implicit move constructor: https://en.cppreference.com/w/cpp/language/move_constructorThis constructor doesn't correctly handle moving the
_value
data which is then freed. A similar issue is presumably why the copy constructor is made private.There are three fixes that I see.
WiFiManagerParameter(const WiFiManagerParameter&&) = delete;
Note: the copy constructor is implicitly deleted by the inclusion of the unique_ptr.
A simple copy constructor could be implemented with:
The only change to the rest of the project was replacing the line:
value.toCharArray(_params[i]->_value, _params[i]->_length+1); // length+1 null terminated
with
value.toCharArray(_params[i]->_value.get(), _params[i]->_length+1); // length+1 null terminated
The text was updated successfully, but these errors were encountered: