btrfs-progs: image: Fix data races when reporting progress
authorAdam Buchbinder <abuchbinder@google.com>
Wed, 12 Jul 2017 20:05:10 +0000 (13:05 -0700)
committerDavid Sterba <dsterba@suse.com>
Thu, 20 Jul 2017 15:43:43 +0000 (17:43 +0200)
Making the code data-race safe requires that reads *and* writes
happen under a mutex lock, if any of the access are writes. See
Dmitri Vyukov, "Benign data races: what could possibly go wrong?"
for more details.

The fix here was to put most of the main loop of restore_worker
under a mutex lock.

This race was detected using fsck-tests/012-leaf-corruption.

==================
WARNING: ThreadSanitizer: data race
  Write of size 4 by main thread:
    #0 add_cluster btrfs-progs/image/main.c:1931
    #1 restore_metadump btrfs-progs/image/main.c:2566
    #2 main btrfs-progs/image/main.c:2859

  Previous read of size 4 by thread T6:
    #0 restore_worker btrfs-progs/image/main.c:1720

  Location is stack of main thread.

  Thread T6 (running) created by main thread at:
    #0 pthread_create <null>
    #1 mdrestore_init btrfs-progs/image/main.c:1868
    #2 restore_metadump btrfs-progs/image/main.c:2534
    #3 main btrfs-progs/image/main.c:2859

SUMMARY: ThreadSanitizer: data race btrfs-progs/image/main.c:1931 in
add_cluster

Signed-off-by: Adam Buchbinder <abuchbinder@google.com>
Signed-off-by: David Sterba <dsterba@suse.com>
image/main.c

index fac9b76..2100f20 100644 (file)
@@ -1720,12 +1720,13 @@ static void *restore_worker(void *data)
                }
                async = list_entry(mdres->list.next, struct async_work, list);
                list_del_init(&async->list);
-               pthread_mutex_unlock(&mdres->mutex);
 
                if (mdres->compress_method == COMPRESS_ZLIB) {
                        size = compress_size; 
+                       pthread_mutex_unlock(&mdres->mutex);
                        ret = uncompress(buffer, (unsigned long *)&size,
                                         async->buffer, async->bufsize);
+                       pthread_mutex_lock(&mdres->mutex);
                        if (ret != Z_OK) {
                                error("decompressiion failed with %d", ret);
                                err = -EIO;
@@ -1803,7 +1804,6 @@ error:
                if (!mdres->multi_devices && async->start == BTRFS_SUPER_INFO_OFFSET)
                        write_backup_supers(outfd, outbuf);
 
-               pthread_mutex_lock(&mdres->mutex);
                if (err && !mdres->error)
                        mdres->error = err;
                mdres->num_items--;
@@ -1933,7 +1933,9 @@ static int add_cluster(struct meta_cluster *cluster,
        u32 i, nritems;
        int ret;
 
+       pthread_mutex_lock(&mdres->mutex);
        mdres->compress_method = header->compress;
+       pthread_mutex_unlock(&mdres->mutex);
 
        bytenr = le64_to_cpu(header->bytenr) + BLOCK_SIZE;
        nritems = le32_to_cpu(header->nritems);