diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /fs/ubifs/crypto.c | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'fs/ubifs/crypto.c')
| -rw-r--r-- | fs/ubifs/crypto.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c new file mode 100644 index 000000000..0b14d004a --- /dev/null +++ b/fs/ubifs/crypto.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ubifs.h" + +static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len); +} + +static int ubifs_crypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + /* + * Creating an encryption context is done unlocked since we + * operate on a new inode which is not visible to other users + * at this point. So, no need to check whether inode is locked. + */ + return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len, 0, false); +} + +static bool ubifs_crypt_empty_dir(struct inode *inode) +{ + return ubifs_check_dir_empty(inode) == 0; +} + +/** + * ubifs_encrypt - Encrypt data. + * @inode: inode which refers to the data node + * @dn: data node to encrypt + * @in_len: length of data to be compressed + * @out_len: allocated memory size for the data area of @dn + * @block: logical block number of the block + * + * This function encrypt a possibly-compressed data in the data node. + * The encrypted data length will store in @out_len. + */ +int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn, + unsigned int in_len, unsigned int *out_len, int block) +{ + struct ubifs_info *c = inode->i_sb->s_fs_info; + void *p = &dn->data; + unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE); + int err; + + ubifs_assert(c, pad_len <= *out_len); + dn->compr_size = cpu_to_le16(in_len); + + /* pad to full block cipher length */ + if (pad_len != in_len) + memset(p + in_len, 0, pad_len - in_len); + + err = fscrypt_encrypt_block_inplace(inode, virt_to_page(p), pad_len, + offset_in_page(p), block); + if (err) { + ubifs_err(c, "fscrypt_encrypt_block_inplace() failed: %d", err); + return err; + } + *out_len = pad_len; + + return 0; +} + +int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn, + unsigned int *out_len, int block) +{ + struct ubifs_info *c = inode->i_sb->s_fs_info; + int err; + unsigned int clen = le16_to_cpu(dn->compr_size); + unsigned int dlen = *out_len; + + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen) { + ubifs_err(c, "bad compr_size: %i", clen); + return -EINVAL; + } + + ubifs_assert(c, dlen <= UBIFS_BLOCK_SIZE); + err = fscrypt_decrypt_block_inplace(inode, virt_to_page(&dn->data), + dlen, offset_in_page(&dn->data), + block); + if (err) { + ubifs_err(c, "fscrypt_decrypt_block_inplace() failed: %d", err); + return err; + } + *out_len = clen; + + return 0; +} + +const struct fscrypt_operations ubifs_crypt_operations = { + .inode_info_offs = (int)offsetof(struct ubifs_inode, i_crypt_info) - + (int)offsetof(struct ubifs_inode, vfs_inode), + .legacy_key_prefix = "ubifs:", + .get_context = ubifs_crypt_get_context, + .set_context = ubifs_crypt_set_context, + .empty_dir = ubifs_crypt_empty_dir, +}; |
