Bug 218607 - psx_syscall6() doesn't work on C++
Summary: psx_syscall6() doesn't work on C++
Status: NEW
Alias: None
Product: Tools
Classification: Unclassified
Component: libcap (show other bugs)
Hardware: All Linux
: P3 enhancement
Assignee: Tools/Libcap default virtual assignee
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-03-16 03:09 UTC by vini.ipsmaker
Modified: 2024-03-24 05:52 UTC (History)
1 user (show)

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description vini.ipsmaker 2024-03-16 03:09:53 UTC
I have zero experience with linker scripts, so the problem could be me, but after trying for so many weeks I finally decided to just open a bug asking for help. Maybe it's a libpsx bug after all.

Below you may find the full source code for a C++11 program that creates an extra thread and later tries to change the no-new-privs bit for the extra thread using psx_syscall6(). The threads will stay alive for 15min. I use this time to examine /proc files on the system and every time I verified that only one thread has the no-privs-bit set. I don't understand why libpsx is failing here.

#include <iostream>
#include <thread>
#include <sys/psx_syscall.h>

#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>

int main()
{
  std::thread t{[]() {
    sleep(60 * 15);
  }};
  psx_syscall6(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0);
  sleep(60 * 15);
  t.join();
}

I'm compiling this program with the following command:

g++ $(pkg-config --libs libpsx) t.cpp

I also tried to convert the C++ program to a C program using pthread_create() manually, and it didn't work (one of the threads will just die indicating that the sighandler for libpsx isn't installed?). I really don't understand how one is supposed to use libpsx.
Comment 1 Andrew G. Morgan 2024-03-16 17:04:15 UTC
libpsx is a wrapper for thread creation and management that uses -lpthread.

On the face of it your program makes no reference to `pthread_create` in the output of `objdump -x a.out`. The `libpsx` code expects the linking to wrap these function calls with some accounting code that keeps track of POSIX threads.

```
$ pkg-config --libs libpsx
-lpsx -lpthread -Wl,-wrap,pthread_create
```

I'm not familiar with how c++ `<thread>` is implemented. Does it even use `pthread_create` on Linux?

Running `ltrace a.out` on your program does not suggest that the `$(pkg-config --libs libpsx)` linking options are attaching themselves to anything. You should see something like: `__wrap_pthread_create` in the output at thread creation time.
Comment 2 vini.ipsmaker 2024-03-16 19:22:11 UTC
> Running `ltrace a.out` on your program does not suggest that the
> `$(pkg-config --libs libpsx)` linking options are attaching themselves to
> anything.

That's the function it's calling here according to ltrace: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)())

Should we also have a libpsx++ then? I suppose it'd wrap the above function and use the same sighandling mechanism from libpsx?

> I also tried to convert the C++ program to a C program using pthread_create()
> manually, and it didn't work (one of the threads will just die indicating
> that the sighandler for libpsx isn't installed?). I really don't understand
> how one is supposed to use libpsx.

I finally found out what was wrong with my C version of the same program. You can ignore this part of my initial report.

It seems the only remaining problem now is how one is supposed to use libpsx in C++ programs. Should we have a libpsx++ library that C++ programs link against?
Comment 3 Andrew G. Morgan 2024-03-16 21:07:07 UTC
Possibly, but there is a lot of name mangling going on, so I'm not sure.

Ideally, if it can be made to work, we would provide both wrapper calls in the same library and not need a separate library build.
Comment 4 vini.ipsmaker 2024-03-16 21:13:04 UTC
> a lot of name mangling going on

libc++ (LLVM) might be using a different function to wrap as well, so maybe there's yet another function to wrap later. Unfortunately I don't have a handy libc++-based env to test right now. However having just libstdc++ should be good enough for a start anyway as that's the most common ABI one founds in Linux systems by a large margin.
Comment 5 Andrew G. Morgan 2024-03-16 22:12:33 UTC
This isn't very promising:

https://stackoverflow.com/a/8405029/14760867
Comment 6 Andrew G. Morgan 2024-03-16 22:28:35 UTC
I wonder if a potential workaround more natural to C++ might be to use inheritance to provide, say, a `psx::thread` object, and substitute wrapper methods for creation? It is not clear to me how useful this would be since the wrapping would only apply to code that used `psx::thread`, but it might be workable for some projects.
Comment 7 vini.ipsmaker 2024-03-16 23:39:46 UTC
> This isn't very promising [...]

Why isn't <https://stackoverflow.com/a/8405029/14760867> promising? Doesn't wrapping the mangled name look good enough? I think that should be the way to go.

The real problem should actually come later, adding a C++ compilation unit so you can access std::thread::native_handle() to do the bookkeeping for the new thread.

If you do decide to add a C++ compilation unit to libpsx, you complicate the build for libpsx by adding a new dependency (a C++ compiler).

If you decide to add the C++ compilation unit in a different library, then you complicate orchestration between the new library and libpsx (libcap using libpsx should work on C++ projects that link against the new library as well... and C libraries in the C++ project calling pthread_create() directly should also work here).

> I wonder if a potential workaround more natural to C++ might be to use
> inheritance to provide, say, a `psx::thread` object, and substitute wrapper
> methods for creation? It is not clear to me how useful this would be since
> the wrapping would only apply to code that used `psx::thread`, but it might
> be workable for some projects.

This wouldn't work for my C++ project. I don't control 3rd party libraries that might create threads. And I do need to use std::thread so functions such as std::notify_all_at_thread_exit() work correctly. std::*_at_thread_exit() functions trigger some hardcoded libstdc++ functions that are guaranteed to execute only after thread-local variables are destroyed.
Comment 8 Andrew G. Morgan 2024-03-17 00:12:08 UTC
How many mangled names would we have to wrap though? Is the mangling even consistent for single compiler, libc++ variant?
Comment 9 vini.ipsmaker 2024-03-17 00:21:05 UTC
> How many mangled names would we have to wrap though? Is the mangling even
> consistent for single compiler, libc++ variant?

C++ mangling is very stable. The developers for libstdc++ and libc++ also take ABI stability very seriously.

It'd require mangling for two functions in total:

* libstdc++ thread constructor
* libc++ thread constructor

However I suggest to start with just libstdc++ to gather experience. It can expand to support libc++ in later releases.
Comment 10 Andrew G. Morgan 2024-03-24 05:52:45 UTC
So I took a look at this (try.cc):

////
#include <iostream>
#include <thread>
#include <sys/psx_syscall.h>

#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>

extern "C" void __wrap__ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE() {
    std::cout << "oh no\n";
    exit(1);
}

int main()
{
  std::thread t{[]() {
      int x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0);
      sleep(2);
      int y = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0);
      std::cout << "thread was: " << x << " is: " << y << "\n";
  }};
  int x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0);
  sleep(1);
  psx_syscall6(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0);
  int y = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0);
  std::cout << "main was: " << x << " is: " << y << "\n";
  t.join();
}
////

Compiled with:

$ g++ try.cc $(pkg-config --libs libpsx) -Wl,-wrap,_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE
$ ./a.out
oh no

Which does imply we can intercept the thread constructor. I just am not yet sure how to chain to the real version.

Note You need to log in before you can comment on or make changes to this bug.