Avoid thread churn deadlock with PSX rewrite.

This bug, reported by David Runge, identifies another regression
vs. libpsx-2.71:

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

A regression of the LWP threading support reintroduced an old bug
in the libpsx-2.72 rewrite, specifically this bug:

   https://github.com/golang/go/issues/42494

This present change includes x86_64, i386 and arm* (32-bit and 64-bit)
support. I've tested each of these with debian OSes, some running via QEMU.
Before releasing 2.74, I plan to extend the testing to all of the targets
supported by Debian (see https://wiki.debian.org/DebianInstaller/Qemu).

Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
This commit is contained in:
Andrew G. Morgan 2025-02-01 08:49:27 -08:00
parent 2dda39e0e3
commit 025f28ca4f
9 changed files with 407 additions and 172 deletions

View File

@ -139,6 +139,10 @@ endif
test: vet setid gowns captree psx-fd $(TESTS)
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
ifeq ($(CGO_REQUIRED),0)
CC="$(CC)" CGO_ENABLED="1" $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
CC="$(CC)" CGO_ENABLED="1" $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
endif
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
LD_LIBRARY_PATH=../libcap ./compare-cap
./psx-signals

View File

@ -48,7 +48,7 @@ func tryFileCaps() {
// Failing attempt to remove capabilities.
var empty *cap.Set
if err := empty.SetFile(os.Args[0]); err != syscall.EPERM {
log.Fatalf("failed to be blocked from removing filecaps: %v", err)
log.Fatalf("failed to be blocked from removing filecaps (with=%q): %v", cap.GetProc(), err)
}
// The privilege we want (in the case we are root, we need the
@ -96,7 +96,7 @@ func tryFileCaps() {
// Failing attempt to remove capabilities.
if err := empty.SetFd(f); err != syscall.EPERM {
log.Fatalf("failed to be blocked from fremoving filecaps: %v", err)
log.Fatalf("failed to be blocked from removing filecaps (w/ %q): %v", cap.GetProc(), err)
}
// For the next section, we won't set the effective bit on the file.

View File

@ -15,7 +15,7 @@ STAPSXLIBNAME=$(PSXTITLE).a
CAPFILES=cap_alloc cap_proc cap_extint cap_flag cap_text cap_file cap_syscalls
CAPMAGICOBJ=cap_magic.o
PSXFILES=../psx/psx ../psx/wrap/psx_wrap
PSXFILES=../psx/psx ../psx/psx_calls ../psx/wrap/psx_wrap
PSXMAGICOBJ=psx_magic.o
# Always build libcap sources this way:

91
psx/libpsx.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef LIBPSX_H
#define LIBPSX_H
/*
* Copyright (c) 2025 Andrew G. Morgan <morgan@kernel.org>
*
* These header definitions are private to the PSX mechanism. External
* API references are to be found in <sys/psx_syscall.h>
*
* Since we no longer (libcap-2.72) operate at the pthreads
* abstraction, we need our own mutex etc implementation.
*/
typedef unsigned char psx_mutex_t;
#define _psx_mu_blocked(x) \
__atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST)
#define _psx_mu_lock(x) \
while (_psx_mu_blocked(x)) sched_yield()
#define _psx_mu_unlock(x) \
__atomic_clear((void *)(x), __ATOMIC_SEQ_CST)
#define _psx_mu_unlock_return(x, y) \
do { _psx_mu_unlock(x); return (y); } while (0)
#define _psx_mu_cond_wait(x) \
do { \
_psx_mu_unlock(x); \
sched_yield(); \
_psx_mu_lock(x); \
} while (0)
/* Not reliably defined by *libc so, alias the direct syscall. */
#define _psx_gettid() syscall(SYS_gettid)
extern void psx_lock(void);
extern void psx_unlock(void);
extern void psx_cond_wait(void);
extern long psx_mix(long value);
typedef enum {
_PSX_IDLE = 0,
_PSX_SETUP = 1,
_PSX_SYSCALL = 2,
_PSX_EXITING = 3,
} psx_tracker_state_t;
/*
* Tracking threads is done via a hash map of these objects.
*/
typedef struct psx_thread_ref_s {
long sweep;
long pending;
long tid;
long retval;
} psx_thread_ref_t;
/*
* This global structure holds the global coordination state for
* libcap's psx_syscall() support.
*/
typedef struct {
long pid;
char *pid_path;
psx_mutex_t state_mu;
psx_tracker_state_t state;
int initialized;
int incomplete;
int psx_sig;
int force_failure; /* leave this as zero to avoid forcing a crash */
psx_sensitivity_t sensitivity;
struct {
long syscall_nr;
long arg1, arg2, arg3, arg4, arg5, arg6;
int six;
int active;
} cmd;
/* This is kept opaque here, but its details are known to psx_calls.c */
void *actions;
int map_entries;
long map_mask;
psx_thread_ref_t *map;
} psx_tracker_t;
/* defined in psx_calls.c */
extern psx_tracker_t psx_tracker;
extern int psx_actions_size(void);
extern void psx_confirm_sigaction(void);
#endif /* def LIBPSX_H */

213
psx/psx.c
View File

@ -17,7 +17,6 @@
#include <errno.h>
#include <fcntl.h>
#include <pthread.h> /* pthread_atfork() */
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
@ -29,10 +28,8 @@
#include <sys/types.h>
#include <unistd.h>
/* Not reliably defined by *libc so, alias the direct syscall. */
#define _psx_gettid() syscall(SYS_gettid)
#include "psx_syscall.h"
#include "../psx/libpsx.h"
#ifdef _PSX_DEBUG_MEMORY
@ -45,7 +42,8 @@ static void *_psx_calloc(const char *file, const int line,
}
static void _psx_free(const char *file, const int line, void *ptr) {
fprintf(stderr, "psx:%d:%s:%d: free(%p)\n", _psx_gettid(), file, line, ptr);
fprintf(stderr, "psx:%d:%s:%d: free(%p)\n",
_psx_gettid(), file, line, ptr);
return free(ptr);
}
@ -73,75 +71,12 @@ void psx_load_syscalls(long int (**syscall_fn)(long int,
}
/*
* Since we no longer (libcap-2.72) operate at the pthreads
* abstraction, we need our own mutex etc implementation.
* This global coordinates the PSX mechanism.
*/
typedef uint8_t psx_mutex_t;
#define _psx_mu_blocked(x) \
__atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST)
#define _psx_mu_lock(x) \
while (_psx_mu_blocked(x)) sched_yield()
#define _psx_mu_unlock(x) \
__atomic_clear((void *)(x), __ATOMIC_SEQ_CST)
#define _psx_mu_unlock_return(x, y) \
do { _psx_mu_unlock(x); return (y); } while (0)
#define _psx_mu_cond_wait(x) \
do { \
_psx_mu_unlock(x); \
sched_yield(); \
_psx_mu_lock(x); \
} while (0)
typedef enum {
_PSX_IDLE = 0,
_PSX_SETUP = 1,
_PSX_SYSCALL = 2,
_PSX_EXITING = 3,
} psx_tracker_state_t;
/*
* Tracking threads is done via a hash map of these objects.
*/
typedef struct psx_thread_ref_s {
long sweep;
long pending;
long tid;
long retval;
} psx_thread_ref_t;
/*
* This global structure holds the global coordination state for
* libcap's psx_syscall() support.
*/
static struct psx_tracker_s {
long pid;
char *pid_path;
psx_mutex_t state_mu;
psx_tracker_state_t state;
int initialized;
int incomplete;
int psx_sig;
psx_sensitivity_t sensitivity;
struct {
long syscall_nr;
long arg1, arg2, arg3, arg4, arg5, arg6;
int six;
int active;
} cmd;
struct sigaction sig_action;
struct sigaction chained_action;
int map_entries;
long map_mask;
psx_thread_ref_t *map;
} psx_tracker;
__attribute__((visibility ("hidden"))) psx_tracker_t psx_tracker;
/* psx_mix is our trivial hash mixing for the thread reference map */
static long psx_mix(long value) {
__attribute__((visibility ("hidden"))) long psx_mix(long value) {
return value ^ (value >> 7) ^ (value >> 13) ^ (value >> 23);
}
@ -168,7 +103,7 @@ static void _psx_proc_start(void)
long pid = getpid();
psx_tracker.pid = pid;
if (psx_tracker.pid_path == NULL) {
psx_tracker.pid_path = malloc(3*sizeof(pid) + 13 /* strlen(taskdir_fmt) */);
psx_tracker.pid_path = calloc(1, 3*sizeof(pid) + sizeof(taskdir_fmt));
}
sprintf(psx_tracker.pid_path, taskdir_fmt, pid);
psx_tracker.state = _PSX_IDLE;
@ -194,11 +129,22 @@ static void psx_syscall_start(void)
*
* https://bugzilla.kernel.org/show_bug.cgi?id=210533
*
* Our current strategy is to aggressively intercept SIGSYS,
* We tried SYGSYS until
*
* https://bugzilla.kernel.org/show_bug.cgi?id=219687
*
* Our current strategy is to aggressively intercept SIGNO=33,
* something that is confirmed to be the case each time _PSX_SETUP
* state is entered.
* state is entered. Note, this signal is special and hidden by
* glibc and musl, but we inject our use of it via raw system
* calls and quietly cooperate with those library usages. Go
* treats this signal specially (avoiding blocking it for extended
* periods) because of its hidden glibc usage as discussed here:
*
* https://github.com/golang/go/issues/42494
*/
psx_tracker.psx_sig = SIGSYS;
psx_tracker.psx_sig = 33;
psx_tracker.actions = calloc(2, psx_actions_size());
psx_set_map(256);
atexit(_psx_cleanup);
pthread_atfork(NULL, NULL, _psx_new_proc);
@ -210,7 +156,7 @@ static void psx_syscall_start(void)
* to be confused with psx_sig (interrupt) blocking - which is
* performed when the signal handler is being confirmed.
*/
static void psx_lock(void)
__attribute__((visibility ("hidden"))) void psx_lock(void)
{
_psx_mu_lock(&psx_tracker.state_mu);
if (!psx_tracker.initialized) {
@ -222,7 +168,7 @@ static void psx_lock(void)
/*
* This is the only way this library unlocks.
*/
static void psx_unlock(void)
__attribute__((visibility ("hidden"))) void psx_unlock(void)
{
_psx_mu_unlock(&psx_tracker.state_mu);
}
@ -232,7 +178,7 @@ static void psx_unlock(void)
* other code to run that may require the lock. This is the only way
* the psx code waits like this.
*/
static void psx_cond_wait(void)
__attribute__((visibility ("hidden"))) void psx_cond_wait(void)
{
_psx_mu_cond_wait(&psx_tracker.state_mu);
}
@ -251,96 +197,12 @@ static void _psx_cleanup(void) {
psx_cond_wait();
}
psx_tracker.state = _PSX_EXITING;
free(psx_tracker.actions);
free(psx_tracker.map);
free(psx_tracker.pid_path);
psx_unlock();
}
/*
* psx_posix_syscall_actor performs the system call on the targeted
* thread and signals it is no longer pending.
*/
static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
/* bail early to the next in the chain if not something we recognize */
if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
info == NULL || info->si_code != SI_TKILL ||
info->si_pid != psx_tracker.pid) {
if (psx_tracker.chained_action.sa_sigaction != 0) {
psx_tracker.chained_action.sa_sigaction(signum, info, ignore);
}
return;
}
long int retval;
if (!psx_tracker.cmd.six) {
retval = syscall(psx_tracker.cmd.syscall_nr,
psx_tracker.cmd.arg1,
psx_tracker.cmd.arg2,
psx_tracker.cmd.arg3);
} else {
retval = syscall(psx_tracker.cmd.syscall_nr,
psx_tracker.cmd.arg1,
psx_tracker.cmd.arg2,
psx_tracker.cmd.arg3,
psx_tracker.cmd.arg4,
psx_tracker.cmd.arg5,
psx_tracker.cmd.arg6);
}
/*
* communicate the result of the thread's attempt to perform the
* syscall.
*/
long tid = _psx_gettid();
psx_lock();
psx_thread_ref_t *ref = &psx_tracker.map[psx_mix(tid) & psx_tracker.map_mask];
ref->retval = retval;
ref->pending = 0;
/*
* Block this thread until all threads have been interrupted.
* This prevents threads clone()ing after running the syscall and
* confusing the psx mechanism into thinking they need to also run
* the syscall. They wouldn't need to run it, because they would
* inherit the thread state of a syscall that has already
* happened. However, figuring that out for an unblocked thread is
* hard, so we prevent it from happening.
*/
while (psx_tracker.cmd.active) {
psx_cond_wait();
}
psx_tracker.incomplete--;
psx_unlock();
}
/*
* psx_confirm_sigaction (re)confirms that the psx handler is the
* first handler to respond to the psx signal. It assumes that
* psx_tracker.psx_sig has been set.
*/
static void psx_confirm_sigaction(void) {
sigset_t mask, orig;
struct sigaction existing_sa;
/*
* Block interrupts while potentially rewriting the handler.
*/
sigemptyset(&mask);
sigaddset(&mask, psx_tracker.psx_sig);
sigprocmask(SIG_BLOCK, &mask, &orig);
sigaction(psx_tracker.psx_sig, NULL, &existing_sa);
if (existing_sa.sa_sigaction != psx_posix_syscall_actor) {
memcpy(&psx_tracker.chained_action, &existing_sa, sizeof(struct sigaction));
psx_tracker.sig_action.sa_sigaction = psx_posix_syscall_actor;
sigemptyset(&psx_tracker.sig_action.sa_mask);
psx_tracker.sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
sigaction(psx_tracker.psx_sig, &psx_tracker.sig_action, NULL);
}
sigprocmask(SIG_SETMASK, &orig, NULL);
}
/*
* under lock perform a state transition. Changing state is generally
* done via this function. However, there is a single exception in
@ -466,7 +328,6 @@ long int __psx_syscall(long int syscall_nr, ...) {
int restore_errno = errno;
psx_new_state(_PSX_SETUP, _PSX_SYSCALL);
psx_tracker.cmd.active = 1;
/*
* cleaning up before we start helps a fork()ed child not inherit
@ -548,9 +409,25 @@ long int __psx_syscall(long int syscall_nr, ...) {
free(old);
x = &psx_tracker.map[mix & psx_tracker.map_mask];
}
/* a new entry */
/*
* A new entry - this is where we will also
* (first) enable the PSX parts of our installed
* handler. This is, potentially racing with other
* users of the same signal, so we do this under
* lock.
*/
psx_lock();
x->pending = 1;
x->tid = tid;
psx_tracker.cmd.active = 1;
psx_unlock();
/*
* There is a small chance that this signal may be
* racing with another user of this signal.
* Locking above should ensure both forks of the
* handler get invoked - perhaps out of order
* though...
*/
syscall(SYS_tkill, tid, psx_tracker.psx_sig);
}
psx_lock();
@ -645,6 +522,12 @@ int psx_set_sensitivity(psx_sensitivity_t level) {
return 0;
}
/*
* The following is required for legacy linkage libcap-2.71 and
* earlier backward compatibility. The Go use of psx no longer has any
* need for this, and does not provide the define when building this
* code.
*/
#ifdef _LIBPSX_PTHREAD_LINKAGE
/*

258
psx/psx_calls.c Normal file
View File

@ -0,0 +1,258 @@
/*
* Copyright (c) 2025 Andrew G. Morgan <morgan@kernel.org>
*
* This file contains the low level magic to ensure that the PSX
* mechanism is the first to intercept the specified psx_sig signal.
* This is a hidden function of the psx library in its own file
* because its use of kernel headers conflicts badly with the more
* traditional *libc provided headers.
*/
#include <linux/signal.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "psx_syscall.h"
#include "../psx/libpsx.h"
/*
* This is terrible, but some of the OS installed <asm/signal.h>
* headers only seem to know about the legacy 32-bit signal masks. So,
* we need to #define our way around that...
*
* Also, the rt_sig*() system calls use a different sigaction
* definition in these cases, so #define around that too.
*/
#if defined(__arm__) || defined(__i386__) || defined(__aarch64__)
#undef _NSIG
#undef _NSIG_BPW
#undef _NSIG_WORDS
#undef SA_RESTORER
#undef sa_handler
#undef sa_sigaction
#define _NSIG 64
#define _NSIG_BPW (8*sizeof(unsigned long))
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
#if defined(__arm__) || defined(__i386__)
#define SA_RESTORER 0x04000000
#endif /* architectures that use SA_RESTORER */
typedef struct {
unsigned long sig[_NSIG_WORDS];
} psx_sigset_t;
#define sigset_t psx_sigset_t
struct psx_sigaction {
void *sa_handler;
unsigned long sa_flags;
#ifdef SA_RESTORER
void *sa_restorer;
#endif
sigset_t sa_mask;
};
#define sigaction psx_sigaction
#endif /* various architecture defines */
/*
* Local definition of the "sigaction"s for signal handling including
* chaining of signal handlers. The content of this type is not known
* outside of this present source file. The reason for this
* obfuscation is the fact that this present file uses kernel headers
* which may differ in structure definitions from the regular *libc
* implementations with the same name.
*/
typedef struct {
struct sigaction sig_action;
struct sigaction chained_action;
} psx_actions_t;
/*
* Used to allocate space for the actions in the tracker, without
* revealing the content outside this current file.
*/
__attribute__((visibility ("hidden"))) int psx_actions_size(void) {
return sizeof(psx_actions_t);
}
/* int how, sigset_t *nset, sigset_t *oset */
#define _psx_rt_sigprocmask(how, nset, oset) \
syscall(SYS_rt_sigprocmask, how, nset, oset, sizeof(sigset_t))
/* int sig, struct sigaction *act, struct sigaction *oact */
#define _psx_rt_sigaction(sig, act, oact) \
syscall(SYS_rt_sigaction, sig, act, oact, sizeof(sigset_t))
#define _psx_sigemptyset(pmask) \
memset(pmask, 0, sizeof(*(pmask)))
#ifdef _NSIG_WORDS
#define _psx_sigaddset(pmask, signo) \
(pmask)->sig[(signo-1)/_NSIG_BPW] |= 1UL << (((signo-1)%_NSIG_BPW))
#else /* ndef _NSIG_WORDS */
#define _psx_sigaddset(pmask, signo) \
*(pmask) |= 1UL << (signo-1)
#endif /* def _NSIG_WORDS */
#ifdef SA_RESTORER
/*
* Actual assembly code for this "function" is embedded in
* hidden form in psx_posix_syscall_actor() below.
*/
__attribute__((visibility ("hidden"))) void psx_restorer(void);
#endif /* def SA_RESTORER */
/*
* psx_posix_syscall_actor performs the system call on the targeted
* thread and signals it is no longer pending.
*/
static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
/* bail early to the next in the chain if not something we recognize */
psx_lock();
if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
info == NULL || info->si_code != SI_TKILL ||
info->si_pid != psx_tracker.pid) {
psx_actions_t *actions;
void (*chained_actor)(int, siginfo_t *,void *);
psx_unlock();
actions = psx_tracker.actions;
chained_actor = (void *) actions->chained_action.sa_handler;
if (chained_actor != 0) {
chained_actor(signum, info, ignore);
}
#ifdef SA_RESTORER
/*
* This architecture requires we provide a signal exit fixup
* trampoline. We use some assembly to provide this, aliased
* to a function (prototype above). This assembly won't be
* executed as part of the preceding code, but has to live
* somewhere so we hide it here where it will get compiled,
* but not slow down the PSX functionality.
*/
if (psx_tracker.force_failure) {
#if defined(__x86_64__)
asm("\npsx_restorer:\n\tmov $15,%rax\n\tsyscall\n");
#elif defined(__i386__)
asm("\npsx_restorer:\n\tmov $173, %eax\n\tint $0x80\n");
#elif defined(__arm__)
asm("\npsx_restorer:\n\tmov r7,#173\n\tswi 0\n");
#else
#error "unsupported architecture - https://bugzilla.kernel.org/show_bug.cgi?id=219687"
/*
* These are supported by go (go tool dist list | grep linux),
* so we plan to also support them:
* linux/loong64
* linux/mips
* linux/mips64
* linux/mips64le
* linux/mipsle
* linux/ppc64
* linux/ppc64le
* linux/riscv64
* linux/s390x
*/
#endif /* supported architectures */
}
#endif /* def SA_RESTORER */
return;
}
psx_unlock();
long int retval;
if (!psx_tracker.cmd.six) {
retval = syscall(psx_tracker.cmd.syscall_nr,
psx_tracker.cmd.arg1,
psx_tracker.cmd.arg2,
psx_tracker.cmd.arg3);
} else {
retval = syscall(psx_tracker.cmd.syscall_nr,
psx_tracker.cmd.arg1,
psx_tracker.cmd.arg2,
psx_tracker.cmd.arg3,
psx_tracker.cmd.arg4,
psx_tracker.cmd.arg5,
psx_tracker.cmd.arg6);
}
/*
* communicate the result of the thread's attempt to perform the
* syscall.
*/
long tid = _psx_gettid();
psx_lock();
psx_thread_ref_t *ref =
&psx_tracker.map[psx_mix(tid) & psx_tracker.map_mask];
ref->retval = retval;
ref->pending = 0;
/*
* Block this thread until all threads have been interrupted.
* This prevents threads clone()ing after running the syscall and
* confusing the psx mechanism into thinking they need to also run
* the syscall. They wouldn't need to run it, because they would
* inherit the thread state of a syscall that has already
* happened. However, figuring that out for an unblocked thread is
* hard, so we prevent it from happening.
*/
while (psx_tracker.cmd.active) {
psx_cond_wait();
}
psx_tracker.incomplete--;
psx_unlock();
}
/*
* only used by psx_confirm_sigaction() which is called under lock. We
* don't have to rely on the stack for this structure because we're
* reserving static space for it.
*/
static struct sigaction existing_sa;
/*
* psx_confirm_sigaction (re)confirms that the psx handler is the
* first handler to respond to the psx signal. It assumes that
* psx_tracker.psx_sig has been set.
*/
__attribute__((visibility ("hidden"))) void psx_confirm_sigaction(void) {
sigset_t mask, orig;
psx_actions_t *actions = psx_tracker.actions;
/*
* Block interrupts while potentially rewriting the handler.
*/
_psx_sigemptyset(&mask);
_psx_sigaddset(&mask, psx_tracker.psx_sig);
_psx_rt_sigprocmask(SIG_BLOCK, &mask, &orig);
_psx_rt_sigaction(psx_tracker.psx_sig, NULL, &existing_sa);
if (existing_sa.sa_handler != (void *) &psx_posix_syscall_actor) {
memcpy(&actions->chained_action, &existing_sa,
sizeof(struct sigaction));
/*
* no less masking etc than original handler (some
* architectures hook things on the restorer etc)
*/
memcpy(&actions->sig_action, &existing_sa, sizeof(existing_sa));
actions->sig_action.sa_handler = (void *) &psx_posix_syscall_actor;
actions->sig_action.sa_flags |= SA_SIGINFO | SA_ONSTACK | SA_RESTART
#ifdef SA_RESTORER
| SA_RESTORER;
/* if we need a restorer, and nothing is set, use our own */
if (actions->sig_action.sa_restorer == 0) {
actions->sig_action.sa_restorer = (void *) &psx_restorer;
}
#else
;
#endif /* def SA_RESTORER */
_psx_rt_sigaction(psx_tracker.psx_sig, &actions->sig_action, NULL);
}
_psx_rt_sigprocmask(SIG_SETMASK, &orig, NULL);
}

View File

@ -43,8 +43,6 @@
extern "C" {
#endif
#include <pthread.h>
/*
* Programmatic way to recognize feature set.
*/

View File

@ -5,6 +5,7 @@
*/
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>

View File

@ -119,7 +119,7 @@ int main(int argc, char **argv) {
cap_free(caps);
printf("no privilege in main process thread: len:%ld, caps:\"%s\"\n",
greatest_len, text);
(long int) greatest_len, text);
if (greatest_len != 1) {
printf("failed to lower privilege as expected\n");
exit(1);
@ -148,7 +148,7 @@ int main(int argc, char **argv) {
*/
printf("greatest privilege in main process thread: len:%ld, caps:\"%s\"\n",
greatest_len, text);
(long int) greatest_len, text);
cap_free(text);
if (greatest_len != 1) {