[2/2] test: py: LD_PRELOAD the C++ standard library when using ASan
diff mbox series

Message ID 20241105014949.32244-3-laurent.pinchart@ideasonboard.com
State Accepted
Commit f326eb4bf26e359d980f60cfa81cc25e8347f26f
Headers show
Series
  • test: py: Fix unit test error with ASan
Related show

Commit Message

Laurent Pinchart Nov. 5, 2024, 1:49 a.m. UTC
When the ASan runtime is linked using --as-needed, its dependency on the
C++ standard library is stripped. This results to a failure to properly
handled exceptions when a C++ dynamically loaded .so is used, as in the
Python unit tests that load the libcamera Python module:

AddressSanitizer: CHECK failed: asan_interceptors.cpp:335 "((__interception::real___cxa_throw)) != (0)" (0x0, 0x0) (tid=32679)
    #0 0x7fa2f32e6c19 in CheckUnwind /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_rtl.cpp:69
    #1 0x7fa2f330c9fd in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/sanitizer_common/sanitizer_termination.cpp:86
    #2 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:335
    #3 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:334
    #4 0x7fa2efb6da8b in operator() ../../src/py/libcamera/py_main.cpp:157
[...]

The issue has been reported in [1] and so far remains unfixed. Work
around it by preloading the C++ standard library.

[1] https://github.com/google/sanitizers/issues/934

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 meson.build         | 3 +++
 test/py/meson.build | 6 ++++++
 2 files changed, 9 insertions(+)

Comments

