bug/race in mtdchar device?

Joakim Tjernlund joakim.tjernlund at transmode.se
Mon Sep 21 07:11:06 PDT 2015


Trying to find a falsh corretion bug I stumbled onto this in mtdchar.c:
			/*
			  FIXME: Allow INTERRUPTIBLE. Which means
			  not having the wait_queue head on the stack.

			  If the wq_head is on the stack, and we
			  leave because we got interrupted, then the
			  wq_head is no longer there when the
			  callback routine tries to wake us up.
			*/
			ret = mtd_erase(mtd, erase);
			if (!ret) {
				set_current_state(TASK_UNINTERRUPTIBLE);
				add_wait_queue(&waitq, &wait);
				if (erase->state != MTD_ERASE_DONE &&
				    erase->state != MTD_ERASE_FAILED)
					schedule();
				remove_wait_queue(&waitq, &wait);
				set_current_state(TASK_RUNNING);

				ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
			}
			kfree(erase);

Is the FIXME comment correct? If so, should not init_waitqueue_head(&waitq) be
moved to after set_current_state(TASK_UNINTERRUPTIBLE) ?

Also, it feels a bit odd that mtd_erase is called before setting up the wait queue
as mtd_erase can call the callback before returning.

I am contemplating a changing the code into:
			set_current_state(TASK_UNINTERRUPTIBLE);
			init_waitqueue_head(&waitq);
			add_wait_queue(&waitq, &wait);
			ret = mtd_erase(mtd, erase);
			if (ret) {
				remove_wait_queue(&waitq, &wait);
				set_current_state(TASK_RUNNING);
				kfree(erase);
				return ret;
			}
			schedule(); /* Wait for erase to finish. */
			remove_wait_queue(&waitq, &wait);
			ret = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
			kfree(erase);

Does that make sense?

 Jocke




More information about the linux-mtd mailing list