More accurately validate the prerequisites for using emmc_optimized
The emmc_optimized encryption setting is allowed only when the inline
crypto hardware supports only 32-bit DUNs; see CDD 9.9.3.1 [C-1-15].
vold tries to validate this setting accordingly, but previously it did
not have access to the actual DUN length, so instead it checked the
storage type to determine whether a 32-bit DUN limit *might* exist. In
android14-5.15 and later kernels, the DUN length is exposed via a file
in the disk's queue directory in sysfs, which allows doing this check
properly. Therefore, update vold to use this file when possible.
Bug: 207390665
Test: Set emmc_optimized on (a) a device using UFS storage, and (b) a
device using virtio storage with no inline crypto support passed
through. Both with PRODUCT_SHIPPING_API_LEVEL = 35. Verified
that the devices failed to boot and the log showed the expected
reasons. (I did not have a device actually using eMMC at hand.)
Change-Id: Ib2f1cab313aa57e8d5aabe4ebfcc2d08909fcc89
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 3eb4599..4f42dd8 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -37,6 +37,7 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <unistd.h>
#include <private/android_filesystem_config.h>
@@ -233,7 +234,20 @@
return false;
}
-static bool MightBeEmmcStorage(const std::string& blk_device) {
+// Checks whether the kernel definitely supports the sysfs files that describe the storage
+// hardware's inline encryption capabilities. They are supported in upstream 5.18 and later, and in
+// android14-5.15 and later (but not android13-5.15). For simplicity we just check for 5.18.
+static bool DoesKernelSupportBlkCryptoSysfsFiles() {
+ struct utsname uts;
+ unsigned int major = 0, minor = 0;
+ if (uname(&uts) != 0 || sscanf(uts.release, "%u.%u", &major, &minor) != 2) {
+ return true; // This should never happen; assume new rather than old.
+ }
+ return major > 5 || (major == 5 && minor >= 18);
+}
+
+// Checks whether the storage hardware might support only 32-bit data unit numbers.
+static bool DoesHardwareSupportOnly32DunBits(const std::string& blk_device) {
// Handle symlinks.
std::string real_path;
if (!Realpath(blk_device, &real_path)) {
@@ -249,15 +263,53 @@
}
// Now we should have the "real" block device.
- LOG(DEBUG) << "MightBeEmmcStorage(): blk_device = " << blk_device
- << ", real_path=" << real_path;
std::string name = Basename(real_path);
- return StartsWith(name, "mmcblk") ||
- // virtio devices may provide inline encryption support that is
- // backed by eMMC inline encryption on the host, thus inheriting the
- // DUN size limitation. So virtio devices must be allowed here too.
- // TODO(b/207390665): check the maximum DUN size directly instead.
- StartsWith(name, "vd");
+
+ // If possible, do the check precisely via sysfs.
+ // Exclude older devices, just in case they are broken by doing the check correctly...
+ if (GetFirstApiLevel() >= __ANDROID_API_V__) {
+ std::string sysfs_path = "/sys/class/block/" + name + "/queue/crypto/max_dun_bits";
+ if (!android::vold::pathExists(sysfs_path)) {
+ // For a partition, "queue" is in the parent directory which represents the disk.
+ sysfs_path = "/sys/class/block/" + name + "/../queue/crypto/max_dun_bits";
+ }
+ if (android::vold::pathExists(sysfs_path)) {
+ std::string max_dun_bits;
+ if (!android::base::ReadFileToString(sysfs_path, &max_dun_bits)) {
+ PLOG(ERROR) << "Error reading " << sysfs_path;
+ return false;
+ }
+ max_dun_bits = android::base::Trim(max_dun_bits);
+ if (max_dun_bits != "32") {
+ LOG(ERROR) << sysfs_path << " = " << max_dun_bits;
+ // In this case, using emmc_optimized is not appropriate because the hardware
+ // supports inline encryption but does not have the 32-bit DUN limit.
+ return false;
+ }
+ LOG(DEBUG) << sysfs_path << " = " << max_dun_bits;
+ return true;
+ }
+ if (DoesKernelSupportBlkCryptoSysfsFiles()) {
+ // In this case, using emmc_optimized is not appropriate because the hardware does not
+ // support inline encryption.
+ LOG(ERROR) << sysfs_path << " does not exist";
+ return false;
+ }
+ // In this case, the kernel might be too old to support the sysfs files.
+ }
+
+ // Fallback method for older kernels that don't have the crypto capabilities in sysfs. The
+ // 32-bit DUN limit is only known to exist on eMMC storage, and also on virtio storage that
+ // inherits the limit from eMMC on the host. So allow either of those storage types. Note that
+ // this can be overly lenient compared to actually checking max_dun_bits.
+ if (StartsWith(name, "mmcblk") || StartsWith(name, "vd")) {
+ LOG(DEBUG) << __func__ << "(): << blk_device = " << blk_device
+ << ", real_path = " << real_path;
+ return true;
+ }
+ // Log at ERROR level here so that it shows up in the kernel log.
+ LOG(ERROR) << __func__ << "(): << blk_device = " << blk_device << ", real_path = " << real_path;
+ return false;
}
// Sets s_data_options to the file encryption options for the /data filesystem.
@@ -273,9 +325,10 @@
return false;
}
if ((s_data_options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
- !MightBeEmmcStorage(entry->blk_device)) {
- LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove "
- "this flag from the device's fstab";
+ !DoesHardwareSupportOnly32DunBits(entry->blk_device)) {
+ // This would unnecessarily reduce security and not be compliant with the CDD.
+ LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on hardware limited to "
+ "32-bit DUNs. Remove this flag from the device's fstab";
return false;
}
return true;