]> git.hungrycats.org Git - linux/commitdiff
[PATCH] tmpfs 4/7 getpage/truncate race fix
authorAndrew Morton <akpm@osdl.org>
Wed, 22 Oct 2003 01:20:16 +0000 (18:20 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Wed, 22 Oct 2003 01:20:16 +0000 (18:20 -0700)
From: Hugh Dickins <hugh@veritas.com>

Extend use of that SHMEM_PAGEIN flag to where shmem_getpage adds a page
to the cache.  It couldn't have caused a BUG_ON(inode->i_blocks), but if
i_size is reduced (from another cpu) the instant after shmem_swp_alloc
checks it, shmem_getpage could insert a page into the cache just after
truncate_inode_pages has passed through cleaning it, leaving stale data
(which may mysteriously reappear if the file is later extended).

Easily fixed for tmpfs, using the mechanism just added for swapoff; and
probably more important there, since its read from swap can insert non-0
data.  But is there not a similar issue, a tiny window, in filemap.c?
if truncate_inode_pages comes in between checking i_size and adding new
page to cache.  Not worth getting excited, but something to beware of.

mm/shmem.c

index 573430c7f7e5b5c5a0b07bbb54e5162a7e94e2c3..724ed5427e5a61a349ab463d5ac028b3a72c7e0a 100644 (file)
@@ -52,7 +52,7 @@
 
 #define VM_ACCT(size)    (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
 
-/* info->flags needs a VM_flag to handle swapoff/truncate races efficiently */
+/* info->flags needs a VM_flag to handle pagein/truncate races efficiently */
 #define SHMEM_PAGEIN    VM_READ
 
 /* Pretend that each entry is of this size in directory's i_size */
@@ -498,6 +498,8 @@ done2:
                 * Call truncate_inode_pages again: racing shmem_unuse_inode
                 * may have swizzled a page in from swap since vmtruncate or
                 * generic_delete_inode did it, before we lowered next_index.
+                * Also, though shmem_getpage checks i_size before adding to
+                * cache, no recheck after: so fix the narrow window there too.
                 */
                spin_unlock(&info->lock);
                truncate_inode_pages(inode->i_mapping, inode->i_size);
@@ -863,6 +865,7 @@ repeat:
                        swap_free(swap);
                } else if (!(error = move_from_swap_cache(
                                swappage, idx, mapping))) {
+                       info->flags |= SHMEM_PAGEIN;
                        shmem_swp_set(info, entry, 0);
                        shmem_swp_unmap(entry);
                        spin_unlock(&info->lock);
@@ -932,6 +935,7 @@ repeat:
                                        goto failed;
                                goto repeat;
                        }
+                       info->flags |= SHMEM_PAGEIN;
                }
 
                info->alloced++;