diff options
author | Dan Albert <danalbert@google.com> | 2015-04-07 18:43:15 -0700 |
---|---|---|
committer | Dan Albert <danalbert@google.com> | 2015-04-08 14:12:21 -0700 |
commit | 1b4f316651096f0ef9301b4ffde4816a08a54ab5 (patch) | |
tree | f141604343f4114337483ed0fb30fe36f4750310 /libs/androidfw | |
parent | 47c1cf4b35616059409a0674382c2318494e877b (diff) | |
download | frameworks_base-1b4f316651096f0ef9301b4ffde4816a08a54ab5.zip frameworks_base-1b4f316651096f0ef9301b4ffde4816a08a54ab5.tar.gz frameworks_base-1b4f316651096f0ef9301b4ffde4816a08a54ab5.tar.bz2 |
Fix UB in ResourceTable::stringToInt.
Was here because UBsan found integer overflow in the parsing for hex
numbers, since hex numbers here are actually unsigned but assigned to
a signed integer.
Also fixes a number of missing error conditions.
Change-Id: Iaea576daedfc6c75521cde02de3fe9dd0198a3b7
Diffstat (limited to 'libs/androidfw')
-rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 74 | ||||
-rw-r--r-- | libs/androidfw/tests/Android.mk | 30 | ||||
-rw-r--r-- | libs/androidfw/tests/ResTable_test.cpp | 81 |
3 files changed, 151 insertions, 34 deletions
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 7241069..fbe08ec 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,6 +17,16 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <ctype.h> +#include <memory.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <limits> +#include <type_traits> + #include <androidfw/ByteBucketArray.h> #include <androidfw/ResourceTypes.h> #include <androidfw/TypeWrappers.h> @@ -27,17 +37,10 @@ #include <utils/String16.h> #include <utils/String8.h> -#ifdef HAVE_ANDROID_OS +#ifdef __ANDROID__ #include <binder/TextOutput.h> #endif -#include <stdlib.h> -#include <string.h> -#include <memory.h> -#include <ctype.h> -#include <stdint.h> -#include <stddef.h> - #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) #endif @@ -4551,8 +4554,7 @@ static bool parse_unit(const char* str, Res_value* outValue, return false; } - -bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue) { while (len > 0 && isspace16(*s)) { s++; @@ -4564,7 +4566,7 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) } size_t i = 0; - int32_t val = 0; + int64_t val = 0; bool neg = false; if (*s == '-') { @@ -4576,28 +4578,50 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) return false; } + static_assert(std::is_same<uint32_t, Res_value::data_type>::value, + "Res_value::data_type has changed. The range checks in this " + "function are no longer correct."); + // Decimal or hex? - if (s[i] == '0' && s[i+1] == 'x') { - if (outValue) - outValue->dataType = outValue->TYPE_INT_HEX; + bool isHex; + if (len > 1 && s[i] == '0' && s[i+1] == 'x') { + isHex = true; i += 2; + + if (neg) { + return false; + } + + if (i == len) { + // Just u"0x" + return false; + } + bool error = false; while (i < len && !error) { val = (val*16) + get_hex(s[i], &error); i++; + + if (val > std::numeric_limits<uint32_t>::max()) { + return false; + } } if (error) { return false; } } else { - if (outValue) - outValue->dataType = outValue->TYPE_INT_DEC; + isHex = false; while (i < len) { if (s[i] < '0' || s[i] > '9') { return false; } val = (val*10) + s[i]-'0'; i++; + + if ((neg && -val < std::numeric_limits<int32_t>::min()) || + (!neg && val > std::numeric_limits<int32_t>::max())) { + return false; + } } } @@ -4607,13 +4631,21 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) i++; } - if (i == len) { - if (outValue) - outValue->data = val; - return true; + if (i != len) { + return false; } - return false; + if (outValue) { + outValue->dataType = + isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC; + outValue->data = static_cast<Res_value::data_type>(val); + } + return true; +} + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + return U16StringToInt(s, len, outValue); } bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 58b78b5..a353575 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -19,6 +19,7 @@ # targets here. # ========================================================== LOCAL_PATH:= $(call my-dir) + testFiles := \ AttributeFinder_test.cpp \ ByteBucketArray_test.cpp \ @@ -32,27 +33,33 @@ testFiles := \ TypeWrappers_test.cpp \ ZipUtils_test.cpp +androidfw_test_cflags := \ + -Wall \ + -Werror \ + -Wunused \ + -Wunreachable-code \ + -Wno-missing-field-initializers \ + +# gtest is broken. +androidfw_test_cflags += -Wno-unnamed-type-template-args + # ========================================================== # Build the host tests: libandroidfw_tests # ========================================================== include $(CLEAR_VARS) LOCAL_MODULE := libandroidfw_tests - -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -# gtest is broken. -LOCAL_CFLAGS += -Wno-unnamed-type-template-args - +LOCAL_CFLAGS := $(androidfw_test_cflags) LOCAL_SRC_FILES := $(testFiles) LOCAL_STATIC_LIBRARIES := \ libandroidfw \ libutils \ libcutils \ - liblog + liblog \ + libz \ include $(BUILD_HOST_NATIVE_TEST) - # ========================================================== # Build the device tests: libandroidfw_tests # ========================================================== @@ -60,14 +67,11 @@ ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) LOCAL_MODULE := libandroidfw_tests - -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -# gtest is broken. -LOCAL_CFLAGS += -Wno-unnamed-type-template-args - +LOCAL_CFLAGS := $(androidfw_test_cflags) LOCAL_SRC_FILES := $(testFiles) \ BackupData_test.cpp \ - ObbFile_test.cpp + ObbFile_test.cpp \ + LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libcutils \ diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 6a9314e..dcfe91e 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -16,6 +16,10 @@ #include <androidfw/ResourceTypes.h> +#include <codecvt> +#include <locale> +#include <string> + #include <utils/String8.h> #include <utils/String16.h> #include "TestHelpers.h" @@ -201,4 +205,81 @@ TEST(ResTableTest, emptyTableHasSensibleDefaults) { ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0); } +void testU16StringToInt(const char16_t* str, uint32_t expectedValue, + bool expectSuccess, bool expectHex) { + size_t len = std::char_traits<char16_t>::length(str); + + // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; + std::string s = convert.to_bytes(std::u16string(str, len)); + + Res_value out = {}; + ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) + << "Failed with " << s; + + if (!expectSuccess) { + ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; + return; + } + + if (expectHex) { + ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; + } else { + ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; + } + + ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; +} + +TEST(ResTableTest, U16StringToInt) { + testU16StringToInt(u"", 0U, false, false); + testU16StringToInt(u" ", 0U, false, false); + testU16StringToInt(u"\t\n", 0U, false, false); + + testU16StringToInt(u"abcd", 0U, false, false); + testU16StringToInt(u"10abcd", 0U, false, false); + testU16StringToInt(u"42 42", 0U, false, false); + testU16StringToInt(u"- 42", 0U, false, false); + testU16StringToInt(u"-", 0U, false, false); + + testU16StringToInt(u"0x", 0U, false, true); + testU16StringToInt(u"0xnope", 0U, false, true); + testU16StringToInt(u"0X42", 0U, false, true); + testU16StringToInt(u"0x42 0x42", 0U, false, true); + testU16StringToInt(u"-0x0", 0U, false, true); + testU16StringToInt(u"-0x42", 0U, false, true); + testU16StringToInt(u"- 0x42", 0U, false, true); + + // Note that u" 42" would pass. This preserves the old behavior, but it may + // not be desired. + testU16StringToInt(u"42 ", 0U, false, false); + testU16StringToInt(u"0x42 ", 0U, false, true); + + // Decimal cases. + testU16StringToInt(u"0", 0U, true, false); + testU16StringToInt(u"-0", 0U, true, false); + testU16StringToInt(u"42", 42U, true, false); + testU16StringToInt(u" 42", 42U, true, false); + testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u"042", 42U, true, false); + testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); + + // Hex cases. + testU16StringToInt(u"0x0", 0x0, true, true); + testU16StringToInt(u"0x42", 0x42, true, true); + testU16StringToInt(u" 0x42", 0x42, true, true); + + // Just before overflow cases: + testU16StringToInt(u"2147483647", INT_MAX, true, false); + testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, + false); + testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); + + // Overflow cases: + testU16StringToInt(u"2147483648", 0U, false, false); + testU16StringToInt(u"-2147483649", 0U, false, false); + testU16StringToInt(u"0x1ffffffff", 0U, false, true); +} + } |