Commit 7ea600b5314529f9d1b9d6d3c41cb26fce6a7a4a

Authored by Al Viro
1 parent 06ae43f34b

Nest rename_lock inside vfsmount_lock

... lest we get livelocks between path_is_under() and d_path() and friends.

The thing is, wrt fairness lglocks are more similar to rwsems than to rwlocks;
it is possible to have thread B spin on attempt to take lock shared while thread
A is already holding it shared, if B is on lower-numbered CPU than A and there's
a thread C spinning on attempt to take the same lock exclusive.

As the result, we need consistent ordering between vfsmount_lock (lglock) and
rename_lock (seq_lock), even though everything that takes both is going to take
vfsmount_lock only shared.

Spotted-by: Brad Spengler <spender@grsecurity.net>
Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Showing 1 changed file with 11 additions and 5 deletions Side-by-side Diff

... ... @@ -2542,7 +2542,6 @@
2542 2542 bool slash = false;
2543 2543 int error = 0;
2544 2544  
2545   - br_read_lock(&vfsmount_lock);
2546 2545 while (dentry != root->dentry || vfsmnt != root->mnt) {
2547 2546 struct dentry * parent;
2548 2547  
... ... @@ -2572,8 +2571,6 @@
2572 2571 if (!error && !slash)
2573 2572 error = prepend(buffer, buflen, "/", 1);
2574 2573  
2575   -out:
2576   - br_read_unlock(&vfsmount_lock);
2577 2574 return error;
2578 2575  
2579 2576 global_root:
... ... @@ -2590,7 +2587,7 @@
2590 2587 error = prepend(buffer, buflen, "/", 1);
2591 2588 if (!error)
2592 2589 error = is_mounted(vfsmnt) ? 1 : 2;
2593   - goto out;
  2590 + return error;
2594 2591 }
2595 2592  
2596 2593 /**
2597 2594  
... ... @@ -2617,9 +2614,11 @@
2617 2614 int error;
2618 2615  
2619 2616 prepend(&res, &buflen, "\0", 1);
  2617 + br_read_lock(&vfsmount_lock);
2620 2618 write_seqlock(&rename_lock);
2621 2619 error = prepend_path(path, root, &res, &buflen);
2622 2620 write_sequnlock(&rename_lock);
  2621 + br_read_unlock(&vfsmount_lock);
2623 2622  
2624 2623 if (error < 0)
2625 2624 return ERR_PTR(error);
2626 2625  
... ... @@ -2636,9 +2635,11 @@
2636 2635 int error;
2637 2636  
2638 2637 prepend(&res, &buflen, "\0", 1);
  2638 + br_read_lock(&vfsmount_lock);
2639 2639 write_seqlock(&rename_lock);
2640 2640 error = prepend_path(path, &root, &res, &buflen);
2641 2641 write_sequnlock(&rename_lock);
  2642 + br_read_unlock(&vfsmount_lock);
2642 2643  
2643 2644 if (error > 1)
2644 2645 error = -EINVAL;
2645 2646  
2646 2647  
... ... @@ -2702,11 +2703,13 @@
2702 2703 return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
2703 2704  
2704 2705 get_fs_root(current->fs, &root);
  2706 + br_read_lock(&vfsmount_lock);
2705 2707 write_seqlock(&rename_lock);
2706 2708 error = path_with_deleted(path, &root, &res, &buflen);
  2709 + write_sequnlock(&rename_lock);
  2710 + br_read_unlock(&vfsmount_lock);
2707 2711 if (error < 0)
2708 2712 res = ERR_PTR(error);
2709   - write_sequnlock(&rename_lock);
2710 2713 path_put(&root);
2711 2714 return res;
2712 2715 }
... ... @@ -2830,6 +2833,7 @@
2830 2833 get_fs_root_and_pwd(current->fs, &root, &pwd);
2831 2834  
2832 2835 error = -ENOENT;
  2836 + br_read_lock(&vfsmount_lock);
2833 2837 write_seqlock(&rename_lock);
2834 2838 if (!d_unlinked(pwd.dentry)) {
2835 2839 unsigned long len;
... ... @@ -2839,6 +2843,7 @@
2839 2843 prepend(&cwd, &buflen, "\0", 1);
2840 2844 error = prepend_path(&pwd, &root, &cwd, &buflen);
2841 2845 write_sequnlock(&rename_lock);
  2846 + br_read_unlock(&vfsmount_lock);
2842 2847  
2843 2848 if (error < 0)
2844 2849 goto out;
... ... @@ -2859,6 +2864,7 @@
2859 2864 }
2860 2865 } else {
2861 2866 write_sequnlock(&rename_lock);
  2867 + br_read_unlock(&vfsmount_lock);
2862 2868 }
2863 2869  
2864 2870 out: