Escape comma and backslash for list props
Comma is used as a separator for list props. To support comma on
StringList properties, elements are now escaped with backslashes.
Bug: 147402003
Test: fuzzing parser and formatter locally, sysprop_test, boot
cuttlefish
Change-Id: I69f1b5e7ba12746d982e9b05a62945a8746d9487
diff --git a/CppGen.cpp b/CppGen.cpp
index 98ca12b..f047e7d 100644
--- a/CppGen.cpp
+++ b/CppGen.cpp
@@ -115,16 +115,19 @@
template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
Vec ret;
+ if (*str == '\0') return ret;
const char* p = str;
for (;;) {
- const char* found = p;
- while (*found != '\0' && *found != ',') {
- ++found;
+ const char* r = p;
+ std::string value;
+ while (*r != ',') {
+ if (*r == '\\') ++r;
+ if (*r == '\0') break;
+ value += *r++;
}
- std::string value(p, found);
ret.emplace_back(DoParse<typename Vec::value_type>(value.c_str()));
- if (*found == '\0') break;
- p = found + 1;
+ if (*r == '\0') break;
+ p = r + 1;
}
return ret;
}
@@ -164,10 +167,15 @@
bool first = true;
for (auto&& element : value) {
- if (!first) ret += ",";
+ if (!first) ret += ',';
else first = false;
if constexpr(std::is_same_v<T, std::optional<std::string>>) {
- if (element) ret += *element;
+ if (element) {
+ for (char c : *element) {
+ if (c == '\\' || c == ',') ret += '\\';
+ ret += c;
+ }
+ }
} else {
ret += FormatValue(element);
}
diff --git a/JavaGen.cpp b/JavaGen.cpp
index 9a6bae8..4ac45b1 100644
--- a/JavaGen.cpp
+++ b/JavaGen.cpp
@@ -42,6 +42,7 @@
constexpr const char* kJavaFileImports =
R"(import android.os.SystemProperties;
+import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.List;
@@ -53,7 +54,7 @@
)";
constexpr const char* kJavaParsersAndFormatters =
- R"(private static Boolean tryParseBoolean(String str) {
+ R"s(private static Boolean tryParseBoolean(String str) {
switch (str.toLowerCase(Locale.US)) {
case "1":
case "true":
@@ -107,8 +108,17 @@
List<T> ret = new ArrayList<>();
- for (String element : str.split(",")) {
- ret.add(elementParser.apply(element));
+ int p = 0;
+ for (;;) {
+ StringBuilder sb = new StringBuilder();
+ while (p < str.length() && str.charAt(p) != ',') {
+ if (str.charAt(p) == '\\') ++p;
+ if (p == str.length()) break;
+ sb.append(str.charAt(p++));
+ }
+ ret.add(elementParser.apply(sb.toString()));
+ if (p == str.length()) break;
+ ++p;
}
return ret;
@@ -126,11 +136,15 @@
return ret;
}
+private static String escape(String str) {
+ return str.replaceAll("([\\\\,])", "\\\\$1");
+}
+
private static <T> String formatList(List<T> list) {
StringJoiner joiner = new StringJoiner(",");
for (T element : list) {
- joiner.add(element == null ? "" : element.toString());
+ joiner.add(element == null ? "" : escape(element.toString()));
}
return joiner.toString();
@@ -145,7 +159,7 @@
return joiner.toString();
}
-)";
+)s";
const std::regex kRegexDot{"\\."};
const std::regex kRegexUnderscore{"_"};
diff --git a/tests/CppGenTest.cpp b/tests/CppGenTest.cpp
index e1ce0fa..f8994b6 100644
--- a/tests/CppGenTest.cpp
+++ b/tests/CppGenTest.cpp
@@ -327,16 +327,19 @@
template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
Vec ret;
+ if (*str == '\0') return ret;
const char* p = str;
for (;;) {
- const char* found = p;
- while (*found != '\0' && *found != ',') {
- ++found;
+ const char* r = p;
+ std::string value;
+ while (*r != ',') {
+ if (*r == '\\') ++r;
+ if (*r == '\0') break;
+ value += *r++;
}
- std::string value(p, found);
ret.emplace_back(DoParse<typename Vec::value_type>(value.c_str()));
- if (*found == '\0') break;
- p = found + 1;
+ if (*r == '\0') break;
+ p = r + 1;
}
return ret;
}
@@ -376,10 +379,15 @@
bool first = true;
for (auto&& element : value) {
- if (!first) ret += ",";
+ if (!first) ret += ',';
else first = false;
if constexpr(std::is_same_v<T, std::optional<std::string>>) {
- if (element) ret += *element;
+ if (element) {
+ for (char c : *element) {
+ if (c == '\\' || c == ',') ret += '\\';
+ ret += c;
+ }
+ }
} else {
ret += FormatValue(element);
}
diff --git a/tests/JavaGenTest.cpp b/tests/JavaGenTest.cpp
index a0d01c4..eadc187 100644
--- a/tests/JavaGenTest.cpp
+++ b/tests/JavaGenTest.cpp
@@ -101,12 +101,13 @@
)";
constexpr const char* kExpectedPublicOutput =
- R"(// Generated by the sysprop generator. DO NOT EDIT!
+ R"s(// Generated by the sysprop generator. DO NOT EDIT!
package com.somecompany;
import android.os.SystemProperties;
+import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.List;
@@ -172,8 +173,17 @@
List<T> ret = new ArrayList<>();
- for (String element : str.split(",")) {
- ret.add(elementParser.apply(element));
+ int p = 0;
+ for (;;) {
+ StringBuilder sb = new StringBuilder();
+ while (p < str.length() && str.charAt(p) != ',') {
+ if (str.charAt(p) == '\\') ++p;
+ if (p == str.length()) break;
+ sb.append(str.charAt(p++));
+ }
+ ret.add(elementParser.apply(sb.toString()));
+ if (p == str.length()) break;
+ ++p;
}
return ret;
@@ -191,11 +201,15 @@
return ret;
}
+ private static String escape(String str) {
+ return str.replaceAll("([\\\\,])", "\\\\$1");
+ }
+
private static <T> String formatList(List<T> list) {
StringJoiner joiner = new StringJoiner(",");
for (T element : list) {
- joiner.add(element == null ? "" : element.toString());
+ joiner.add(element == null ? "" : escape(element.toString()));
}
return joiner.toString();
@@ -267,15 +281,16 @@
SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value));
}
}
-)";
+)s";
constexpr const char* kExpectedInternalOutput =
- R"(// Generated by the sysprop generator. DO NOT EDIT!
+ R"s(// Generated by the sysprop generator. DO NOT EDIT!
package com.somecompany;
import android.os.SystemProperties;
+import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.List;
@@ -341,8 +356,17 @@
List<T> ret = new ArrayList<>();
- for (String element : str.split(",")) {
- ret.add(elementParser.apply(element));
+ int p = 0;
+ for (;;) {
+ StringBuilder sb = new StringBuilder();
+ while (p < str.length() && str.charAt(p) != ',') {
+ if (str.charAt(p) == '\\') ++p;
+ if (p == str.length()) break;
+ sb.append(str.charAt(p++));
+ }
+ ret.add(elementParser.apply(sb.toString()));
+ if (p == str.length()) break;
+ ++p;
}
return ret;
@@ -360,11 +384,15 @@
return ret;
}
+ private static String escape(String str) {
+ return str.replaceAll("([\\\\,])", "\\\\$1");
+ }
+
private static <T> String formatList(List<T> list) {
StringJoiner joiner = new StringJoiner(",");
for (T element : list) {
- joiner.add(element == null ? "" : element.toString());
+ joiner.add(element == null ? "" : escape(element.toString()));
}
return joiner.toString();
@@ -504,7 +532,7 @@
SystemProperties.set("vendor.el", value == null ? "" : formatEnumList(value, el_values::getPropValue));
}
}
-)";
+)s";
} // namespace