Eliminating -wrap use.

This addresses the following bug:

   https://bugzilla.kernel.org/show_bug.cgi?id=219456

insofar as it eliminates the need for -wrap=pthread_create
linkage. Mostly, code that uses -lpsx functions can simply
link with -lpsx now. However, for legacy reasons the library
still works when linked wrapped or with the new content of
the libpsx.pc file:

   -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed -lpthread

These last options are required for getting -lcap to act at a
consistent process level and not a thread level.

Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
This commit is contained in:
Andrew G. Morgan 2024-11-09 21:33:33 -08:00
parent 1474f5b96e
commit b017fcff26
10 changed files with 95 additions and 66 deletions

View File

@ -67,7 +67,7 @@ SYSTEM_HEADERS = /usr/include
SUDO := sudo
CC := $(CROSS_COMPILE)gcc
LD := $(CC) -Wl,-x -shared
LD := $(CC) -Wl,-x -shared -Wl,-shared
AR := $(CROSS_COMPILE)ar
RANLIB := $(CROSS_COMPILE)ranlib
OBJCOPY := $(CROSS_COMPILE)objcopy
@ -107,8 +107,8 @@ endif
USE_GPERF ?= $(shell which gperf >/dev/null 2>/dev/null && echo yes)
LIBCAPLIB := -L$(topdir)/libcap -lcap
PSXLINKFLAGS := -lpthread -Wl,-wrap,pthread_create
LIBPSXLIB := -L$(topdir)/libcap -lpsx $(PSXLINKFLAGS)
PSXLINKFLAGS := -lpthread
LIBPSXLIB := -L$(topdir)/libcap -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed $(PSXLINKFLAGS)
INCS=$(topdir)/libcap/include/sys/capability.h
INDENT := $(shell if [ -n "$$(which indent 2>/dev/null)" ]; then echo "| indent -kr" ; fi)
@ -142,12 +142,6 @@ GOROOT ?= $(shell $(GO) env GOROOT)
GOCGO ?= $(shell if [ "$(shell $(GO) env CGO_ENABLED)" = 1 ]; then echo yes ; else echo no ; fi)
GOOSARCH ?= $(shell $(GO) env GOHOSTOS)_$(shell $(GO) env GOHOSTARCH)
CGO_REQUIRED := $(shell $(topdir)/go/cgo-required.sh $(GO))
ifeq ($(CGO_REQUIRED),1)
# Strictly speaking go1.15 doesn't need this, but 1.16 is when the
# real golang support arrives for non-cgo support, so drop the last
# vestige of legacy workarounds then.
CGO_LDFLAGS_ALLOW := CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*"
endif
CGO_CFLAGS := $(LIBCAP_INCLUDES)
CGO_LDFLAGS := -L$(topdir)/libcap
GO_BUILD_FLAGS :=

View File

@ -9,7 +9,7 @@ test: all
# This binary will only run from this location because we use an rpath
# to find the in-tree build of libpsx.so.
threadcpp: thread.cpp ../../libcap/libpsx.so
g++ -I../../libcap -o $@ $< -Wl,-rpath,../../libcap -Wl,-wrap,pthread_create -lpsx -lpthread
g++ -I../../libcap -o $@ $< -Wl,-rpath,../../libcap -lpsx -lpthread
../../libcap/libpsx.so:
$(MAKE) -C ../../libcap libpsx.so

View File

