avbtool: Bail if the same rollback index location is used multiple times.

The other day I saw a mail with an example 'avbtool make_vbmeta_image'
example invocation had multiple --chain-partition options for
different partitions but all using the same rollback index
location. This wouldn't work very well when processed on a
libavb-using device so fail early.

Bug: None
Test: New unit test and all unit tests pass.
Change-Id: I1385035f4ed689e334c7ef31764eeb6fbd64a84d
diff --git a/avbtool b/avbtool
index bc121bc..815caa0 100755
--- a/avbtool
+++ b/avbtool
@@ -2209,16 +2209,25 @@
 
     # Insert chained partition descriptors, if any
     if chain_partitions:
+      used_locations = {}
       for cp in chain_partitions:
         cp_tokens = cp.split(':')
         if len(cp_tokens) != 3:
           raise AvbError('Malformed chained partition "{}".'.format(cp))
+        partition_name = cp_tokens[0]
+        rollback_index_location = int(cp_tokens[1])
+        file_path = cp_tokens[2]
+        # Check that the same rollback location isn't being used by
+        # multiple chained partitions.
+        if used_locations.get(rollback_index_location):
+          raise AvbError('Rollback Index Location {} is already in use.'.format(
+              rollback_index_location))
+        used_locations[rollback_index_location] = True
         desc = AvbChainPartitionDescriptor()
-        desc.partition_name = cp_tokens[0]
-        desc.rollback_index_location = int(cp_tokens[1])
+        desc.partition_name = partition_name
+        desc.rollback_index_location = rollback_index_location
         if desc.rollback_index_location < 1:
           raise AvbError('Rollback index location must be 1 or larger.')
-        file_path = cp_tokens[2]
         desc.public_key = open(file_path, 'rb').read()
         descriptors.append(desc)
 
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index 58926db..4d9f2a8 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -1419,6 +1419,32 @@
                         d.public_key_len));
 }
 
+TEST_F(AvbToolTest, ChainedPartitionNoLocationCollision) {
+  base::FilePath vbmeta_path = testdir_.Append("vbmeta_cp.bin");
+
+  base::FilePath pk_path = testdir_.Append("testkey_rsa2048.avbpubkey");
+
+  EXPECT_COMMAND(
+      0,
+      "./avbtool extract_public_key --key test/data/testkey_rsa2048.pem"
+      " --output %s",
+      pk_path.value().c_str());
+
+  // Check that avbtool bails if the same Rollback Index Location is
+  // used for multiple chained partitions.
+  EXPECT_COMMAND(
+      1,
+      "./avbtool make_vbmeta_image "
+      "--output %s "
+      "--chain_partition system:1:%s "
+      "--chain_partition other:1:%s "
+      "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
+      "--internal_release_string \"\"",
+      vbmeta_path.value().c_str(),
+      pk_path.value().c_str(),
+      pk_path.value().c_str());
+}
+
 TEST_F(AvbToolTest, AppendVBMetaImage) {
   size_t boot_size = 5 * 1024 * 1024;
   size_t boot_partition_size = 32 * 1024 * 1024;