init: check property type in host_init_verifier

We have all of the 'type' information for properties available during
build time, so let's check this when setting properties in init.

Test: setprop apexd.status bad results in:
host_init_verifier: Command 'setprop apexd.status bad'
(out/soong/.intermediates/system/core/rootdir/init.rc/android_x86_core/init.rc:927)
failed: Property type check failed, value doesn't match expected type
'enum starting ready'
host_init_verifier: Failed to parse init script
'out/soong/.intermediates/system/core/rootdir/init.rc/android_x86_core/init.rc'
with 1 errors
Test: CF builds without that error

Change-Id: Iaad07747c09f4a10b2b816c455d6e8a485357ab9
diff --git a/init/Android.bp b/init/Android.bp
index d939fcc..131267b 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -281,6 +281,8 @@
     static_libs: [
         "libbase",
         "libselinux",
+        "libpropertyinfoserializer",
+        "libpropertyinfoparser",
     ],
     whole_static_libs: ["libcap"],
     shared_libs: [
@@ -304,6 +306,7 @@
         "host_import_parser.cpp",
         "host_init_verifier.cpp",
         "parser.cpp",
+        "property_type.cpp",
         "rlimit_parser.cpp",
         "tokenizer.cpp",
         "service.cpp",
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 9d23921..bef6966 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -29,7 +29,9 @@
 #include <android-base/strings.h>
 
 #include "builtin_arguments.h"
+#include "host_init_verifier.h"
 #include "interface_utils.h"
+#include "property_type.h"
 #include "rlimit_parser.h"
 #include "service.h"
 #include "util.h"
@@ -171,6 +173,15 @@
                        << "' from init; use the restorecon builtin directly";
     }
 
+    const char* target_context = nullptr;
+    const char* type = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
+
+    if (!CheckType(type, value)) {
+        return Error() << "Property type check failed, value doesn't match expected type '"
+                       << (type ?: "(null)") << "'";
+    }
+
     return {};
 }
 
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 522709e..3acc3cc 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include "host_init_verifier.h"
+
 #include <errno.h>
 #include <getopt.h>
 #include <pwd.h>
@@ -31,6 +33,7 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <hidl/metadata.h>
+#include <property_info_serializer/property_info_serializer.h>
 
 #include "action.h"
 #include "action_manager.h"
@@ -53,6 +56,10 @@
 using android::base::ParseInt;
 using android::base::ReadFileToString;
 using android::base::Split;
+using android::properties::BuildTrie;
+using android::properties::ParsePropertyInfoFile;
+using android::properties::PropertyInfoArea;
+using android::properties::PropertyInfoEntry;
 
 static std::vector<std::string> passwd_files;
 
@@ -143,11 +150,12 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
+    std::cout << "usage: host_init_verifier [options] <init rc file>\n"
                  "\n"
                  "Tests an init script for correctness\n"
                  "\n"
                  "-p FILE\tSearch this passwd file for users and groups\n"
+                 "--property_contexts=FILE\t Use this file for property_contexts\n"
               << std::endl;
 }
 
@@ -172,23 +180,53 @@
     return result;
 }
 
+const PropertyInfoArea* property_info_area;
+
+void HandlePropertyContexts(const std::string& filename,
+                            std::vector<PropertyInfoEntry>* property_infos) {
+    auto file_contents = std::string();
+    if (!ReadFileToString(filename, &file_contents)) {
+        PLOG(ERROR) << "Could not read properties from '" << filename << "'";
+        exit(EXIT_FAILURE);
+    }
+
+    auto errors = std::vector<std::string>{};
+    ParsePropertyInfoFile(file_contents, property_infos, &errors);
+    for (const auto& error : errors) {
+        LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
+    }
+    if (!errors.empty()) {
+        exit(EXIT_FAILURE);
+    }
+}
+
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
+    auto property_infos = std::vector<PropertyInfoEntry>();
+
     while (true) {
+        static const char kPropertyContexts[] = "property-contexts=";
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
+                {kPropertyContexts, required_argument, nullptr, 0},
                 {nullptr, 0, nullptr, 0},
         };
 
-        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
+        int option_index;
+        int arg = getopt_long(argc, argv, "p:", long_options, &option_index);
 
         if (arg == -1) {
             break;
         }
 
         switch (arg) {
+            case 0:
+                if (long_options[option_index].name == kPropertyContexts) {
+                    HandlePropertyContexts(optarg, &property_infos);
+                }
+                break;
             case 'h':
                 PrintUsage();
                 return EXIT_FAILURE;
@@ -216,6 +254,16 @@
     }
     SetKnownInterfaces(*interface_inheritance_hierarchy_map);
 
+    std::string serialized_contexts;
+    std::string trie_error;
+    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
+                   &trie_error)) {
+        LOG(ERROR) << "Unable to serialize property contexts: " << trie_error;
+        return EXIT_FAILURE;
+    }
+
+    property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
+
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
     ActionManager& am = ActionManager::GetInstance();
diff --git a/init/host_init_verifier.h b/init/host_init_verifier.h
new file mode 100644
index 0000000..5d24f2a
--- /dev/null
+++ b/init/host_init_verifier.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <property_info_parser/property_info_parser.h>
+
+namespace android {
+namespace init {
+
+extern const android::properties::PropertyInfoArea* property_info_area;
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3baaf7c..7d707cc 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -478,7 +478,7 @@
         return PROP_ERROR_PERMISSION_DENIED;
     }
 
-    if (type == nullptr || !CheckType(type, value)) {
+    if (!CheckType(type, value)) {
         *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
                               (type ?: "(null)"));
         return PROP_ERROR_INVALID_VALUE;