All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wang Shilong <wangsl.fnst@cn.fujitsu.com>
To: Takashi Iwai <tiwai@suse.de>, Chris Mason <clm@fb.com>
Cc: Josef Bacik <jbacik@fb.com>, <linux-btrfs@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] Btrfs: Fix memory corruption by ulist_add_merge() on 32bit arch
Date: Mon, 28 Jul 2014 17:38:52 +0800	[thread overview]
Message-ID: <53D61A2C.40805@cn.fujitsu.com> (raw)
In-Reply-To: <1406537824-2376-1-git-send-email-tiwai@suse.de>

Hi Takashi,

This seems like a promising fix, i just have a little question:

ulist_add() logic is we firstly cast a pointer to u64(see ptr_to_u64()), 
and then
cast u64 to pointer back(u64_to_ptr()). So normally, arg u64 is actually 
a pointer.

If the below overflow happens, that means casting can change original value?

Am i missing something here?

Thanks,
Wang
On 07/28/2014 04:57 PM, Takashi Iwai wrote:
> We've got bug reports that btrfs crashes when quota is enabled on
> 32bit kernel, typically with the Oops like below:
>   BUG: unable to handle kernel NULL pointer dereference at 00000004
>   IP: [<f9234590>] find_parent_nodes+0x360/0x1380 [btrfs]
>   *pde = 00000000
>   Oops: 0000 [#1] SMP
>   CPU: 0 PID: 151 Comm: kworker/u8:2 Tainted: G S      W 3.15.2-1.gd43d97e-default #1
>   Workqueue: btrfs-qgroup-rescan normal_work_helper [btrfs]
>   task: f1478130 ti: f147c000 task.ti: f147c000
>   EIP: 0060:[<f9234590>] EFLAGS: 00010213 CPU: 0
>   EIP is at find_parent_nodes+0x360/0x1380 [btrfs]
>   EAX: f147dda8 EBX: f147ddb0 ECX: 00000011 EDX: 00000000
>   ESI: 00000000 EDI: f147dda4 EBP: f147ddf8 ESP: f147dd38
>    DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
>   CR0: 8005003b CR2: 00000004 CR3: 00bf3000 CR4: 00000690
>   Stack:
>    00000000 00000000 f147dda4 00000050 00000001 00000000 00000001 00000050
>    00000001 00000000 d3059000 00000001 00000022 000000a8 00000000 00000000
>    00000000 000000a1 00000000 00000000 00000001 00000000 00000000 11800000
>   Call Trace:
>    [<f923564d>] __btrfs_find_all_roots+0x9d/0xf0 [btrfs]
>    [<f9237bb1>] btrfs_qgroup_rescan_worker+0x401/0x760 [btrfs]
>    [<f9206148>] normal_work_helper+0xc8/0x270 [btrfs]
>    [<c025e38b>] process_one_work+0x11b/0x390
>    [<c025eea1>] worker_thread+0x101/0x340
>    [<c026432b>] kthread+0x9b/0xb0
>    [<c0712a71>] ret_from_kernel_thread+0x21/0x30
>    [<c0264290>] kthread_create_on_node+0x110/0x110
>
> This indicates a NULL corruption in prefs_delayed list.  The further
> investigation and bisection pointed that the call of ulist_add_merge()
> results in the corruption.
>
> ulist_add_merge() takes u64 as aux and writes a 64bit value into
> old_aux.  The callers of this function in backref.c, however, pass a
> pointer of a pointer to old_aux.  That is, the function overwrites
> 64bit value on 32bit pointer.  This caused a NULL in the adjacent
> variable, in this case, prefs_delayed.
>
> Here is a quick attempt to band-aid over this: a new function,
> ulist_add_merge_ptr() is introduced to pass/store properly a pointer
> value instead of u64.  There are still ugly void ** cast remaining
> in the callers because void ** cannot be taken implicitly.  But, it's
> safer than explicit cast to u64, anyway.
>
> Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=887046
> Cc: <stable@vger.kernel.org> [v3.11+]
> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> ---
>
> Alternatively, we can change the argument of aux and old_aux to a
> pointer from u64, as backref.c is the only user of ulist_add_merge()
> function.  I'll cook up another patch if it's the preferred way.
>
>   fs/btrfs/backref.c | 11 +++++------
>   fs/btrfs/ulist.h   | 15 +++++++++++++++
>   2 files changed, 20 insertions(+), 6 deletions(-)
>
> diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
> index e25564bfcb46..d7a24620a963 100644
> --- a/fs/btrfs/backref.c
> +++ b/fs/btrfs/backref.c
> @@ -276,9 +276,8 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
>   			}
>   			if (ret > 0)
>   				goto next;
> -			ret = ulist_add_merge(parents, eb->start,
> -					      (uintptr_t)eie,
> -					      (u64 *)&old, GFP_NOFS);
> +			ret = ulist_add_merge_ptr(parents, eb->start,
> +						  eie, (void **)&old, GFP_NOFS);
>   			if (ret < 0)
>   				break;
>   			if (!ret && extent_item_pos) {
> @@ -1008,9 +1007,9 @@ again:
>   					goto out;
>   				ref->inode_list = eie;
>   			}
> -			ret = ulist_add_merge(refs, ref->parent,
> -					      (uintptr_t)ref->inode_list,
> -					      (u64 *)&eie, GFP_NOFS);
> +			ret = ulist_add_merge_ptr(refs, ref->parent,
> +						  ref->inode_list,
> +						  (void **)&eie, GFP_NOFS);
>   			if (ret < 0)
>   				goto out;
>   			if (!ret && extent_item_pos) {
> diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h
> index 7f78cbf5cf41..695fc2bac680 100644
> --- a/fs/btrfs/ulist.h
> +++ b/fs/btrfs/ulist.h
> @@ -57,6 +57,21 @@ void ulist_free(struct ulist *ulist);
>   int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask);
>   int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
>   		    u64 *old_aux, gfp_t gfp_mask);
> +
> +/* just like ulist_add_merge() but take a pointer for the aux data */
> +static inline int ulist_add_merge_ptr(struct ulist *ulist, u64 val, void *aux,
> +				      void **old_aux, gfp_t gfp_mask)
> +{
> +#if BITS_PER_LONG == 32
> +	u64 old64 = (uintptr_t)*old_aux;
> +	int ret = ulist_add_merge(ulist, val, (uintptr_t)aux, &old64, gfp_mask);
> +	*old_aux = (void *)((uintptr_t)old64);
> +	return ret;
> +#else
> +	return ulist_add_merge(ulist, val, (u64)aux, (u64 *)old_aux, gfp_mask)
> +#endif
> +}
> +
>   struct ulist_node *ulist_next(struct ulist *ulist,
>   			      struct ulist_iterator *uiter);
>   


  reply	other threads:[~2014-07-28  9:44 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-28  8:57 [PATCH] Btrfs: Fix memory corruption by ulist_add_merge() on 32bit arch Takashi Iwai
2014-07-28  9:38 ` Wang Shilong [this message]
2014-07-28  9:49   ` Takashi Iwai
2014-07-28 13:16 ` Josef Bacik
2014-07-28 13:48   ` Takashi Iwai
2014-07-28 14:01     ` Takashi Iwai
2014-07-30  9:57       ` Takashi Iwai
2014-07-30 14:29         ` Josef Bacik
2014-07-30 15:01           ` Takashi Iwai
2014-07-30 15:05             ` Takashi Iwai
2014-07-30 15:40               ` Josef Bacik
2014-07-30 15:52                 ` Takashi Iwai
2014-07-30 16:01                   ` Josef Bacik
2014-07-30 16:35                     ` Takashi Iwai
2014-07-30 17:00                       ` Josef Bacik
2014-07-30 17:45                         ` Takashi Iwai
2014-08-06 15:14                           ` Takashi Iwai
2014-08-12 17:39                             ` Chris Mason
2014-08-14  9:54                               ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53D61A2C.40805@cn.fujitsu.com \
    --to=wangsl.fnst@cn.fujitsu.com \
    --cc=clm@fb.com \
    --cc=jbacik@fb.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.