From: Shao Mingyin ubifs_shash_copy_state() duplicates the rolling hash state with crypto_shash_export() and crypto_shash_import(). The export buffer is allocated with kmalloc(), so the function can fail with -ENOMEM, and crypto_shash_import() can fail as well. Five callers ignore the return value. The target descriptor then has no valid state, and the crypto_shash_final() that follows computes a digest from uninitialized data. When preparing an auth node this stores a wrong HMAC on the media, so the affected journal head no longer authenticates after a remount. During replay it makes the verification of a valid image fail with -EPERM, and in replay_bud() and add_replay_bud() it leaves the replay hash state inconsistent with the media, which corrupts the auth nodes written by later commits. Check the error at all five call sites and propagate it. In add_replay_bud() the descriptor belongs to the new bud, so free it on the added error path. Fixes: 6a98bc4614de ("ubifs: Add authentication nodes to journal") Fixes: da8ef65f9573 ("ubifs: Authenticate replayed journal") Cc: stable@vger.kernel.org Signed-off-by: Shao Mingyin --- diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c index 1f770795ee70..62dfacfb6d6b 100644 --- a/fs/ubifs/auth.c +++ b/fs/ubifs/auth.c @@ -70,7 +70,10 @@ int ubifs_prepare_auth_node(struct ubifs_info *c, void *node, SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm); hash_desc->tfm = c->hash_tfm; - ubifs_shash_copy_state(c, inhash, hash_desc); + + err = ubifs_shash_copy_state(c, inhash, hash_desc); + if (err) + return err; err = crypto_shash_final(hash_desc, hash); if (err) diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index c6edcbf35ebc..42cadf558a4f 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -412,7 +412,11 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum) UBIFS_REF_NODE_SZ); if (err) goto out; - ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash); + + err = ubifs_shash_copy_state(c, c->log_hash, + c->jheads[i].log_hash); + if (err) + goto out; } ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len); diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 263045e05cf1..0b82c67a2dcc 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -566,10 +566,14 @@ authenticate_sleb_hash(struct ubifs_info *c, struct shash_desc *log_hash, u8 *hash) { SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm); + int err; hash_desc->tfm = c->hash_tfm; - ubifs_shash_copy_state(c, log_hash, hash_desc); + err = ubifs_shash_copy_state(c, log_hash, hash_desc); + if (err) + return err; + return crypto_shash_final(hash_desc, hash); } @@ -694,8 +698,10 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) goto out; } - ubifs_shash_copy_state(c, b->bud->log_hash, - c->jheads[b->bud->jhead].log_hash); + err = ubifs_shash_copy_state(c, b->bud->log_hash, + c->jheads[b->bud->jhead].log_hash); + if (err) + goto out; /* * The bud does not have to start from offset zero - the beginning of @@ -915,7 +921,9 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, goto out; } - ubifs_shash_copy_state(c, c->log_hash, bud->log_hash); + err = ubifs_shash_copy_state(c, c->log_hash, bud->log_hash); + if (err) + goto out_free_log_hash; ubifs_add_bud(c, bud); @@ -924,6 +932,8 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, list_add_tail(&b->list, &c->replay_buds); return 0; +out_free_log_hash: + kfree(bud->log_hash); out: kfree(bud); kfree(b);