jffs2: fix unbalanced locking
Li Zefan reported an unbalanced locking issue, found by his internal debugging feature on runtime. The particular case he was looking at doesn't lead to a deadlock, as the structure that this lock is embedded in is freed on error. But we should straighten out the error handling. Because several callers of jffs2_do_read_inode_internal() / jffs2_do_read_inode() already handle the locking/unlocking and inode clearing at their own level, let's just push any unlocks/clearing down to the caller. This consistency is much easier to verify. Reported-by: Li Zefan <lizefan@huawei.com> Cc: David Woodhouse <dwmw2@infradead.org> Cc: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Cc: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
This commit is contained in:
Родитель
3094fe121e
Коммит
7aaea7605c
|
@ -272,12 +272,9 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
|
||||||
mutex_lock(&f->sem);
|
mutex_lock(&f->sem);
|
||||||
|
|
||||||
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
|
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
iget_failed(inode);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
inode->i_mode = jemode_to_cpu(latest_node.mode);
|
inode->i_mode = jemode_to_cpu(latest_node.mode);
|
||||||
i_uid_write(inode, je16_to_cpu(latest_node.uid));
|
i_uid_write(inode, je16_to_cpu(latest_node.uid));
|
||||||
i_gid_write(inode, je16_to_cpu(latest_node.gid));
|
i_gid_write(inode, je16_to_cpu(latest_node.gid));
|
||||||
|
|
|
@ -1203,17 +1203,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
|
JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
|
||||||
ret, retlen, sizeof(*latest_node));
|
ret, retlen, sizeof(*latest_node));
|
||||||
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
||||||
mutex_unlock(&f->sem);
|
return ret ? ret : -EIO;
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return ret?ret:-EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
||||||
if (crc != je32_to_cpu(latest_node->node_crc)) {
|
if (crc != je32_to_cpu(latest_node->node_crc)) {
|
||||||
JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
|
JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
|
||||||
f->inocache->ino, ref_offset(rii.latest_ref));
|
f->inocache->ino, ref_offset(rii.latest_ref));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1250,16 +1246,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
* keep in RAM to facilitate quick follow symlink
|
* keep in RAM to facilitate quick follow symlink
|
||||||
* operation. */
|
* operation. */
|
||||||
uint32_t csize = je32_to_cpu(latest_node->csize);
|
uint32_t csize = je32_to_cpu(latest_node->csize);
|
||||||
if (csize > JFFS2_MAX_NAME_LEN) {
|
if (csize > JFFS2_MAX_NAME_LEN)
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -ENAMETOOLONG;
|
return -ENAMETOOLONG;
|
||||||
}
|
|
||||||
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
||||||
if (!f->target) {
|
if (!f->target) {
|
||||||
JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
|
JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1271,8 +1262,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
kfree(f->target);
|
kfree(f->target);
|
||||||
f->target = NULL;
|
f->target = NULL;
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1289,15 +1278,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
if (f->metadata) {
|
if (f->metadata) {
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (!frag_first(&f->fragtree)) {
|
if (!frag_first(&f->fragtree)) {
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
/* ASSERT: f->fraglist != NULL */
|
/* ASSERT: f->fraglist != NULL */
|
||||||
|
@ -1305,8 +1290,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
|
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
/* OK. We're happy */
|
/* OK. We're happy */
|
||||||
|
@ -1400,10 +1383,8 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
|
||||||
f->inocache = ic;
|
f->inocache = ic;
|
||||||
|
|
||||||
ret = jffs2_do_read_inode_internal(c, f, &n);
|
ret = jffs2_do_read_inode_internal(c, f, &n);
|
||||||
if (!ret) {
|
|
||||||
mutex_unlock(&f->sem);
|
mutex_unlock(&f->sem);
|
||||||
jffs2_do_clear_inode(c, f);
|
jffs2_do_clear_inode(c, f);
|
||||||
}
|
|
||||||
jffs2_xattr_do_crccheck_inode(c, ic);
|
jffs2_xattr_do_crccheck_inode(c, ic);
|
||||||
kfree (f);
|
kfree (f);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче