Bug 20489 - timer_create in compatibility mode on a 64bit big-endian system segfaults
Summary: timer_create in compatibility mode on a 64bit big-endian system segfaults
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: librt (show other bugs)
Version: 2.19
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-08-18 21:20 UTC by ian.merin
Modified: 2017-01-12 18:39 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2016-08-19 00:00:00
fweimer: security-


Attachments
code sample (688 bytes, text/x-csrc)
2016-08-19 20:53 UTC, ian.merin
Details
code sample (117 bytes, text/x-chdr)
2016-08-19 20:56 UTC, ian.merin
Details
code sample (555 bytes, text/x-csrc)
2016-08-19 20:57 UTC, ian.merin
Details
main (310 bytes, text/x-csrc)
2016-08-19 20:58 UTC, ian.merin
Details

Note You need to log in before you can comment on or make changes to this bug.
Description ian.merin 2016-08-18 21:20:40 UTC
On a big-endian system running a 64 bit operating system (specifically ppc64 GNU/Linux 3.12.37)

In the case that the following is true

#if SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)

timer_create is redirected to
int __timer_create_old (clockid_t clock_id, struct sigevent *evp,
                              int *timerid)

however, the signature for timer_create is:


int timer_create(clockid_t clockid, struct sigevent *sevp,
                        timer_t *timerid);

On this system, timer_t is defined as a 64bit type, while int is defined as a 32 bit type.


Thus when the timerid is dereferenced and set to a specific index in the 
timer_t __compat_timer_list[OLD_TIMER_MAX] attribute_hidden;
array, the least significant 32 bits are ignored, and the timer_t variable is incorrectly assigned a very high index.

e.g.

If the timer index returned is one, the memory for timerid will look like

0x0000000100000000, instead of 0x1

Of course this causes access of invalid memory, and the calling program segfaults.
Comment 1 ian.merin 2016-08-18 21:27:47 UTC
# /lib64/libc.so.6 
GNU C Library (EGLIBC) stable release version 2.19, by Roland McGrath et al.
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.9.2.
Compiled on a Linux 3.14.0 system on 2016-08-10.
Available extensions:
        crypt add-on version 2.1 by Michael Glad and others
        GNU Libidn by Simon Josefsson
        Native POSIX Threads Library by Ulrich Drepper et al
        BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.eglibc.org/issues/>.
Comment 2 joseph@codesourcery.com 2016-08-18 21:30:13 UTC
On Thu, 18 Aug 2016, ian.merin at thalesesec dot com wrote:

> #if SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)

> timer_create is redirected to
> int __timer_create_old (clockid_t clock_id, struct sigevent *evp,
>                               int *timerid)
> 
> however, the signature for timer_create is:
> 
> 
> int timer_create(clockid_t clockid, struct sigevent *sevp,
>                         timer_t *timerid);
> 
> On this system, timer_t is defined as a 64bit type, while int is defined as a
> 32 bit type.

And in glibc versions in the relevant interval, timer_t was int.  So this 
redirection to a function taking int * is correct; callers of this 
function were compiled against glibc versions at least 2.2 and less than 
2.3.3, and will be passing a pointer to int.

