Added checks to handle corrupted telegrams.

pull/47/head 0.9.17
weetmuts 2019-11-04 00:35:00 +01:00
rodzic d5adff8918
commit bb7927f8f1
7 zmienionych plików z 132 dodań i 57 usunięć

12
CHANGES
Wyświetl plik

@ -1,4 +1,16 @@
Version 0.9.17: 2019-11-03
Thanks to afl-fuzz I found and added
some (in retrospect pretty obvious)
missing checks to deal with
corrupted telegrams that crashed
wmbusmeters.
Now afl-fuzz does not trigger any crash
after running for a couple of minutes.
Lets fuzz more....
Version 0.9.16: 2019-11-03
Significant rewrite of serial.cc.

Wyświetl plik

@ -678,16 +678,30 @@ void Telegram::addMoreExplanation(int pos, const char* fmt, ...)
}
}
void Telegram::parse(vector<uchar> &frame)
bool Telegram::parse(vector<uchar> &frame)
{
vector<uchar>::iterator bytes = frame.begin();
parsed.clear();
if (frame.size() == 0) return;
if (frame.size() == 0) return false;
if (frame.size() < 11)
{
verbose("(wmbus) cannot parse telegram with length %zu\n", frame.size());
return false;
}
len = frame[0];
if ((int)len+1 > (int)frame.size())
{
// I have some bad test data, that needs to be cleaned out...
verbose("(wmbus) error not enough bytes frame=%zu but len=%zu\n", frame.size(), len);
}
if ((int)len+1 != (int)frame.size())
{
// I have some bad test data, that needs to be cleaned out...
verbose("(wmbus) discrepancy frame=%zu should be len=%zu\n", frame.size(), len);
}
addExplanation(bytes, 1, "%02x length (%d bytes)", len, len);
c_field = frame[1];
addExplanation(bytes, 1, "%02x c-field (%s)", c_field, cType(c_field).c_str());
if (frame.size() < 4) return;
m_field = frame[3]<<8 | frame[2];
string man = manufacturerFlag(m_field);
addExplanation(bytes, 2, "%02x%02x m-field (%02x=%s)", frame[2], frame[3], m_field, man.c_str());
@ -710,10 +724,18 @@ void Telegram::parse(vector<uchar> &frame)
addExplanation(bytes, 1, "%02x ci-field (%s)", ci_field, ciType(ci_field).c_str());
int header_size = 0;
if (ci_field == 0x78) {
if (ci_field == 0x78)
{
header_size = 0; // And no encryption possible.
} else
if (ci_field == 0x72) {
}
else if (ci_field == 0x72)
{
if (frame.size() < 22)
{
verbose("(wmbus) cannot parse telegram ci=0x72 with length %zu\n", frame.size());
return false;
}
// Example, begins aith frame[11]: 99999999 MMMM VV TT 01 00 0000
// Ignore 4 id bytes for now, should perhaps check that they are identical with the id.
// But if they are not, what to do?
@ -739,8 +761,14 @@ void Telegram::parse(vector<uchar> &frame)
if (config_info.length() > 0) config_info.pop_back();
addExplanation(bytes, 2, "%02x%02x config (%s)", frame[13], frame[14], config_info.c_str());
header_size = 4+8;
} else
if (ci_field == 0x7a) {
}
else if (ci_field == 0x7a)
{
if (frame.size() < 15)
{
verbose("(wmbus) cannot parse telegram ci=0x7a with length %zu\n", frame.size());
return false;
}
acc = frame[11];
addExplanation(bytes, 1, "%02x acc", acc);
status = frame[12];
@ -760,14 +788,27 @@ void Telegram::parse(vector<uchar> &frame)
if (config_info.length() > 0) config_info.pop_back();
addExplanation(bytes, 2, "%02x%02x config (%s)", frame[13], frame[14], config_info.c_str());
header_size = 4;
} else
if (ci_field == 0x8d || ci_field == 0x8c) {
}
else if (ci_field == 0x8d || ci_field == 0x8c)
{
if (frame.size() < 13)
{
verbose("(wmbus) cannot parse telegram ci=0x8d or 0x8c with length %zu\n", frame.size());
return false;
}
cc_field = frame[11];
addExplanation(bytes, 1, "%02x cc-field (%s)", cc_field, ccType(cc_field).c_str());
acc = frame[12];
addExplanation(bytes, 1, "%02x acc", acc);
header_size = 2;
if (ci_field == 0x8d) {
if (ci_field == 0x8d)
{
if (frame.size() < 17)
{
verbose("(wmbus) cannot parse telegram ci=0x8d with length %zu\n", frame.size());
return false;
}
sn[0] = frame[13];
sn[1] = frame[14];
sn[2] = frame[15];
@ -775,8 +816,9 @@ void Telegram::parse(vector<uchar> &frame)
addExplanation(bytes, 4, "%02x%02x%02x%02x sn", sn[0], sn[1], sn[2], sn[3]);
header_size = 6;
}
} else
if (ci_field == 0xa2) {
}
else if (ci_field == 0xa2)
{
// Manufacturer specific telegram payload. Oh well....
}
else
@ -786,7 +828,13 @@ void Telegram::parse(vector<uchar> &frame)
}
payload.clear();
payload.insert(payload.end(), frame.begin()+(11+header_size), frame.end());
int skip = 11+header_size;
if (skip < (int)frame.size())
{
// The case 11+header_size larger than frame_size is probably due to bad input data
// that is currently allowed to pass, see above.
payload.insert(payload.end(), frame.begin()+skip, frame.end());
}
verbose("(wmbus) received telegram");
verboseFields();
debugPayload("(wmbus) frame", frame);
@ -794,6 +842,7 @@ void Telegram::parse(vector<uchar> &frame)
if (isDebugEnabled()) {
explainParse("(wmbus)", 0);
}
return true;
}
void Telegram::explainParse(string intro, int from)

Wyświetl plik

@ -164,7 +164,7 @@ struct Telegram {
bool handled {}; // Set to true, when a meter has accepted the telegram.
void parse(vector<uchar> &payload);
bool parse(vector<uchar> &payload);
void print();
void verboseFields();

Wyświetl plik

@ -393,17 +393,21 @@ void WMBusAmber::handleMessage(int msgid, vector<uchar> &frame)
case (0):
{
Telegram t;
t.parse(frame);
bool handled = false;
for (auto f : telegram_listeners_)
bool ok = t.parse(frame);
if (ok)
{
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(amb8465) telegram ignored by all configured meters!\n");
bool handled = false;
for (auto f : telegram_listeners_)
{
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(amb8465) telegram ignored by all configured meters!\n");
}
}
break;
}

Wyświetl plik

@ -559,17 +559,20 @@ void WMBusIM871A::handleRadioLink(int msgid, vector<uchar> &payload)
case RADIOLINK_MSG_WMBUSMSG_IND: // 0x03
{
Telegram t;
t.parse(payload);
bool handled = false;
for (auto f : telegram_listeners_) {
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
bool ok = t.parse(payload);
if (ok)
{
verbose("(im871a) telegram ignored by all configured meters!\n");
bool handled = false;
for (auto f : telegram_listeners_) {
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(im871a) telegram ignored by all configured meters!\n");
}
}
}
break;

Wyświetl plik

@ -131,7 +131,7 @@ FrameStatus WMBusRawTTY::checkRawTTYFrame(vector<uchar> &data,
// Ugly: 00615B2A442D2C998734761B168D2021D0871921|58387802FF2071000413F81800004413F8180000615B
// Here the frame is prefixed with some random data.
if (data.size() == 0) {
if (data.size() < 11) {
return PartialFrame;
}
int payload_len = data[0];
@ -226,19 +226,22 @@ void WMBusRawTTY::processSerialData()
void WMBusRawTTY::handleMessage(vector<uchar> &frame)
{
Telegram t;
t.parse(frame);
bool handled = false;
for (auto f : telegram_listeners_)
bool ok = t.parse(frame);
if (ok)
{
Telegram copy = t;
if (f) {
f(&copy);
bool handled = false;
for (auto f : telegram_listeners_)
{
Telegram copy = t;
if (f) {
f(&copy);
}
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(rawtty) telegram ignored by all configured meters!\n");
}
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(rawtty) telegram ignored by all configured meters!\n");
}
}

Wyświetl plik

@ -192,17 +192,21 @@ void WMBusRTLWMBUS::processSerialData()
void WMBusRTLWMBUS::handleMessage(vector<uchar> &frame)
{
Telegram t;
t.parse(frame);
bool handled = false;
for (auto f : telegram_listeners_)
bool ok = t.parse(frame);
if (ok)
{
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(rtlwmbus) telegram ignored by all configured meters!\n");
bool handled = false;
for (auto f : telegram_listeners_)
{
Telegram copy = t;
if (f) f(&copy);
if (copy.handled) handled = true;
}
if (isVerboseEnabled() && !handled)
{
verbose("(rtlwmbus) telegram ignored by all configured meters!\n");
}
}
}