bootstat: validate last kmsg and bootreason content more carefully

Establish a tighter trust relationship with digitization of the
battery level.  Validate the reboot reason collected from the last
kmsg.  Validate the last reboot reason before using for enhancing
system reboot reason.

Test: system/core/bootstat/boot_reason_test.sh
Bug: 63636262
Change-Id: I45fbac4a33c09295cda89de3b73c93a884033e3b
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index ce28059..e2202fd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -334,6 +334,8 @@
 
 // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
 std::string BootReasonStrToReason(const std::string& boot_reason) {
+  static const size_t max_reason_length = 256;
+
   std::string ret(GetProperty(system_reboot_reason_property));
   std::string reason(boot_reason);
   // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
@@ -428,9 +430,15 @@
       size_t pos = content.rfind(cmd);
       if (pos != std::string::npos) {
         pos += strlen(cmd);
-        std::string subReason(content.substr(pos));
-        pos = subReason.find('\'');
-        if (pos != std::string::npos) subReason.erase(pos);
+        std::string subReason(content.substr(pos, max_reason_length));
+        for (pos = 0; pos < subReason.length(); ++pos) {
+          char c = tounderline(subReason[pos]);
+          if (!::isprint(c) || (c == '\'')) {
+            subReason.erase(pos);
+            break;
+          }
+          subReason[pos] = ::tolower(c);
+        }
         if (subReason != "") {  // Will not land "reboot" as that is too blunt.
           if (isKernelRebootReason(subReason)) {
             ret = "reboot," + subReason;  // User space can't talk kernel reasons.
@@ -461,13 +469,20 @@
       static const int battery_dead_threshold = 2;  // percent
       static const char battery[] = "healthd: battery l=";
       size_t pos = content.rfind(battery);  // last one
+      std::string digits;
       if (pos != std::string::npos) {
-        int level = atoi(content.substr(pos + strlen(battery)).c_str());
+        digits = content.substr(pos + strlen(battery));
+      }
+      char* endptr = NULL;
+      unsigned long long level = strtoull(digits.c_str(), &endptr, 10);
+      if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
         LOG(INFO) << "Battery level at shutdown " << level << "%";
         if (level <= battery_dead_threshold) {
           ret = "shutdown,battery";
         }
-      } else {  // Most likely
+      } else {        // Most likely
+        digits = "";  // reset digits
+
         // Content buffer no longer will have console data. Beware if more
         // checks added below, that depend on parsing console content.
         content = "";
@@ -501,8 +516,11 @@
 
         pos = content.find(match);  // The first one it finds.
         if (pos != std::string::npos) {
-          pos += strlen(match);
-          int level = atoi(content.substr(pos).c_str());
+          digits = content.substr(pos + strlen(match));
+        }
+        endptr = NULL;
+        level = strtoull(digits.c_str(), &endptr, 10);
+        if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
           LOG(INFO) << "Battery level at startup " << level << "%";
           if (level <= battery_dead_threshold) {
             ret = "shutdown,battery";
@@ -518,6 +536,10 @@
       // Content buffer no longer will have console data. Beware if more
       // checks added below, that depend on parsing console content.
       content = GetProperty(last_reboot_reason_property);
+      // Cleanup last_boot_reason regarding acceptable character set
+      std::transform(content.begin(), content.end(), content.begin(), ::tolower);
+      std::transform(content.begin(), content.end(), content.begin(), tounderline);
+      std::transform(content.begin(), content.end(), content.begin(), toprintable);
 
       // String is either "reboot,<reason>" or "shutdown,<reason>".
       // We will set if default reasons, only override with detail if thermal.