[RESTRICT AUTOMERGE] Fix potential out of bounds writes in LogEvent.
mUidFieldIndex, mExclusiveStateFieldIndex,
mAttributionChain{Start|End}Index are stored using int8_t.
These values can overflow when mValues.size() exceeds INT8_MAX.
Add proper bounds checking to ensure these values don't exceed
INT8_MAX.
Bug: 174488848
Bug: 174485572
Test: m
Test: statsd_test
Test: TreeHugger
Change-Id: I416e2de06ca2935017e15d4e91000652831e6b6c
(cherry picked from commit 53251a491faab1fb88e28b7cafe5913842b8d71d)
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index f56fa62..4f03172 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -19,6 +19,7 @@
#include <android-base/stringprintf.h>
#include <android/binder_ibinder.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "annotations.h"
@@ -216,13 +217,18 @@
last[2] = true;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
- // Check if at least one node was successfully parsed.
- if (mValues.size() - 1 > firstUidInChainIndex) {
+
+ if (mValues.size() - 1 > INT8_MAX) {
+ mValid = false;
+ } else if (mValues.size() - 1 > firstUidInChainIndex) {
+ // At least one node was successfully parsed.
mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
}
- parseAnnotations(numAnnotations, firstUidInChainIndex);
+ if (mValid) {
+ parseAnnotations(numAnnotations, firstUidInChainIndex);
+ }
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
@@ -234,7 +240,8 @@
}
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
- if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
+ if (mValues.empty() || mValues.size() - 1 > INT8_MAX || !checkPreviousValueType(INT)
+ || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
@@ -270,6 +277,12 @@
return;
}
+ if (static_cast<int>(mValues.size() - 1) < firstUidInChainIndex) { // AttributionChain is empty.
+ mValid = false;
+ android_errorWriteLog(0x534e4554, "174485572");
+ return;
+ }
+
const bool primaryField = readNextValue<uint8_t>();
mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
}
@@ -280,6 +293,12 @@
return;
}
+ if (mValues.size() - 1 > INT8_MAX) {
+ android_errorWriteLog(0x534e4554, "174488848");
+ mValid = false;
+ return;
+ }
+
const bool exclusiveState = readNextValue<uint8_t>();
mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 5c170c0..aed2547 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -363,6 +363,116 @@
EXPECT_EQ(event.getResetState(), resetState);
}
+TEST(LogEventTest, TestExclusiveStateAnnotationAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 64;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+ AStatsEvent_writeInt32(event, 1);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_EXCLUSIVE_STATE, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_EQ(-1, logEvent.getExclusiveStateFieldIndex());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestUidAnnotationAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 64;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+ AStatsEvent_writeInt32(event, 1);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_IS_UID, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_EQ(-1, logEvent.getUidFieldIndex());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestAttributionChainEndIndexAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 65;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_FALSE(logEvent.hasAttributionChain());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestEmptyAttributionChainWithPrimaryFieldFirstUidAnnotation) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ uint32_t uids[] = {};
+ const char* tags[] = {};
+
+ AStatsEvent_writeInt32(event, 10);
+ AStatsEvent_writeAttributionChain(event, uids, tags, 0);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+
+ AStatsEvent_release(event);
+}
+
} // namespace statsd
} // namespace os
} // namespace android