Bug 216213
Summary: | UBIFS: In encrypted directories, truncate operation causes ubifs to be read-only. | ||
---|---|---|---|
Product: | File System | Reporter: | wangzhaolong1 |
Component: | Other | Assignee: | fs_other |
Status: | NEW --- | ||
Severity: | high | ||
Priority: | P1 | ||
Hardware: | All | ||
OS: | Linux | ||
Kernel Version: | 5.19.0 -rc5 | Subsystem: | |
Regression: | No | Bisected commit-id: | |
Attachments: | In the truncate_data_node() function, the length of the compressed data after truncation may be greater than the length of the compressed data before truncation. |
Description
wangzhaolong1
2022-07-07 09:15:58 UTC
Created attachment 301356 [details]
In the truncate_data_node() function, the length of the compressed data after truncation may be greater than the length of the compressed data before truncation.
The ubifs_compress() function of the UBIFS checks the data to be compressed and the compression result. If the data length is short than 128 bytes or the compressed data length is not ideal, the UBIFS does not compress the data.
void ubifs_compress(const struct ubifs_info *c, const void *in_buf,
int in_len, void *out_buf, int *out_len, int *compr_type)
{
......
if (*compr_type == UBIFS_COMPR_NONE)
goto no_compr;
......
err = crypto_comp_compress(compr->cc, in_buf, in_len, out_buf,
(unsigned int *)out_len);
......
if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
goto no_compr;
......
return;
no_compr:
memcpy(out_buf, in_buf, in_len);
*out_len = in_len;
*compr_type = UBIFS_COMPR_NONE;
}
Therefore, in the truncatation process the compressed length of the truncated data may be greater than the compressed length of the raw data read from the flash memory.
When the length of the truncated data is greater than the length of the compressed raw data, truncate_data_node() calls the ubifs_encrypt() function and it will triggers assert. This will cause the file system to become read-only.
static int truncate_data_node(const struct ubifs_info *c, const struct inode *inode,
unsigned int block, struct ubifs_data_node *dn,
int *new_len)
{
void *buf;
int err, dlen, compr_type, out_len, old_dlen;
out_len = le32_to_cpu(dn->size);
......
err = ubifs_decrypt(inode, dn, &dlen, block);
......
err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
if (err)
goto out;
ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
if (IS_ENCRYPTED(inode)) {
err = ubifs_encrypt(inode, dn, out_len, &old_dlen, block);
......
}
After ubifs_compress() is called, "out_len" may be greater than "old_dlen".The out len is the compressed length of the truncated data, the old_dlen is the length of compressed raw data read from the flash memory.
When "out_len" greater than "old_dlen", the ubifs_compress() function will trigger assert.
int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
{
......
unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE);
......
ubifs_assert(c, pad_len <= *out_len);
......
}
When "out_len" is nearly close to "old_len", it is also possible that the ubifs_compress() function will trigger an assert() because of round_up() option.
Assert triggered by this improper check can cause the file system to become read, which is a serious problem.
> When "out_len" greater than "old_dlen", the ubifs_encrypt() function will
> ubifs_encrypt() function will trigger an assert() because of round_up()
>
> When "out_len" greater than "old_dlen", the ubifs_compress() function will
> trigger assert.
>
> int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
> {
> ......
> unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE);
> ......
> ubifs_assert(c, pad_len <= *out_len);
> ......
> }
> When "out_len" is nearly close to "old_len", it is also possible that the
> ubifs_compress() function will trigger an assert() because of round_up()
> option.
>
> Assert triggered by this improper check can cause the file system to become
> read, which is a serious problem.
Correct some clerical errors, ubifs_compress() here should be ubifs_encrypt()
When "out_len" greater than "old_dlen", the ubifs_encrypt() function will
trigger assert.
int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
{
......
unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE);
......
ubifs_assert(c, pad_len <= *out_len);
......
}
When "out_len" is nearly close to "old_len", it is also possible that the
ubifs_encrypt() function will trigger an assert() because of round_up()
option.
Assert triggered by this improper check can cause the file system to become
read, which is a serious problem.
(In reply to wangzhaolong1 from comment #0) > [kernel config]: > CONFIG_MTD_NAND_NANDSIM=m > CONFIG_MTD_UBI=m > CONFIG_UBIFS_FS=m > CONFIG_UBIFS_FS_ADVANCED_COMPR=y > CONFIG_UBIFS_FS_LZO=y > CONFIG_UBIFS_FS_ZLIB=y > CONFIG_UBIFS_FS_ZSTD=y > CONFIG_UBIFS_ATIME_SUPPORT=y > CONFIG_UBIFS_FS_XATTR=y > CONFIG_UBIFS_FS_SECURITY=y > CONFIG_UBIFS_FS_AUTHENTICATION=y CONFIG_FS_ENCRYPTION=y |