develooper Front page | perl.perl5.porters | Postings from November 2008

[perl #60574] sv_upgrade() loses 64-bit alignment, causing SIGBUS on sparc

Thread Next
Niko Tyni
November 16, 2008 07:46
[perl #60574] sv_upgrade() loses 64-bit alignment, causing SIGBUS on sparc
Message ID:
# New Ticket Created by  Niko Tyni 
# Please include the string:  [perl #60574]
# in the subject line of all future correspondence about this issue. 
# <URL: >

This is a bug report for perl from Niko Tyni <>
generated with the help of perlbug 1.39 running under perl 5.11.0.

As seen in <>, calling sv_chop() or otherwise
upgrading an SV can result in an unaligned 64-bit access on the sparc

This was uncovered by the JSON-XS-2.23 test suite failing for us on
sparc with Perl 5.10.0. The failure is a SIGBUS that only occurs with
gcc optimization level -O2 or higher.

On 5.10.0, calling sv_chop() will upgrade the SV into an SVt_PVIV due
to the 'OOK hack'. The upgrade will allocate a new memory area for
sv->sv_any, possibly losing the 64-bit alignment in the process.

Calling the SvGROW() macro afterwards will cast sv->sv_any to an XPV
pointer, which should be 64-bit aligned according to __alignof__().
With -O2, gcc will compile the code using a doubleword load instruction
(ldd) that fails with a SIGBUS if the 64-bit alignment has been lost.

I'm attaching a test program that mimics what JSON-XS does when compiled
with -DUSE_CHOP. The test program compiled at -O2 dies with a SIGBUS
for me on the second doit() call.

This particular failure mode has been fixed in bleadperl with change
32836, which reimplements the 'OOK hack' in a different way so that the
SV isn't upgraded anymore. The failure is still present in maint-5.10,
as of change 34715.

However, it seems to me that this is a more general problem that should
be fixed by making sv_upgrade() somehow retain the alignment of the
existing SV. 

The same test program, when compiled with -O2 but without -DUSE_CHOP
(making it explicitly call SvUPGRADE() instead), dies here on current
bleadperl (change 34714) with a SIGBUS on the second doit() call.

Note that using a debugging perl build (-DDEBUGGING) seems to defeat the crash.

Details for the bleadperl SIGBUS:

 Program received signal SIGBUS, Bus error.
 [Switching to Thread 0xf7aca6b0 (LWP 32099)]
 0x0001070c in doit () at crash.c:15
 15      SvGROW (sv, SvCUR (sv) + 1);
 (gdb) bt
 #0  0x0001070c in doit () at crash.c:15
 #1  0x00010774 in main (argc=<value optimized out>, argv=0xff995984, env=0xff995990) at crash.c:25
 (gdb) print *sv
 $1 = {sv_any = 0x2569c, sv_refcnt = 1, sv_flags = 17413, sv_u = {svu_iv = 157328, svu_uv = 157328,
     svu_rv = 0x26690, svu_pv = 0x26690 "foo", svu_array = 0x26690, svu_hash = 0x26690, svu_gp = 0x26690}}

The preprocessor output for the above SvGROW() invocation is

 (((XPV*) (sv)->sv_any)->xpv_len < (((XPV*) (sv)->sv_any)->xpv_cur + 1) ? Perl_sv_grow(my_perl, sv,((XPV*) (sv)->sv_any)->xpv_cur + 1) : ((sv)->sv_u.svu_pv));

The instruction that causes the bus error is a double-word load (ldd)
during the first comparison. The compiler (gcc -O2) is using the
double-word instruction to load both xpv_cur and xpv_len in one go.

 0x00010708 <doit+56>:   ld  [ %i1 ], %g1
 0x0001070c <doit+60>:   ldd  [ %g1 + 8 ], %g2
 0x00010710 <doit+64>:   add  %g2, 1, %i2
 0x00010714 <doit+68>:   cmp  %g3, %i2
Quoting Julien Cristau in the Debian bug:

> __alignof__(XPV) is 8, so gcc is allowed to assume that any XPV is
> 64-bit aligned, as far as I can tell.  xpv_cur's offset is 8, so it
> should also be 64-bit aligned.

Site configuration information for perl 5.11.0:

Configured by niko at Sat Nov 15 18:40:57 UTC 2008.

Summary of my perl5 (revision 5 version 11 subversion 0) configuration:
    osname=linux, osvers=2.6.24-etchnhalf.1-sparc64-smp, archname=sparc-linux-gnu-thread-multi
    uname='linux raakel 2.6.24-etchnhalf.1-sparc64-smp #1 smp tue oct 14 04:04:33 utc 2008 sparc64 gnulinux '
    config_args='-Dusethreads -Duselargefiles -Dcccdlflags=-fPIC -Darchname=sparc-linux-gnu -Uafs -Ud_csh -Ud_ualarm -Uusesfio -Uusenm -DDEBUGGING=-g -Dprefix=/home/niko/bleadperl -Dusedevel -Doptimize=-O2 -Duseshrplib -des'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
    cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -g',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
    ccversion='', gccversion='4.3.2', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=4321
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib /lib64 /usr/lib64
    libs=-lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc -lgdbm_compat
    perllibs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
    libc=/lib/, so=so, useshrplib=true,
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/home/niko/bleadperl/lib/5.11.0/sparc-linux-gnu-thread-multi/CORE'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -g -L/usr/local/lib -fstack-protector'

Locally applied patches:

@INC for perl 5.11.0:

Environment for perl 5.11.0:
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PERL_BADLANG (unset)

Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About