Fix OOM in createOrUpdateIdsigFile when a directory is passed
createOrUpdateIdsigFile uses V4Signature::create function, which takes
an fd that is passed to it and lseeks to the end of it. If that fd
corresponds to a directory on ext4, then lseek will return (off_t)-1. As
a result, the function will OOM trying to allocate 72 petabytes of
memory.
This change fixes the issue by adding verification logic in
createOrUpdateIdsigFile that passed pfd corresponds to a regular file.
It also adds unit tests for createOrUpdateIdsigFile.
I've also added comment to V4Signature::create telling callers that they
should validate their fd before calling the function.
Bug: 261840405
Test: atest virtualizationservice_device_test
Test: adb shell /apex/com.android.virt/bin/vm create-idsig \
/apex/com.android.virt/app/EmptyPayloadApp@AOSP.MASTER \
/data/local/tmp/fun-with-microdroid
Change-Id: Iddb694e10946eefb4df8959d06fe3488e9c6ac66
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
index 6c085f6..94abf99 100644
--- a/libs/apkverify/src/v4.rs
+++ b/libs/apkverify/src/v4.rs
@@ -146,6 +146,11 @@
/// Read a stream for an APK file and creates a corresponding `V4Signature` struct that digests
/// the APK file. Note that the signing is not done.
+ /// Important: callers of this function are expected to verify the validity of the passed |apk|.
+ /// To be more specific, they should check that |apk| corresponds to a regular file, as calling
+ /// lseek on directory fds is not defined in the standard, and on ext4 it will return (off_t)-1
+ /// (see: https://bugzilla.kernel.org/show_bug.cgi?id=200043), which will result in this
+ /// function OOMing.
pub fn create(
mut apk: &mut R,
block_size: usize,
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 3e4323d..edca883 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -115,6 +115,24 @@
}
}
+fn create_or_update_idsig_file(
+ input_fd: &ParcelFileDescriptor,
+ idsig_fd: &ParcelFileDescriptor,
+) -> Result<()> {
+ let mut input = clone_file(input_fd)?;
+ let metadata = input.metadata().context("failed to get input metadata")?;
+ if !metadata.is_file() {
+ bail!("input is not a regular file");
+ }
+ let mut sig = V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256)
+ .context("failed to create idsig")?;
+
+ let mut output = clone_file(idsig_fd)?;
+ output.set_len(0).unwrap();
+ sig.write_into(&mut output).unwrap();
+ Ok(())
+}
+
/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
/// singleton servers, like tombstone receiver.
#[derive(Debug, Default)]
@@ -345,12 +363,8 @@
check_manage_access()?;
- let mut input = clone_file(input_fd)?;
- let mut sig = V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256).unwrap();
-
- let mut output = clone_file(idsig_fd)?;
- output.set_len(0).unwrap();
- sig.write_into(&mut output).unwrap();
+ create_or_update_idsig_file(input_fd, idsig_fd)
+ .map_err(|e| Status::new_service_specific_error_str(-1, Some(format!("{:?}", e))))?;
Ok(())
}
@@ -1303,4 +1317,50 @@
}
Ok(())
}
+
+ #[test]
+ fn test_create_or_update_idsig_file_empty_apk() -> Result<()> {
+ let apk = tempfile::tempfile().unwrap();
+ let idsig = tempfile::tempfile().unwrap();
+
+ let ret = create_or_update_idsig_file(
+ &ParcelFileDescriptor::new(apk),
+ &ParcelFileDescriptor::new(idsig),
+ );
+ assert!(ret.is_err(), "should fail");
+ Ok(())
+ }
+
+ #[test]
+ fn test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()> {
+ let tmp_dir = tempfile::TempDir::new().unwrap();
+ let apk = File::open(tmp_dir.path()).unwrap();
+ let idsig = tempfile::tempfile().unwrap();
+
+ let ret = create_or_update_idsig_file(
+ &ParcelFileDescriptor::new(apk),
+ &ParcelFileDescriptor::new(idsig),
+ );
+ assert!(ret.is_err(), "should fail");
+ Ok(())
+ }
+
+ /// Verifies that create_or_update_idsig_file won't oom if a fd that corresponds to a directory
+ /// on ext4 filesystem is passed.
+ /// On ext4 lseek on a directory fd will return (off_t)-1 (see:
+ /// https://bugzilla.kernel.org/show_bug.cgi?id=200043), which will result in
+ /// create_or_update_idsig_file ooming while attempting to allocate petabytes of memory.
+ #[test]
+ fn test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()> {
+ // APEXes are backed by the ext4.
+ let apk = File::open("/apex/com.android.virt/").unwrap();
+ let idsig = tempfile::tempfile().unwrap();
+
+ let ret = create_or_update_idsig_file(
+ &ParcelFileDescriptor::new(apk),
+ &ParcelFileDescriptor::new(idsig),
+ );
+ assert!(ret.is_err(), "should fail");
+ Ok(())
+ }
}