Add check for hidl package root hash.
When compiling a module, say foo.bar.baz@1.0::IBaz, assume the package
root is foo.bar:some/dir/interfaces, then:
- look at the file some/dir/interfaces/current.txt
- read hash from file corresponding from foo.bar.baz@1.0::IBaz
- file supports same-line comments with '#'
- format of file is each line is blank or looks like:
"<sha-256 hash> <fqName>"
- if the file is misformed or the hal does not match a sha in that file,
then it is considered build breakage.
Test: build with and without changing frozen files
Bug: 34178341
Change-Id: Ieddbc796ea974ac7c2e8b95ca69009c31e0cfb60
diff --git a/Coordinator.cpp b/Coordinator.cpp
index 72c8557..dee56aa 100644
--- a/Coordinator.cpp
+++ b/Coordinator.cpp
@@ -26,6 +26,7 @@
#include <hidl-util/StringHelper.h>
#include "AST.h"
+#include "Hash.h"
#include "Interface.h"
extern android::status_t parseFile(android::AST *ast);
@@ -378,7 +379,14 @@
}
// enforce all rules.
- status_t err = enforceMinorVersionUprevs(package);
+ status_t err;
+
+ err = enforceMinorVersionUprevs(package);
+ if (err != OK) {
+ return err;
+ }
+
+ err = enforceHashes(package);
if (err != OK) {
return err;
}
@@ -491,6 +499,51 @@
return OK;
}
+status_t Coordinator::enforceHashes(const FQName ¤tPackage) {
+ status_t err = OK;
+ std::vector<FQName> packageInterfaces;
+ err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
+ if (err != OK) {
+ return err;
+ }
+
+ for (const FQName ¤tFQName : packageInterfaces) {
+ AST *ast = parse(currentFQName);
+
+ if (ast == nullptr) {
+ err = UNKNOWN_ERROR;
+ continue;
+ }
+
+ std::string packageRootPath = getPackageRootPath(currentFQName);
+ std::string error;
+ std::vector<std::string> frozen = Hash::lookupHash(packageRootPath, currentFQName.string(), &error);
+
+ if (error.size() > 0) {
+ LOG(ERROR) << error;
+ err = UNKNOWN_ERROR;
+ continue;
+ }
+
+ // hash not define, interface not frozen
+ if (frozen.size() == 0) {
+ continue;
+ }
+
+ std::string currentHash = Hash::getHash(ast->getFilename()).hexString();
+
+ if(std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
+ LOG(ERROR) << currentFQName.string() << " has hash " << currentHash
+ << " which does not match hash on record. This interface has "
+ << "been frozen. Do not change it!";
+ err = UNKNOWN_ERROR;
+ continue;
+ }
+ }
+
+ return err;
+}
+
// static
bool Coordinator::MakeParentHierarchy(const std::string &path) {
static const mode_t kMode = 0755;
diff --git a/Coordinator.h b/Coordinator.h
index ddf5dfa..9f75d46 100644
--- a/Coordinator.h
+++ b/Coordinator.h
@@ -92,6 +92,7 @@
// Enforce a set of restrictions on a set of packages. These include:
// - minor version upgrades
// "packages" contains names like "android.hardware.nfc@1.1".
+ // - hashing restrictions
status_t enforceRestrictionsOnPackage(const FQName &fqName);
static bool MakeParentHierarchy(const std::string &path);
@@ -117,6 +118,7 @@
// Rules of enforceRestrictionsOnPackage are listed below.
status_t enforceMinorVersionUprevs(const FQName &fqName);
+ status_t enforceHashes(const FQName &fqName);
DISALLOW_COPY_AND_ASSIGN(Coordinator);
};
diff --git a/Hash.cpp b/Hash.cpp
index 348a79f..0a8df47 100644
--- a/Hash.cpp
+++ b/Hash.cpp
@@ -19,8 +19,10 @@
#include <fstream>
#include <iomanip>
#include <map>
+#include <regex>
#include <sstream>
+#include <android-base/logging.h>
#include <openssl/sha.h>
namespace android {
@@ -72,4 +74,97 @@
return mPath;
}
+#define TOKEN "([^\\s*]+)"
+#define HASH TOKEN
+#define FQNAME TOKEN
+#define SPACES " +"
+#define MAYBE_SPACES " *"
+#define OPTIONAL_COMMENT "(?:#.*)?"
+static const std::regex kHashLine(
+ "(?:"
+ MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES
+ ")?"
+ OPTIONAL_COMMENT);
+
+struct HashFile {
+ static const HashFile *parse(const std::string &path, std::string *err) {
+ static std::map<std::string, HashFile*> hashfiles;
+ auto it = hashfiles.find(path);
+
+ if (it == hashfiles.end()) {
+ it = hashfiles.insert(it, {path, readHashFile(path, err)});
+ }
+
+ return it->second;
+ }
+
+ std::vector<std::string> lookup(const std::string &fqName) const {
+ auto it = hashes.find(fqName);
+
+ if (it == hashes.end()) {
+ return {};
+ }
+
+ return it->second;
+ }
+
+private:
+ static HashFile *readHashFile(const std::string &path, std::string *err) {
+ std::ifstream stream(path);
+ if (!stream) {
+ return nullptr;
+ }
+
+ HashFile *file = new HashFile();
+ file->path = path;
+
+ std::string line;
+ while(std::getline(stream, line)) {
+ std::smatch match;
+ bool valid = std::regex_match(line, match, kHashLine);
+
+ if (!valid) {
+ *err = "Error reading line from " + path + ": " + line;
+ delete file;
+ return nullptr;
+ }
+
+ CHECK_EQ(match.size(), 3u);
+
+ std::string hash = match.str(1);
+ std::string fqName = match.str(2);
+
+ if (hash.size() == 0 && fqName.size() == 0) {
+ continue;
+ }
+
+ if (hash.size() == 0 || fqName.size() == 0) {
+ *err = "Hash or fqName empty on " + path + ": " + line;
+ delete file;
+ return nullptr;
+ }
+
+ file->hashes[fqName].push_back(hash);
+ }
+ return file;
+ }
+
+ std::string path;
+ std::map<std::string,std::vector<std::string>> hashes;
+};
+
+//static
+std::vector<std::string> Hash::lookupHash(const std::string &path,
+ const std::string &interfaceName,
+ std::string *err) {
+ *err = "";
+ const HashFile *file = HashFile::parse(path + "/current.txt", err);
+
+ if (file == nullptr || err->size() > 0) {
+ return {};
+ }
+
+ return file->lookup(interfaceName);
+}
+
} // android
\ No newline at end of file
diff --git a/Hash.h b/Hash.h
index 99dea31..420ff1c 100644
--- a/Hash.h
+++ b/Hash.h
@@ -26,6 +26,11 @@
struct Hash {
static const Hash &getHash(const std::string &path);
+ // returns matching hashes of interfaceName in $path/current.txt
+ static std::vector<std::string> lookupHash(const std::string &path,
+ const std::string &interfaceName,
+ std::string *err);
+
std::string hexString() const;
const std::vector<uint8_t> &raw() const;