If there's a problem here, you'll need to explain it further.
Comment 3 ian.merin 2016-08-18 21:49:30 UTC
(In reply to joseph@codesourcery.com from comment #2)
> On Thu, 18 Aug 2016, ian.merin at thalesesec dot com wrote:
> 
> > #if SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)
> 
> > timer_create is redirected to
> > int __timer_create_old (clockid_t clock_id, struct sigevent *evp,
> >                               int *timerid)
> > 
> > however, the signature for timer_create is:
> > 
> > 
> > int timer_create(clockid_t clockid, struct sigevent *sevp,
> >                         timer_t *timerid);
> > 
> > On this system, timer_t is defined as a 64bit type, while int is defined as a
> > 32 bit type.
> 
> And in glibc versions in the relevant interval, timer_t was int.  So this 
> redirection to a function taking int * is correct; callers of this 
> function were compiled against glibc versions at least 2.2 and less than 
> 2.3.3, and will be passing a pointer to int.
> 
> If there's a problem here, you'll need to explain it further.

I see.  It would seem that the problem is not what I thought it was. timer_t is defined as a void * on the system, and version 2.19 of glibc is being used, yet we are still entering the version of timer_create meant for a much older version of glibc.  

Thanks, I'll have to investigate further.
Comment 4 Florian Weimer 2016-08-19 13:29:11 UTC
(In reply to ian.merin from comment #3)
> (In reply to joseph@codesourcery.com from comment #2)
> > On Thu, 18 Aug 2016, ian.merin at thalesesec dot com wrote:
> > 
> > > #if SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)
> > 
> > > timer_create is redirected to
> > > int __timer_create_old (clockid_t clock_id, struct sigevent *evp,
> > >                               int *timerid)
> > > 
> > > however, the signature for timer_create is:
> > > 
> > > 
> > > int timer_create(clockid_t clockid, struct sigevent *sevp,
> > >                         timer_t *timerid);
> > > 
> > > On this system, timer_t is defined as a 64bit type, while int is defined as a
> > > 32 bit type.
> > 
> > And in glibc versions in the relevant interval, timer_t was int.  So this 
> > redirection to a function taking int * is correct; callers of this 
> > function were compiled against glibc versions at least 2.2 and less than 
> > 2.3.3, and will be passing a pointer to int.
> > 
> > If there's a problem here, you'll need to explain it further.
> 
> I see.  It would seem that the problem is not what I thought it was. timer_t
> is defined as a void * on the system, and version 2.19 of glibc is being
> used, yet we are still entering the version of timer_create meant for a much
> older version of glibc.  
> 
> Thanks, I'll have to investigate further.
Comment 5 ian.merin 2016-08-19 20:07:51 UTC
I have tested this on multiple systems with multiple architectures and different versions of glibc with the same result.  

When using dynamic linking, I cannot avoid using __timer_create_old.  timer_id remains an integer inside to a void * that happens to work because the system is little endian.

My latest test was done on the following system:

4.5.4-1-ARCH #1 SMP PREEMPT Wed May 11 22:21:28 CEST 2016 x86_64 GNU/Linux

$ /usr/lib64/libc.so.6 
GNU C Library (GNU libc) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 6.1.1 20160501.
Available extensions:
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.archlinux.org/>.

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --disable-multilib --disable-werror --enable-checking=release
Thread model: posix
gcc version 6.1.1 20160501 (GCC)
Comment 6 Florian Weimer 2016-08-19 20:15:53 UTC
Please show us the output of “eu-readelf -s libc.so.6 | grep timer_create” for one of the librt.so.1 files.

Do you use dlsym?

Can you post a small example which reproduces the issue, together with the commands you use to compile it?
Comment 7 Florian Weimer 2016-08-19 20:26:58 UTC
(In reply to Florian Weimer from comment #6)
> Please show us the output of “eu-readelf -s libc.so.6 | grep timer_create”
> for one of the librt.so.1 files.
> 
> Do you use dlsym?
> 
> Can you post a small example which reproduces the issue, together with the
> commands you use to compile it?

To clarify, the command should be this:

  eu-readelf -s librt.so.1 | grep timer_create

(readelf instead of eu-readelf should work as well.)
Comment 8 ian.merin 2016-08-19 20:51:49 UTC
I have tested this on multiple systems with multiple architectures and different versions of glibc with the same result.  

When using dynamic linking, I cannot avoid using __timer_create_old.  timer_id remains an integer inside to a void * that happens to work because the system is little endian.

My latest test was done on the following system:

4.5.4-1-ARCH #1 SMP PREEMPT Wed May 11 22:21:28 CEST 2016 x86_64 GNU/Linux

$ /usr/lib64/libc.so.6 
GNU C Library (GNU libc) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 6.1.1 20160501.
Available extensions:
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.archlinux.org/>.

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --disable-multilib --disable-werror --enable-checking=release
Thread model: posix
gcc version 6.1.1 20160501 (GCC)(In reply to Florian Weimer from comment #7)
> (In reply to Florian Weimer from comment #6)
> > Please show us the output of “eu-readelf -s libc.so.6 | grep timer_create”
> > for one of the librt.so.1 files.
> > 
> > Do you use dlsym?
> > 
> > Can you post a small example which reproduces the issue, together with the
> > commands you use to compile it?
> 
> To clarify, the command should be this:
> 
>   eu-readelf -s librt.so.1 | grep timer_create
> 
> (readelf instead of eu-readelf should work as well.)

eu-readelf -s /usr/lib64/librt.so.1 | grep timer_create
  106: 0000000000003ed0    135 FUNC    GLOBAL DEFAULT       14 timer_create@GLIBC_2.2.5
  107: 0000000000003c50    625 FUNC    GLOBAL DEFAULT       14 timer_create@@GLIBC_2.3.3

I'll attach the smallest a stripped down version of the code and compiler options that produced this issue.


  524  gcc -c -fPIC timer.c 
  525  gcc -shared -o libtimer.so timer.o
  527  gcc timer_test.c -L./ -o timer_test -ltimer -lrt
  528  ./timer_test 

outputs:
timer ID is 0x0
timer ID is 0x1
timer ID is 0x2

expect timer_id to be a pointer (this is a little_endian machine so the integer value appears correct)
Comment 9 ian.merin 2016-08-19 20:53:14 UTC
Created attachment 9459 [details]
code sample
Comment 10 ian.merin 2016-08-19 20:54:54 UTC
Comment on attachment 9459 [details]
code sample


>
>
>/*
>*  Included Files
>*/
>
>#include <sys/time.h>
>#include <stdio.h>
>#include <signal.h>
>#include <unistd.h>
>#include <time.h>
>#include <pthread.h>
>#include <stdlib.h>
>#include <signal.h>
>#include <time.h>
>#include "timer.h"
>
>void my_callback(sigval_t id)
>{
>   static int x = 0;
>   printf("   Callback: %d, id = %p\n", ++x, id.sival_ptr);
>} /* end my_callback */
>
>
>int main(int argc, char *argv[])
>{
>	int  rv;
>	void         *tid;
>	void         *tid2;
>	void         *tid3;
>
>
>
>   rv = test_timer_create(&tid, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid2, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid3, my_callback, 500, 500, 1);
>
>   return(EXIT_SUCCESS);
>
>} /* end main */
Comment 11 ian.merin 2016-08-19 20:55:38 UTC
Comment on attachment 9459 [details]
code sample

>
>/*
>*  Included Files
>*/
>
>#include <sys/time.h>
>#include <stdio.h>
>#include <signal.h>
>#include <unistd.h>
>#include <time.h>
>#include <pthread.h>
>#include <stdlib.h>
>#include <signal.h>
>#include <time.h>
>#include "timer.h"
>
>void my_callback(sigval_t id)
>{
>   static int x = 0;
>   printf("   Callback: %d, id = %p\n", ++x, id.sival_ptr);
>} /* end my_callback */
>
>
>int main(int argc, char *argv[])
>{
>	int  rv;
>	void         *tid;
>	void         *tid2;
>	void         *tid3;
>
>
>
>   rv = test_timer_create(&tid, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid2, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid3, my_callback, 500, 500, 1);
>
>   return(EXIT_SUCCESS);
>
>} /* end main */
Comment 12 ian.merin 2016-08-19 20:56:17 UTC
Comment on attachment 9459 [details]
code sample

>
>
>/*
>*  Included Files
>*/
>
>#include <sys/time.h>
>#include <stdio.h>
>#include <signal.h>
>#include <unistd.h>
>#include <time.h>
>#include <pthread.h>
>#include <stdlib.h>
>#include <signal.h>
>#include <time.h>
>#include "timer.h"
>
>void my_callback(sigval_t id)
>{
>   static int x = 0;
>   printf("   Callback: %d, id = %p\n", ++x, id.sival_ptr);
>} /* end my_callback */
>
>
>int main(int argc, char *argv[])
>{
>	int  rv;
>	void         *tid;
>	void         *tid2;
>	void         *tid3;
>
>
>
>   rv = test_timer_create(&tid, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid2, my_callback, 500, 500, 1);
>   rv = test_timer_create(&tid3, my_callback, 500, 500, 1);
>
>   return(EXIT_SUCCESS);
>
>} /* end main */
Comment 13 ian.merin 2016-08-19 20:56:55 UTC
Created attachment 9460 [details]
code sample
Comment 14 ian.merin 2016-08-19 20:57:57 UTC
Created attachment 9461 [details]
code sample
Comment 15 ian.merin 2016-08-19 20:58:53 UTC
Created attachment 9462 [details]
main
Comment 16 Florian Weimer 2016-08-22 14:07:48 UTC
You do not link libtimer.so against librt.  As a result, libtimer.so contains a non-versioned symbol reference, which is bound to a compatibility symbol.

We currently do not have good diagnostics for this issue.  I don't know yet if this is something we can improve in binutils or in ld.so.
Comment 17 ian.merin 2016-08-22 14:42:45 UTC
(In reply to Florian Weimer from comment #16)
> You do not link libtimer.so against librt.  As a result, libtimer.so
> contains a non-versioned symbol reference, which is bound to a compatibility
> symbol.
> 
> We currently do not have good diagnostics for this issue.  I don't know yet
> if this is something we can improve in binutils or in ld.so.

Thank you.  That was the problem.  The issue is resolved.
Comment 18 Florian Weimer 2016-08-22 14:45:27 UTC
I started a cross-posted mailing list discussion:

  https://sourceware.org/ml/libc-alpha/2016-08/msg00694.html
  https://sourceware.org/ml/binutils/2016-08/msg00154.html

I will close/clone/move this bug depending on the outcome of that discussion.