summaryrefslogtreecommitdiff
path: root/tests/generic/595
blob: d559e3bbdb7ba44bba6fddef92645172f07c6d9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright 2020 Google LLC
#
# FS QA Test No. 595
#
# Regression test for a bug in the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl fixed by
# commit 2b4eae95c736 ("fscrypt: don't evict dirty inodes after removing key").
# This bug could cause writes to encrypted files to be lost if they raced with
# the corresponding fscrypt master key being removed.  With f2fs, this bug could
# also crash the kernel.
#
. ./common/preamble
_begin_fstest auto quick encrypt

# Override the default cleanup function.
_cleanup()
{
	# Stop all subprocesses.
	touch $tmp.done
	wait

	rm -f $tmp.*
}

# Import common functions.
. ./common/filter
. ./common/encrypt

# real QA test starts here
_supported_fs generic
_require_scratch_encryption -v 2
_require_command "$KEYCTL_PROG" keyctl

_scratch_mkfs_encrypted &>> $seqres.full
_scratch_mount

dir=$SCRATCH_MNT/dir
runtime=$((4 * TIME_FACTOR))

# Create an encrypted directory.
mkdir $dir
_set_encpolicy $dir $TEST_KEY_IDENTIFIER
_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY"

# Start with a single-threaded reproducer:
echo -e "\n# Single-threaded reproducer"
# Keep a fd open to a file past its fscrypt master key being removed.
exec 3>$dir/file
_rm_enckey $SCRATCH_MNT $TEST_KEY_IDENTIFIER
# Write to and close the open fd.
echo contents >&3
exec 3>&-
# Drop any dentries which might be pinning the inode for "file".
echo 2 > /proc/sys/vm/drop_caches
# In buggy kernels, the inode for "file" was evicted despite the dirty data,
# causing the dirty data to be lost.  Check whether the write made it through.
_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY"
cat $dir/file
rm -f $dir/file

# Also run a multi-threaded reproducer.  This is included for good measure, as
# this type of thing tends to be good for finding other bugs too.
echo -e "\n# Multi-threaded reproducer"
touch $dir/file

# One process add/removes the encryption key repeatedly.
(
	while [ ! -e $tmp.done ]; do
		_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" > /dev/null
		_rm_enckey $SCRATCH_MNT $TEST_KEY_IDENTIFIER &> /dev/null
	done
) &

# Another process repeatedly tries to append to the encrypted file.  The file is
# re-opened each time, so that there are chances for the inode to be evicted.
# Failures to open the file due to the key being removed are ignored.
(
	touch $tmp.expected
	while [ ! -e $tmp.done ]; do
		if sh -c "echo -n X >> $dir/file" 2>/dev/null; then
			# Keep track of the expected file contents.
			echo -n X >> $tmp.expected
		fi
	done
) &

# Run for a while.
sleep $runtime

# Stop all subprocesses.
touch $tmp.done
wait

# Make sure no writes were lost.
_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" > /dev/null
stat $tmp.expected >> $seqres.full
stat $dir/file >> $seqres.full
cmp $tmp.expected $dir/file

echo "Multi-threaded reproducer done"

# success, all done
status=0
exit