UBI: Fix invalid vfree()
authorRichard Weinberger <richard@nod.at>
Sun, 26 Oct 2014 23:46:11 +0000 (00:46 +0100)
committerBen Hutchings <ben@decadent.org.uk>
Fri, 20 Feb 2015 00:49:24 +0000 (00:49 +0000)
commit f38aed975c0c3645bbdfc5ebe35726e64caaf588 upstream.

The logic of vfree()'ing vol->upd_buf is tied to vol->updating.
In ubi_start_update() vol->updating is set long before vmalloc()'ing
vol->upd_buf. If we encounter a write failure in ubi_start_update()
before vmalloc() the UBI device release function will try to vfree()
vol->upd_buf because vol->updating is set.
Fix this by allocating vol->upd_buf directly after setting vol->updating.

Fixes:
[   31.559338] UBI warning: vol_cdev_release: update of volume 2 not finished, volume is damaged
[   31.559340] ------------[ cut here ]------------
[   31.559343] WARNING: CPU: 1 PID: 2747 at mm/vmalloc.c:1446 __vunmap+0xe3/0x110()
[   31.559344] Trying to vfree() nonexistent vm area (ffffc90001f2b000)
[   31.559345] Modules linked in:
[   31.565620]  0000000000000bba ffff88002a0cbdb0 ffffffff818f0497 ffff88003b9ba148
[   31.566347]  ffff88002a0cbde0 ffffffff8156f515 ffff88003b9ba148 0000000000000bba
[   31.567073]  0000000000000000 0000000000000000 ffff88002a0cbe88 ffffffff8156c10a
[   31.567793] Call Trace:
[   31.568034]  [<ffffffff818f0497>] dump_stack+0x4e/0x7a
[   31.568510]  [<ffffffff8156f515>] ubi_io_write_vid_hdr+0x155/0x160
[   31.569084]  [<ffffffff8156c10a>] ubi_eba_write_leb+0x23a/0x870
[   31.569628]  [<ffffffff81569b36>] vol_cdev_write+0x226/0x380
[   31.570155]  [<ffffffff81179265>] vfs_write+0xb5/0x1f0
[   31.570627]  [<ffffffff81179f8a>] SyS_pwrite64+0x6a/0xa0
[   31.571123]  [<ffffffff818fde12>] system_call_fastpath+0x16/0x1b

Signed-off-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/mtd/ubi/upd.c

index 425bf5a..068a246 100644 (file)
@@ -135,6 +135,10 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
        ubi_assert(!vol->updating && !vol->changing_leb);
        vol->updating = 1;
 
+       vol->upd_buf = vmalloc(ubi->leb_size);
+       if (!vol->upd_buf)
+               return -ENOMEM;
+
        err = set_update_marker(ubi, vol);
        if (err)
                return err;
@@ -154,14 +158,12 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
                err = clear_update_marker(ubi, vol, 0);
                if (err)
                        return err;
+
+               vfree(vol->upd_buf);
                vol->updating = 0;
                return 0;
        }
 
-       vol->upd_buf = vmalloc(ubi->leb_size);
-       if (!vol->upd_buf)
-               return -ENOMEM;
-
        vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1,
                               vol->usable_leb_size);
        vol->upd_bytes = bytes;