NVSL / linux-nova

NOVA is a log-structured file system designed for byte-addressable non-volatile memories, developed at the University of California, San Diego.
http://nvsl.ucsd.edu/index.php?path=projects/nova
Other
422 stars 117 forks source link

Data leak if crash during truncate #152

Open iaoing opened 6 months ago

iaoing commented 6 months ago

Issue

The data could leak if the system crashes during truncate.

Reproduce

The blow code snippet shows the operation to resize data blocks after appending an attribute log. https://github.com/NVSL/linux-nova/blob/976a4d1f3d5282863b23aa834e02012167be6ee2/fs/nova/inode.c#L1335-L1342

Modifying the code to simulate a crash before resizing.

    /* Only after log entry is committed, we can truncate size */
    if ((ia_valid & ATTR_SIZE) && (attr->ia_size != oldsize ||
            pi->i_flags & cpu_to_le32(NOVA_EOFBLOCKS_FL))) {
//      nova_set_blocksize_hint(sb, inode, pi, attr->ia_size);

        /* now we can freely truncate the inode */
                if (oldsize >= attr_ia_size)  // simulate a crash if we truncate the file to a smaller size
                nova_setsize(inode, oldsize, attr->ia_size, epoch_id);
    }
insmod nova.ko metadata_csum=1 data_csum=1 data_parity=1 dram_struct_csum=1
mount -t NOVA -o init,dbgmask=255 /dev/pmem0 /mnt/pmem0
touch /mnt/pmem0/foo
echo 123456789 > /mnt/pmem0/foo
# truncate the file to a smaller size
# since we modified the code, the `nova_setsize` function will not be invoked.
truncate -s 4 /mnt/pmem0/foo
# cat the image to represent the image in case of a crash
cat /dev/pmem0 > img
# remount the crash image
umount /mnt/pmem0
cat img > /dev/pmem0
mount -t NOVA -o dbgmask=255 /dev/pmem0 /mnt/pmem0
# the stat shows the size is 4 bytes and the cat shows '1234'
stat /mnt/pmem0/foo
cat /mnt/pmem0/foo
# truncate the file to a larger size
truncate -s 20 /mnt/pmem0/foo
# the cat shows '123456789', data leak.
cat /mnt/pmem0/foo

Reason

The truncate function (nova_notify_change) is not atomic.

Fix

Journaling the attribute change log and the zeroout of the last page tail, then committing them together. This fix might introduce a performance issue due to the logging of the data block in the undo-journal. We cannot solo log the attribute change log, since the resize (zeroout) could occur before the journal is committed, leading to data lost while the file is the old size. Alternatively, declare a special log entry to indicate the completion of the resize. In recovery, if seeing the truncate log, redo the resize. It sounds like a redo log in an undo journal.