2

Consider the following series of events for a device-mapper crypt target, test, holding an ext4 filesystem. These three commands swap out the crypt target's table with a new (dummy) table. This changes the encryption key to an invalid one. The device is suspended to prevent accesses:

# blockdev --flushbufs /dev/mapper/test
# dmsetup suspend test
# dmsetup reload -r --table "$newtable" test

Unfortunately, it's necessary to resume and then re-suspend the device in order to actually commit the table change (to move the table from the inactive slot to the live slot and clear the inactive table), so for a brief period of time, the device will be in a corrupt state (the live table has the wrong encryption key) yet is unsuspended (the reason -r is used above is to prevent catastrophic writes):

# dmsetup resume test
# dmsetup suspend --noflush test
# echo 3 > /proc/sys/vm/drop_caches

Caches are dropped so that any invalid data read and cached between the previous resume and suspend will not hang around. Later, the old table is restored. This makes the device writable:

# dmsetup reload --table "$oldtable" test
# dmsetup resume test

The only difference between the two tables is the key:

# echo $oldtable
0 65536 crypt aes-cbc-essiv:sha256 70f91902a1c1fcf7baaf4da03e852960 0 7:1 0
# echo $newtable
0 65536 crypt aes-cbc-essiv:sha256 00000000000000000000000000000000 0 7:1 0

Can this sequence cause any data corruption on test in any circumstances?

forest
  • 163
  • 10
  • Documentation about dm-clone makes use of dmsetup suspend/load/resume but doesn't need a double suspend/resume. Are you sure you need it? Or does the behavior depend on the table type? Or is this drawback depending on kernel version? https://docs.kernel.org/admin-guide/device-mapper/dm-clone.html – A.B Aug 18 '22 at 22:57
  • 1
    @A.B This is for a project where the encryption keys must be removed from memory. After doing a `reload` with a new table, doing `dmsetup table --showkeys` will still show the previous table and its keys. The only way that I've found to get the change to actually commit such that the keys can't be recovered with access to memory via Volatility is by resuming and re-suspending it. While there may be no need in a normal situation, server forensics has slightly different requirements. – forest Aug 18 '22 at 23:06
  • ok now I understand that you want it both zeroed and suspended and that it appears the new table (or at least the key) is committed only on resume. (and sorry not able to answer this question). – A.B Aug 18 '22 at 23:24
  • @A.B Exactly. And if there's a way to lock a block device so both reads and writes are not accepted _without_ using `dmsetup suspend` and while still allowing `dmsetup reload` to go through, then that would be ideal, but I don't know of any such way (I'm currently reading through the source code for `dmsetup` and reading the strace of all the commands, as I might end up having to write a C program to do this to shorten the possible race condition between resuming and re-suspending). – forest Aug 18 '22 at 23:30
  • Interestingly, it seems [`DM_SECURE_DATA_FLAG`](https://sourceware.org/git/?p=lvm2.git;a=blob;f=libdm/misc/dm-ioctl.h;h=cdb38f639a46cc99cc5e2178912d730be900ca5e#l357) is never used, even when loading keys. – forest Aug 18 '22 at 23:41
  • Everything is probably happening in kernel including the new table. The behavior is seen with dmsetup info: shows `Tables present: LIVE & INACTIVE`. man page tells resume uses the inactive if it was loaded. – A.B Aug 18 '22 at 23:44
  • Yeah the kernel handles everything and all `dmsetup` does is send a few ioctls. The keys have to be moved from the inactive to active table, and I believe that's what resume does. – forest Aug 18 '22 at 23:48

0 Answers0