Kieran Bingham Nov. 5, 2024, 10:17 a.m. UTC | #1
Quoting Laurent Pinchart (2024-11-05 01:49:49)
> When the ASan runtime is linked using --as-needed, its dependency on the
> C++ standard library is stripped. This results to a failure to properly
> handled exceptions when a C++ dynamically loaded .so is used, as in the
> Python unit tests that load the libcamera Python module:
> 
> AddressSanitizer: CHECK failed: asan_interceptors.cpp:335 "((__interception::real___cxa_throw)) != (0)" (0x0, 0x0) (tid=32679)
>     #0 0x7fa2f32e6c19 in CheckUnwind /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_rtl.cpp:69
>     #1 0x7fa2f330c9fd in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/sanitizer_common/sanitizer_termination.cpp:86
>     #2 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:335
>     #3 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:334
>     #4 0x7fa2efb6da8b in operator() ../../src/py/libcamera/py_main.cpp:157
> [...]
> 
> The issue has been reported in [1] and so far remains unfixed. Work
> around it by preloading the C++ standard library.
> 
> [1] https://github.com/google/sanitizers/issues/934
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  meson.build         | 3 +++
>  test/py/meson.build | 6 ++++++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/meson.build b/meson.build
> index 63e45465da9c..e3052260f13e 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -112,6 +112,8 @@ common_arguments = [
>  c_arguments = []
>  cpp_arguments = []
>  
> +cxx_stdlib = 'libstdc++'
> +
>  if cc.get_id() == 'clang'
>      if cc.version().version_compare('<9')
>          error('clang version is too old, libcamera requires 9.0 or newer')
> @@ -137,6 +139,7 @@ if cc.get_id() == 'clang'
>          cpp_arguments += [
>              '-stdlib=libc++',
>          ]
> +        cxx_stdlib = 'libc++'
>      endif
>  
>      cpp_arguments += [
> diff --git a/test/py/meson.build b/test/py/meson.build
> index 481bb481c110..b922e8578c29 100644
> --- a/test/py/meson.build
> +++ b/test/py/meson.build
> @@ -24,6 +24,12 @@ py_env.append('PYTHONPATH', pypathdir)
>  if asan_enabled
>      py_env.append('LD_PRELOAD', asan_runtime)
>  
> +    # Preload the C++ standard library to work around a bug in ASan when
> +    # dynamically loading C++ .so modules.
> +    stdlib = run_command(cxx, '-print-file-name=' + cxx_stdlib + '.so',
> +                         check : true).stdout().strip()
> +    py_env.append('LD_PRELOAD', stdlib)
> +

I assume it is, but have never used LD_PRELOAD this way - is it safe to
have multiple instances of the environment variable?


Does this make a space separated list? Or does it make
LD_PRELOAD=file1.so LD_PRELOAD=file2.so appear in the env?

I presume whatever it does it works as you'll have used it ...

so presumptively:


Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

But I'm interested to know which form is used by meson when appending
the same variable to an environment for future use cases.

--
Kieran


>      # Disable leak detection as the Python interpreter is full of leaks.
>      py_env.append('ASAN_OPTIONS', 'detect_leaks=0')
>  endif
> -- 
> Regards,
> 
> Laurent Pinchart
>
Laurent Pinchart Nov. 5, 2024, 10:40 a.m. UTC | #2
On Tue, Nov 05, 2024 at 10:17:13AM +0000, Kieran Bingham wrote:
> Quoting Laurent Pinchart (2024-11-05 01:49:49)
> > When the ASan runtime is linked using --as-needed, its dependency on the
> > C++ standard library is stripped. This results to a failure to properly
> > handled exceptions when a C++ dynamically loaded .so is used, as in the
> > Python unit tests that load the libcamera Python module:
> > 
> > AddressSanitizer: CHECK failed: asan_interceptors.cpp:335 "((__interception::real___cxa_throw)) != (0)" (0x0, 0x0) (tid=32679)
> >     #0 0x7fa2f32e6c19 in CheckUnwind /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_rtl.cpp:69
> >     #1 0x7fa2f330c9fd in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/sanitizer_common/sanitizer_termination.cpp:86
> >     #2 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:335
> >     #3 0x7fa2f3247824 in __interceptor___cxa_throw /var/tmp/portage/sys-devel/gcc-13.3.1_p20241025/work/gcc-13-20241025/libsanitizer/asan/asan_interceptors.cpp:334
> >     #4 0x7fa2efb6da8b in operator() ../../src/py/libcamera/py_main.cpp:157
> > [...]
> > 
> > The issue has been reported in [1] and so far remains unfixed. Work
> > around it by preloading the C++ standard library.
> > 
> > [1] https://github.com/google/sanitizers/issues/934
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  meson.build         | 3 +++
> >  test/py/meson.build | 6 ++++++
> >  2 files changed, 9 insertions(+)
> > 
> > diff --git a/meson.build b/meson.build
> > index 63e45465da9c..e3052260f13e 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -112,6 +112,8 @@ common_arguments = [
> >  c_arguments = []
> >  cpp_arguments = []
> >  
> > +cxx_stdlib = 'libstdc++'
> > +
> >  if cc.get_id() == 'clang'
> >      if cc.version().version_compare('<9')
> >          error('clang version is too old, libcamera requires 9.0 or newer')
> > @@ -137,6 +139,7 @@ if cc.get_id() == 'clang'
> >          cpp_arguments += [
> >              '-stdlib=libc++',
> >          ]
> > +        cxx_stdlib = 'libc++'
> >      endif
> >  
> >      cpp_arguments += [
> > diff --git a/test/py/meson.build b/test/py/meson.build
> > index 481bb481c110..b922e8578c29 100644
> > --- a/test/py/meson.build
> > +++ b/test/py/meson.build
> > @@ -24,6 +24,12 @@ py_env.append('PYTHONPATH', pypathdir)
> >  if asan_enabled
> >      py_env.append('LD_PRELOAD', asan_runtime)
> >  
> > +    # Preload the C++ standard library to work around a bug in ASan when
> > +    # dynamically loading C++ .so modules.
> > +    stdlib = run_command(cxx, '-print-file-name=' + cxx_stdlib + '.so',
> > +                         check : true).stdout().strip()
> > +    py_env.append('LD_PRELOAD', stdlib)
> > +
> 
> I assume it is, but have never used LD_PRELOAD this way - is it safe to
> have multiple instances of the environment variable?
> 
> 
> Does this make a space separated list? Or does it make
> LD_PRELOAD=file1.so LD_PRELOAD=file2.so appear in the env?
> 
> I presume whatever it does it works as you'll have used it ...

environment.append() appends a new value to an existing environment
variable, with a separator. The separator defaults to ':' on Linux. It
can be overridden with an optional argument to environment.append() if
needed, but in this case ':' is what we want.

> so presumptively:
> 
> 
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> But I'm interested to know which form is used by meson when appending
> the same variable to an environment for future use cases.
> 
> >      # Disable leak detection as the Python interpreter is full of leaks.
> >      py_env.append('ASAN_OPTIONS', 'detect_leaks=0')
> >  endif

Patch
diff mbox series

diff --git a/meson.build b/meson.build
index 63e45465da9c..e3052260f13e 100644
--- a/meson.build
+++ b/meson.build
@@ -112,6 +112,8 @@  common_arguments = [
 c_arguments = []
 cpp_arguments = []
 
+cxx_stdlib = 'libstdc++'
+
 if cc.get_id() == 'clang'
     if cc.version().version_compare('<9')
         error('clang version is too old, libcamera requires 9.0 or newer')
@@ -137,6 +139,7 @@  if cc.get_id() == 'clang'
         cpp_arguments += [
             '-stdlib=libc++',
         ]
+        cxx_stdlib = 'libc++'
     endif
 
     cpp_arguments += [
diff --git a/test/py/meson.build b/test/py/meson.build
index 481bb481c110..b922e8578c29 100644
--- a/test/py/meson.build
+++ b/test/py/meson.build
@@ -24,6 +24,12 @@  py_env.append('PYTHONPATH', pypathdir)
 if asan_enabled
     py_env.append('LD_PRELOAD', asan_runtime)
 
+    # Preload the C++ standard library to work around a bug in ASan when
+    # dynamically loading C++ .so modules.
+    stdlib = run_command(cxx, '-print-file-name=' + cxx_stdlib + '.so',
+                         check : true).stdout().strip()
+    py_env.append('LD_PRELOAD', stdlib)
+
     # Disable leak detection as the Python interpreter is full of leaks.
     py_env.append('ASAN_OPTIONS', 'detect_leaks=0')
 endif