diff mbox

[v2] btrfs-progs: fix segfault when listing column OTIME on big endian host

Message ID 1374761521-17603-1-git-send-email-guaneryu@gmail.com (mailing list archive)
State Accepted, archived
Headers show

Commit Message

Eryu Guan July 25, 2013, 2:12 p.m. UTC
The second btrfs command segfaults on big endian host(ppc64)

	btrfs subvolume snapshot /mnt/btrfs /mnt/btrfs/snap
	btrfs subvolume list -s /mnt/btrfs

And ltrace shows

	localtime(0x10029c482d0)                 = 0
	strftime( <no return ...>
	--- SIGSEGV (Segmentation fault) ---

The corresponding code

btrfs-list.c:
        case BTRFS_LIST_OTIME:
                if (subv->otime)
                        strftime(tstr, 256, "%Y-%m-%d %X",
                                 localtime(&subv->otime));
                else
                        strcpy(tstr, "-");
                printf("%s", tstr);
                break;

localtime() returned NULL then strftime() got SIGSEGV.

The reason is that ri->otime.sec is stored as little endian but
assigned to 't' without conversion.

Signed-off-by: Eryu Guan <guaneryu@gmail.com>
---
v1->v2:
  use btrfs_stack_timespec_sec() instead of raw convert.
  Thanks Miao Xie <miaox@cn.fujitsu.com> for pointing this out.

 btrfs-list.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Zach Brown July 25, 2013, 9:25 p.m. UTC | #1
> btrfs-list.c:
>         case BTRFS_LIST_OTIME:
>                 if (subv->otime)
>                         strftime(tstr, 256, "%Y-%m-%d %X",
>                                  localtime(&subv->otime));
>                 else
>                         strcpy(tstr, "-");
>                 printf("%s", tstr);
>                 break;
> 
> localtime() returned NULL then strftime() got SIGSEGV.
> 
> The reason is that ri->otime.sec is stored as little endian but
> assigned to 't' without conversion.

That's why localtime() returned null, sure, but it doesn't excuse
strftime() being called with a null *tm!  Add some error checking around
localtime().  It should warn that otime is nonsense, not crash.

- z
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Eryu Guan July 26, 2013, 4:34 a.m. UTC | #2
On Fri, Jul 26, 2013 at 5:25 AM, Zach Brown <zab@redhat.com> wrote:
>> btrfs-list.c:
>>         case BTRFS_LIST_OTIME:
>>                 if (subv->otime)
>>                         strftime(tstr, 256, "%Y-%m-%d %X",
>>                                  localtime(&subv->otime));
>>                 else
>>                         strcpy(tstr, "-");
>>                 printf("%s", tstr);
>>                 break;
>> 
>> localtime() returned NULL then strftime() got SIGSEGV.
>> 
>> The reason is that ri->otime.sec is stored as little endian but
>> assigned to 't' without conversion.
>
> That's why localtime() returned null, sure, but it doesn't excuse
> strftime() being called with a null *tm!  Add some error checking around
> localtime().  It should warn that otime is nonsense, not crash.
>

Yes, return value of localtime() should be checked. There're other
places call localtime() or localtime_r() without checking the return
value, I think another patch could fix them all and leave this patch
to fix the root cause.

Thanks,
Eryu Guan
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Sterba Aug. 1, 2013, 9:08 p.m. UTC | #3
On Thu, Jul 25, 2013 at 10:12:01PM +0800, Eryu Guan wrote:
> The second btrfs command segfaults on big endian host(ppc64)
> 
> 	btrfs subvolume snapshot /mnt/btrfs /mnt/btrfs/snap
> 	btrfs subvolume list -s /mnt/btrfs
> 
> And ltrace shows
> 
> 	localtime(0x10029c482d0)                 = 0
> 	strftime( <no return ...>
> 	--- SIGSEGV (Segmentation fault) ---
> 
> The corresponding code
> 
> btrfs-list.c:
>         case BTRFS_LIST_OTIME:
>                 if (subv->otime)
>                         strftime(tstr, 256, "%Y-%m-%d %X",
>                                  localtime(&subv->otime));
>                 else
>                         strcpy(tstr, "-");
>                 printf("%s", tstr);
>                 break;
> 
> localtime() returned NULL then strftime() got SIGSEGV.
> 
> The reason is that ri->otime.sec is stored as little endian but
> assigned to 't' without conversion.

I've sent fixes to both otime endianity and localtime crashes some days
ago and will keep them in integration branch. The reentrant variant of
localtime is safer, though the fix to otime access might be enough to
fix it.

http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg25676.html
http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg25677.html

david
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/btrfs-list.c b/btrfs-list.c
index 4fab858..9deddd5 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1052,7 +1052,7 @@  static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 				flags = btrfs_root_flags(ri);
 				if(sh.len >
 				   sizeof(struct btrfs_root_item_v0)) {
-					t = ri->otime.sec;
+					t = btrfs_stack_timespec_sec(&ri->otime);
 					ogen = btrfs_root_otransid(ri);
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
 					memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);