@ -1,7 +1,7 @@
// Program explore is evolved from the code discussed in more depth
// here:
//
// https://github.com/golang/go/issues/3405
// https://github.com/golang/go/issues/3405
//
// The code here demonstrates that while PR_SET_NO_NEW_PRIVS only
// applies to the calling thread, since
@ -12,14 +12,15 @@
// Based on the command line options, we can manipulate the program to
// behave in various ways. Example command lines:
//
// sudo ./explore
// sudo ./explore --kill=false
// sudo ./explore --kill=false --errno=0
// sudo ./explore
// sudo ./explore --kill=false
// sudo ./explore --kill=false --errno=0
//
// Supported Go toolchains are after go1.10. Those prior to go1.15
// require this environment variable to be set to build successfully:
// Supported Go toolchains are after go1.10. Those prior to go1.16
// are not fully reliable because of a go + glibc/psx incompatibility.
// Details:
//
// export CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*"
// https://bugzilla.kernel.org/show_bug.cgi?id=219478
//
// Go toolchains go1.16+ can be compiled CGO_ENABLED=0 too,
// demonstrating native nocgo support for seccomp features.
@ -161,6 +162,7 @@ func allGood() []SockFilter {
// prctl executes the prctl - unless the --psx commandline argument is
// used, this is on a single thread.
//
//go:uintptrescapes
func prctl(option, arg1, arg2, arg3, arg4, arg5 uintptr) error {
var e syscall.Errno
@ -180,6 +182,7 @@ func prctl(option, arg1, arg2, arg3, arg4, arg5 uintptr) error {
}
// SeccompSetModeFilter is our wrapper for performing our seccomp system call.
//
//go:uintptrescapes
func SeccompSetModeFilter(prog *SockFProg) error {
if _, _, e := syscall.RawSyscall(sysSeccomp, seccompSetModeFilter, seccompFilterFlagTsync, uintptr(unsafe.Pointer(prog))); e != 0 {

View File

@ -1,4 +1,4 @@
.TH CAP_GET_PROC 3 "2022-04-28" "" "Linux Programmer's Manual"
.TH CAP_GET_PROC 3 "2024-11-09" "" "Linux Programmer's Manual"
.SH NAME
cap_get_proc, cap_set_proc, capgetp, cap_get_bound, cap_drop_bound, \
cap_get_ambient, cap_set_ambient, cap_reset_ambient, \
@ -257,11 +257,11 @@ is packaged with a separate POSIX semantics system call library:
If your program uses POSIX threads, to achieve meaningful POSIX
semantics capability manipulation, you should link your program with:
.sp
.B ld ... \-lcap \-lpsx \-lpthread \-\-wrap=pthread_create
.B ld ... \-lcap $(pkg-config \-\-libs \-\-cflags libpsx)
.sp
or,
.sp
.B gcc ... \-lcap \-lpsx \-lpthread \-Wl,\-wrap,pthread_create
.B gcc ... \-lcap $(pkg-config \-\-libs \-\-cflags libpsx)
.sp
When linked this way, due to linker magic, libcap uses
.BR psx_syscall "(3) and " psx_syscall6 (3)

View File

@ -1,4 +1,4 @@
.TH LIBPSX 3 "2021-12-12" "" "Linux Programmer's Manual"
.TH LIBPSX 3 "2024-11-09" "" "Linux Programmer's Manual"
.SH NAME
psx_syscall3, psx_syscall6, psx_set_sensitivity \- POSIX semantics for system calls
.SH SYNOPSIS
@ -18,11 +18,23 @@ void psx_load_syscalls(long int (**syscall_fn)(long int,
long int, long int, long int));
.fi
.sp
Link with one of these:
Any code that uses one of the above functions can be linked as follows:
.sp
.I ld ... \-lpsx \-lpthread \-\-wrap=pthread_create
.I ld ... \-lpsx \-lpthread
.sp
.I gcc ... \-lpsx \-lpthread \-Wl,\-wrap,pthread_create
.I gcc ... \-lpsx \-lpthread
.sp
Note, special flags are needed to get
.B -lcap
to operated with process wide capabilities when linked with
.BR -lpsx .
Namely, use the
.B pkg-config
option file:
.B gcc ... \-lcap $(pkg-config \-\-libs \-\-cflags libpsx)
More details are available in the
.BR cap_get_proc (3)
man page.
.SH DESCRIPTION
The
.B libpsx
@ -51,14 +63,6 @@ but acts as a pass-through for other
.B SIGSYS
uses.
.PP
A linker trick of
.I wrapping
the
.BR pthread_create ()
call with a psx thread registration function is used to ensure
.B libpsx
can keep track of all pthreads.
.PP
An inefficient macrology trick supports the
.BR psx_syscall ()
pseudo function which takes 1 to 7 arguments, depending on the needs
@ -116,8 +120,14 @@ The needs of
.BR libcap (3)
for POSIX semantics of capability manipulation. You can read more
about why this is needed here:
.TP
.sp
https://sites.google.com/site/fullycapable/who-ordered-libpsx
.sp
Versions of
.B libpsx
prior to 2.72 only supported pthreads. Since libpsx-2.72 the library
works with all Linux thread implementations as it operates at the
lowest level of thread abstraction, LWPs.
.SH "REPORTING BUGS"
The
.B libpsx
@ -129,5 +139,6 @@ via:
https://bugzilla.kernel.org/buglist.cgi?component=libcap&list_id=1090757
.SH SEE ALSO
.BR libcap (3),
.BR cap_get_proc (3),
.BR pthreads "(7) and"
.BR nptl (7).

View File

@ -57,10 +57,10 @@ CAPGOPACKAGE: vendor/$(IMPORTDIR)/cap ../cap/*.go good-names.go $(PSXGOPACKAGE)
# Compiles something with this package to compare it to libcap. This
# tests more when run under sudotest (see ../progs/quicktest.sh for that).
compare-cap: compare-cap.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="1" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
web: ../goapps/web/web.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
ifeq ($(RAISE_GO_FILECAP),yes)
$(MAKE) -C ../progs setcap
$(SUDO) ../progs/setcap cap_setpcap,cap_net_bind_service=p web
@ -68,24 +68,24 @@ ifeq ($(RAISE_GO_FILECAP),yes)
endif
setid: ../goapps/setid/setid.go CAPGOPACKAGE PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
gowns: ../goapps/gowns/gowns.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
captree: ../goapps/captree/captree.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
captrace: ../goapps/captrace/captrace.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
ok: ok.go vendor/modules.txt
CC="$(CC)" CGO_ENABLED="0" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
try-launching: try-launching.go CAPGOPACKAGE ok
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
ifeq ($(CGO_REQUIRED),0)
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@-cgo $<
CC="$(CC)" CGO_ENABLED="1" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@-cgo $<
endif
# This is a test case developed from the deadlock investigation,
@ -96,46 +96,46 @@ endif
# the large change was not backported. (See noted bug for a much
# smaller patch for this issue on those older releases.)
psx-fd: psx-fd.go PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
ifeq ($(CGO_REQUIRED),0)
psx-fd-cgo: psx-fd.go PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="1" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
endif
psx-signals: psx-signals.go PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
ifeq ($(CGO_REQUIRED),0)
psx-signals-cgo: psx-signals.go PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="1" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
endif
b210613: b210613.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
b215283: b215283.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
ifeq ($(CGO_REQUIRED),0)
b215283-cgo: b215283.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="1" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
endif
mismatch: mismatch.go PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
iaber: iaber.go CAPGOPACKAGE PSXGOPACKAGE
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
ifeq ($(CGO_REQUIRED),0)
mismatch-cgo: mismatch.go CAPGOPACKAGE
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
CC="$(CC)" CGO_ENABLED="1" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
endif
test: setid gowns captree psx-fd $(TESTS)
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
LD_LIBRARY_PATH=../libcap ./compare-cap
./psx-signals
./mismatch || exit 0 ; exit 1

View File

@ -4,8 +4,8 @@ libdir=@libdir@
includedir=@includedir@
Name: libpsx
Description: libpsx - linux posix syscall API for pthreads
Description: libpsx - linux posix syscall API for threads
Version: @VERSION@
Libs: -L${libdir} -lpsx -lpthread -Wl,-wrap,pthread_create
Libs: -L${libdir} -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed -lpthread
Libs.private: @deps@
Cflags: -I${includedir}

View File

@ -659,11 +659,6 @@ int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
* or not you use this wrapper to reach the __real_ functionality or
* not isn't important to the psx mechanism any longer (since
* libpsx-2.72).
*
* I've explored relaxing the constraint, but it's not yet clear how
* to force -lpsx to load when -lcap provides a weak
* psx_load_syscalls() function. So, for now, that is the reason we
* still require the --wrap=pthread_create linker argument.
*/
int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) {

View File

@ -1,12 +1,32 @@
/*
* Copyright (c) 2019 Andrew G. Morgan <morgan@kernel.org>
* Copyright (c) 2019,24 Andrew G. Morgan <morgan@kernel.org>
*
* This header, and the -lpsx library, provide a number of things to
* support POSIX semantics for syscalls associated with the pthread
* library. Linking this code is tricky and is done as follows:
* library. For any code that references the psx_syscall{3,6}
* functions, linking this code is as follows:
*
* ld ... -lpsx -lpthread --wrap=pthread_create
* or, gcc ... -lpsx -lpthread -Wl,-wrap,pthread_create
* ld ... -lpsx -lpthread
* or,
* gcc ... -lpsx -lpthread
*
* NOTE: linking with -lcap to make it silently aware of the psx
* mechanism is tricky. You have to envelope the -lpsx part with
* the following linker flags:
*
* ld ... --no-as-needed --whole-archive -lpsx --no-whole-archive --as-needed -lpthread
* or,
* gcc ... -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed -lpthread
*
* These options are provided in the
* https://en.wikipedia.org/wiki/Pkg-config libpsx.pc file
* distributed with the library.
*
* FYI Earlier versions of libpsx relied on gcc
* ... -Wl,--wrap=pthread_create linkage in all cases, but since
* libpsx-2.72 the library can work with non pthread threads (LWP)
* under Linux and such wrapping is no longer needed. That being
* said, for compatibility reasons such linking is still supported.
*
* glibc provides a subset of this functionality natively through the
* nptl:setxid mechanism and could implement psx_syscall() directly

View File

@ -5,12 +5,18 @@
extern int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
extern int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*
* psx requires this function to be provided by the linkage wrapping.
*/
int __attribute__((weak))
__real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) {
fprintf(stderr, "libpsx is not linked correctly\n");
exit(1);
if (&pthread_create == &__wrap_pthread_create) {
fprintf(stderr, "libpsx is not linked correctly\n");
exit(1);
}
return pthread_create(thread, attr, start_routine, arg);
}