Skip to content

Commit f1b55a0

Browse files
committed
Refactor: libcrmcommon: Sanity-check remote message and buffer sizes
Coverity has been complaining about the payload_offset and payload_uncompressed being tainted scalar values. There's only so much validation we can do when we're reading from a socket. But apparently this is enough, because it makes the Coverity errors go away. There's a lot more room for improvement in the remote message processing. I found a few bugs a while back that we need to fix involving multiple messages received in rapid succession. This is an improvement for now. Note that I got rid of the CRM_LOG_ASSERT() line that subtracts 1 from the index. As far as I can tell, that's an off-by-one error and we have no reason to expect that position to contain a null byte. The commit that added it doesn't have any information in the commit message or comments. Signed-off-by: Reid Wahl <[email protected]>
1 parent e5b649e commit f1b55a0

File tree

1 file changed

+60
-31
lines changed

1 file changed

+60
-31
lines changed

lib/common/remote.c

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -68,35 +68,47 @@ static struct remote_header_v0 *
6868
localized_remote_header(pcmk__remote_t *remote)
6969
{
7070
struct remote_header_v0 *header = NULL;
71-
uint32_t endian_swapped = 0;
7271

7372
if ((remote == NULL) || (remote->buffer == NULL)
7473
|| (remote->buffer_offset < sizeof(struct remote_header_v0))) {
74+
75+
// Caller error or we haven't received the full header yet
7576
return NULL;
7677
}
7778

7879
header = (struct remote_header_v0 *) remote->buffer;
79-
if (header->endian == ENDIAN_LOCAL) {
80-
return header;
81-
}
82-
83-
endian_swapped = GUINT32_SWAP_LE_BE(header->endian);
84-
CRM_CHECK(endian_swapped == ENDIAN_LOCAL,
85-
crm_err("Invalid message detected (endian mismatch): local magic "
86-
"number %" PRIx32 " matches neither the header's magic "
87-
"number %" PRIx32 " nor the byte-swapped form %" PRIx32,
88-
ENDIAN_LOCAL, header->endian, endian_swapped);
89-
return NULL);
90-
91-
header->endian = endian_swapped;
92-
header->version = GUINT32_SWAP_LE_BE(header->version);
93-
header->id = GUINT64_SWAP_LE_BE(header->id);
94-
header->flags = GUINT64_SWAP_LE_BE(header->flags);
95-
header->size_total = GUINT32_SWAP_LE_BE(header->size_total);
96-
header->payload_offset = GUINT32_SWAP_LE_BE(header->payload_offset);
97-
header->payload_compressed = GUINT32_SWAP_LE_BE(header->payload_compressed);
98-
header->payload_uncompressed =
99-
GUINT32_SWAP_LE_BE(header->payload_uncompressed);
80+
if (header->endian != ENDIAN_LOCAL) {
81+
uint32_t endian_swapped = GUINT32_SWAP_LE_BE(header->endian);
82+
83+
CRM_CHECK(endian_swapped == ENDIAN_LOCAL,
84+
crm_err("Invalid message detected (endian mismatch): local "
85+
"magic number %" PRIx32 " matches neither the "
86+
"header's magic number %" PRIx32 " nor the "
87+
"byte-swapped form %" PRIx32,
88+
ENDIAN_LOCAL, header->endian, endian_swapped);
89+
return NULL);
90+
91+
header->endian = endian_swapped;
92+
header->version = GUINT32_SWAP_LE_BE(header->version);
93+
header->id = GUINT64_SWAP_LE_BE(header->id);
94+
header->flags = GUINT64_SWAP_LE_BE(header->flags);
95+
header->size_total = GUINT32_SWAP_LE_BE(header->size_total);
96+
header->payload_offset = GUINT32_SWAP_LE_BE(header->payload_offset);
97+
header->payload_compressed =
98+
GUINT32_SWAP_LE_BE(header->payload_compressed);
99+
header->payload_uncompressed =
100+
GUINT32_SWAP_LE_BE(header->payload_uncompressed);
101+
}
102+
103+
// Sanity checks
104+
if (header->payload_offset != sizeof(struct remote_header_v0)) {
105+
return NULL;
106+
}
107+
if ((header->payload_offset
108+
+ header->payload_compressed
109+
+ header->payload_uncompressed) != header->size_total) {
110+
return NULL;
111+
}
100112

101113
return header;
102114
}
@@ -268,6 +280,8 @@ xmlNode *
268280
pcmk__remote_message_xml(pcmk__remote_t *remote)
269281
{
270282
xmlNode *xml = NULL;
283+
size_t data_size = 0;
284+
char *payload = NULL;
271285
struct remote_header_v0 *header = localized_remote_header(remote);
272286

273287
if (header == NULL) {
@@ -315,18 +329,33 @@ pcmk__remote_message_xml(pcmk__remote_t *remote)
315329
/* take ownership of the buffer */
316330
remote->buffer_offset = 0;
317331

318-
CRM_LOG_ASSERT(remote->buffer[sizeof(struct remote_header_v0) + header->payload_uncompressed - 1] == 0);
332+
data_size = (size_t) header->payload_offset + header->payload_uncompressed;
319333

320-
xml = pcmk__xml_parse(remote->buffer + header->payload_offset);
321-
if (xml == NULL && header->version > REMOTE_MSG_VERSION) {
322-
crm_warn("Couldn't parse v%d message, we only understand v%d",
323-
header->version, REMOTE_MSG_VERSION);
334+
// Ensure the buffer is as big as it should be
335+
CRM_CHECK(remote->buffer_size >= data_size, return NULL);
324336

325-
} else if (xml == NULL) {
326-
crm_err("Couldn't parse: '%.120s'", remote->buffer + header->payload_offset);
327-
}
337+
/* Ensure the buffer is null-terminated (see
338+
* pcmk__read_available_remote_data()).
339+
*
340+
* Note that payload_uncompressed contains the payload size including the
341+
* null byte (see pcmk__remote_send_xml()).
342+
*/
343+
CRM_CHECK(remote->buffer[data_size] == '\0', return NULL);
344+
345+
payload = remote->buffer + header->payload_offset;
346+
347+
xml = pcmk__xml_parse(payload);
348+
if (xml == NULL) {
349+
if (header->version > REMOTE_MSG_VERSION) {
350+
crm_warn("Couldn't parse v%d message, we only understand v%d",
351+
header->version, REMOTE_MSG_VERSION);
352+
} else {
353+
crm_err("Couldn't parse: '%.120s'", payload);
354+
}
328355

329-
crm_log_xml_trace(xml, "[remote msg]");
356+
} else {
357+
crm_log_xml_trace(xml, "[remote msg]");
358+
}
330359
return xml;
331360
}
332361

0 commit comments

Comments
 (0)