vfs: do the careful dentry name access for all dentry_cmp cases
Commit 12f8ad4b05
("vfs: clean up __d_lookup_rcu() and dentry_cmp()
interfaces") did the careful ACCESS_ONCE() of the dentry name only for
the word-at-a-time case, even though the issue is generic.
Admittedly I don't really see gcc ever reloading the value in the middle
of the loop, so the ACCESS_ONCE() protects us from a fairly theoretical
issue. But better safe than sorry.
Also, this consolidates the common parts of the word-at-a-time and
bytewise logic, which includes checking the length. We'll be changing
that later.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
8c01a529b8
commit
94753db5ed
1 changed files with 26 additions and 28 deletions
54
fs/dcache.c
54
fs/dcache.c
|
@ -153,30 +153,9 @@ int proc_nr_dentry(ctl_table *table, int write, void __user *buffer,
|
||||||
* In contrast, 'ct' and 'tcount' can be from a pathname, and do
|
* In contrast, 'ct' and 'tcount' can be from a pathname, and do
|
||||||
* need the careful unaligned handling.
|
* need the careful unaligned handling.
|
||||||
*/
|
*/
|
||||||
static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
|
static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char *ct, unsigned tcount)
|
||||||
{
|
{
|
||||||
unsigned long a,b,mask;
|
unsigned long a,b,mask;
|
||||||
const unsigned char *cs;
|
|
||||||
|
|
||||||
if (unlikely(dentry->d_name.len != tcount))
|
|
||||||
return 1;
|
|
||||||
/*
|
|
||||||
* Be careful about RCU walk racing with rename:
|
|
||||||
* use ACCESS_ONCE to fetch the name pointer.
|
|
||||||
*
|
|
||||||
* NOTE! Even if a rename will mean that the length
|
|
||||||
* was not loaded atomically, we don't care. The
|
|
||||||
* RCU walk will check the sequence count eventually,
|
|
||||||
* and catch it. And we won't overrun the buffer,
|
|
||||||
* because we're reading the name pointer atomically,
|
|
||||||
* and a dentry name is guaranteed to be properly
|
|
||||||
* terminated with a NUL byte.
|
|
||||||
*
|
|
||||||
* End result: even if 'len' is wrong, we'll exit
|
|
||||||
* early because the data cannot match (there can
|
|
||||||
* be no NUL in the ct/tcount data)
|
|
||||||
*/
|
|
||||||
cs = ACCESS_ONCE(dentry->d_name.name);
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
a = *(unsigned long *)cs;
|
a = *(unsigned long *)cs;
|
||||||
|
@ -197,13 +176,8 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
|
static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char *ct, unsigned tcount)
|
||||||
{
|
{
|
||||||
const unsigned char *cs = dentry->d_name.name;
|
|
||||||
|
|
||||||
if (dentry->d_name.len != tcount)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (*cs != *ct)
|
if (*cs != *ct)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -216,6 +190,30 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
|
||||||
|
{
|
||||||
|
if (dentry->d_name.len != tcount)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Be careful about RCU walk racing with rename:
|
||||||
|
* use ACCESS_ONCE to fetch the name pointer.
|
||||||
|
*
|
||||||
|
* NOTE! Even if a rename will mean that the length
|
||||||
|
* was not loaded atomically, we don't care. The
|
||||||
|
* RCU walk will check the sequence count eventually,
|
||||||
|
* and catch it. And we won't overrun the buffer,
|
||||||
|
* because we're reading the name pointer atomically,
|
||||||
|
* and a dentry name is guaranteed to be properly
|
||||||
|
* terminated with a NUL byte.
|
||||||
|
*
|
||||||
|
* End result: even if 'len' is wrong, we'll exit
|
||||||
|
* early because the data cannot match (there can
|
||||||
|
* be no NUL in the ct/tcount data)
|
||||||
|
*/
|
||||||
|
return dentry_string_cmp(ACCESS_ONCE(dentry->d_name.name), ct, tcount);
|
||||||
|
}
|
||||||
|
|
||||||
static void __d_free(struct rcu_head *head)
|
static void __d_free(struct rcu_head *head)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu);
|
struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu);
|
||||||
|
|
Loading…
Reference in a new issue