New Upstream Release - libcap2

Ready changes

Summary

Merged new upstream version: 2.69 (was: 2.66).

Diff

diff --git a/License b/License
index 43a1297..2398977 100644
--- a/License
+++ b/License
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */
+
 Unless otherwise *explicitly* stated, the following text describes the
 licensed conditions under which the contents of this libcap release
 may be used and distributed.
diff --git a/Make.Rules b/Make.Rules
index d2768e1..721fc7a 100644
--- a/Make.Rules
+++ b/Make.Rules
@@ -1,7 +1,7 @@
 # Common version number defines for libcap
 LIBTITLE=libcap
 VERSION=2
-MINOR=66
+MINOR=69
 
 #
 ## Optional prefixes:
@@ -18,7 +18,7 @@ FAKEROOT=$(DESTDIR)
 # administrative operations that could be needed to recover a system.
 
 ifndef lib
-lib=$(shell ldd /usr/bin/ld|egrep "ld-linux|ld.so"|cut -d/ -f2)
+lib=$(shell ldd /usr/bin/ld|grep -E "ld-linux|ld.so"|cut -d/ -f2)
 endif
 
 ifndef sbin
diff --git a/cap/License b/cap/License
index a0ec04e..095f754 100644
--- a/cap/License
+++ b/cap/License
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */
+
 Unless otherwise *explicitly* stated, the following text describes the
 licensed conditions under which the contents of this libcap/cap release
 may be used and distributed.
diff --git a/cap/go.mod b/cap/go.mod
index 599269e..bfabf0c 100644
--- a/cap/go.mod
+++ b/cap/go.mod
@@ -2,4 +2,4 @@ module kernel.org/pub/linux/libs/security/libcap/cap
 
 go 1.11
 
-require kernel.org/pub/linux/libs/security/libcap/psx v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/psx v1.2.69
diff --git a/cap/names.go b/cap/names.go
index 356da9e..2655380 100644
--- a/cap/names.go
+++ b/cap/names.go
@@ -2,8 +2,8 @@ package cap
 
 /* ** DO NOT EDIT THIS FILE. IT WAS AUTO-GENERATED BY LIBCAP'S GO BUILDER (mknames.go) ** */
 
-// NamedCount holds the number of capability values with official
-// names known at the time this libcap/cap version, was released. The
+// NamedCount holds the number of capability values, with official
+// names, known at the time this libcap/cap version was released. The
 // "../libcap/cap" package is fully able to manipulate higher numbered
 // capability values by numerical value. However, if you find
 // cap.NamedCount < cap.MaxBits(), it is probably time to upgrade this
@@ -42,6 +42,10 @@ const (
 	// where file owner ID should otherwise need be equal to
 	// the UID, except where cap.FSETID is applicable. It
 	// doesn't override MAC and DAC restrictions.
+	//
+	// This capability permits the deletion of a file owned
+	// by another UID in a directory protected by the sticky
+	// (t) bit.
 	FOWNER
 
 	// FSETID allows a process to set the S_ISUID and S_ISUID bits of
diff --git a/contrib/bug216610/.gitignore b/contrib/bug216610/.gitignore
new file mode 100644
index 0000000..1478d58
--- /dev/null
+++ b/contrib/bug216610/.gitignore
@@ -0,0 +1,3 @@
+*~
+arms
+Dockerfile
diff --git a/contrib/bug216610/Dockerfile b/contrib/bug216610/Dockerfile
new file mode 100644
index 0000000..5502b71
--- /dev/null
+++ b/contrib/bug216610/Dockerfile
@@ -0,0 +1,13 @@
+FROM debian:latest
+
+# A directory to share files via.
+RUN mkdir /shared
+
+RUN apt-get update
+RUN apt-get install -y gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi
+RUN apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
+
+# create a builder user
+RUN echo "builder:x:1000:1000:,,,:/home/builder:/bin/bash" >> /etc/passwd
+RUN echo "builder:*:19289:0:99999:7:::" >> /etc/shadow
+RUN mkdir -p /home/builder && chown builder.bin /home/builder
diff --git a/contrib/bug216610/Makefile b/contrib/bug216610/Makefile
new file mode 100644
index 0000000..ce96fb3
--- /dev/null
+++ b/contrib/bug216610/Makefile
@@ -0,0 +1,30 @@
+topdir=$(shell pwd)/../..
+include ../../Make.Rules
+
+GOTARGET=$(shell eval $$(go env) ; echo $${GOHOSTOS}_$${GOARCH})
+
+all: go/fib
+
+go/fib: go/main.go go/fibber/fib.go go/fibber/linkage.go go/fibber/fibs_$(GOTARGET).s go/fibber/fib_$(GOTARGET).syso
+	cd go && CGO_ENABLED=0 go build
+
+# Build the host native version.
+go/fibber/fib_$(GOTARGET).syso go/fibber/linkage.go: c/fib.c ./c/gcc.sh ./package_fns.sh
+	GCC=gcc ./c/gcc.sh -O3 c/fib.c -c -o go/fibber/fib_$(GOTARGET).syso
+	./package_fns.sh fibber go/fibber/fib_$(GOTARGET).syso > go/fibber/linkage.go
+
+Dockerfile: Makefile ./mkdocker.sh
+	./mkdocker.sh > $@
+
+# Use this build target (make arms) to extend support to include arm
+# and arm64 GOARCH values.
+arms: Dockerfile Makefile ./c/gcc.sh ./c/build.sh ./c/fib.c
+	docker run --rm -v $$PWD/c:/shared:z -h debian -u $$(id -u) -it expt shared/build.sh
+	mv c/*.syso go/fibber/
+	touch arms
+
+clean:
+	rm -f *~ arms
+	rm -f c/*.o c/*~
+	rm -f go/fib go/*~
+	rm -f go/fibber/*.syso go/fibber/*~ go/fibber/linkage.go
diff --git a/contrib/bug216610/README.md b/contrib/bug216610/README.md
new file mode 100644
index 0000000..4425715
--- /dev/null
+++ b/contrib/bug216610/README.md
@@ -0,0 +1,139 @@
+# Linking psx and C code without cgo
+
+## Overview
+
+In some embedded situations, there is a desire to compile Go binaries
+to include some C code, but not `libc` etc. For a long time, I had
+assumed this was not possible, since using `cgo` *requires* `libc` and
+`libpthread` linkage.
+
+This _embedded compilation_ need was referenced in a [bug
+filed](https://bugzilla.kernel.org/show_bug.cgi?id=216610) against the
+[`"psx"`](https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/psx)
+package. The bug-filer was seeking an alternative to `CGO_ENABLED=1`
+compilation _requiring_ the `cgo` variant of `psx` build. However, the
+go `"runtime"` package will always
+[`panic()`](https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/runtime/os_linux.go;l=717-720)
+if you try this because it needs `libpthread` and `[g]libc` to work.
+
+In researching that bug report, however, I have learned there is a
+trick to combining a non-CGO built binary with compiled C code. I
+learned about it from a brief reference in the [Go Programming
+Language
+Wiki](https://zchee.github.io/golang-wiki/GcToolchainTricks/).
+
+This present directory evolved from my attempt to understand and
+hopefully resolve what was going on as reported in that bug into an
+example of this _trick_. I was unable to resolve the problem as
+reported because of the aformentioned `panic()` in the Go
+runtime. However, I was able to demonstrate embedding C code in a Go
+binary _without_ use of cgo. In such a binary, the Go-native version
+of `"psx"` is thus achievable. This is what the example in this
+present directory demonstrates.
+
+*Caveat Emptor*: this example is very fragile. The Go team only
+supports `cgo` linking against C. That being said, I'd certainly like
+to receive bug fixes, etc for this directory if you find you need to
+evolve it to make it work for your use case.
+
+## Content
+
+In this example we have:
+
+- Some C code for the functions `fib_init()` and `fib_next()` that
+combine to implement a _compute engine_ to determine [Fibonacci
+Numbers](https://en.wikipedia.org/wiki/Fibonacci_number). The source
+for this is in the sub directory `c/fib.c`.
+
+- Some Go code, in the directory `go/fibber` that uses this C compiled
+compute kernel.
+
+- `c/gcc.sh` which is a wrapper for `gcc` that adjusts the compilation
+to be digestible by Go's (internal) linker (the one that gets invoked
+when compiling `CGO_ENABLED=0`. Using `gcc` directly instead of this
+wrapper generates an incomplete binary - which miscomputes the
+expected answers. See the discussion below for what seems to be going
+on.
+
+- A top level `Makefile` to build it all.
+
+## Building and running the built binary
+
+Set things up with:
+```
+$ git clone git://git.kernel.org/pub/scm/libs/libcap/libcap.git
+$ cd libcap
+$ make all
+$ cd contrib/bug216610
+$ make clean all
+```
+When you run `./go/fib` it should generate the following output:
+```
+$ ./go/fib
+psx syscall result: PID=<nnnnn>
+fib: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
+$
+```
+Where `<nnnnn>` is the PID of the program at runtime and will be
+different each time the program is invoked.
+
+## Discussion
+
+The Fibonacci detail of what is going on is mostly uninteresting. The
+reason for developing this example was to explore the build issues in
+the reported [Bug
+216610](https://bugzilla.kernel.org/show_bug.cgi?id=216610). Ultimately,
+this example offers an alternative path to building a `nocgo` program
+that links to compute kernel of C code.
+
+The reason we have added the `c/gcc.sh` wrapper for `gcc` is that
+we've found the Go linker has a hard time digesting the
+cross-sectional `%rip` based data addressing that various optimization
+modes of gcc like to use. Specifically, in the x86_64/amd64
+architecture, if a `R_X86_64_PC32` relocation entry made in a `.text`
+section refers to an `.rodata.cst8` section in a generated `.syso`
+file, the Go linker seems to [replace this reference with a `0` offset
+to
+`(%rip)`](https://github.com/golang/go/issues/24321#issuecomment-1296084103). What
+our wrapper script does is rewrite the generated assembly to store
+these data references to the `.text` section. The Go linker has no
+problem with this _same section_ relative addressing and is able to
+link the resulting objects without problems.
+
+If you want to cross compile, we have support for 32-bit arm
+compilation: what is needed for the Raspberry PI. To get this support,
+try:
+```
+$ make clean all arms
+$ cd go
+$ GOARCH=arm CGO_ENABLED=0 go build
+```
+The generated `fib` binary runs on a 32-bit Raspberry Pi.
+
+## Future thoughts
+
+At present, this example only works on Linux with `x86_64` and `arm`
+build architectures. (In go-speak that is `linux_amd64` and
+`linux_arm`). This is because I have only provided some bridging
+assembly for Go to C calling conventions for those architecture
+targets: `./go/fibber/fibs_linux_amd64.s` and
+`./go/fibber/fibs_linux_arm.s`. The non-native, `make arms`, cross
+compilation requires the `docker` command to be available.
+
+I intend to implement an `arm64` build, when I have a system on which
+to test it.
+
+**Note** The Fedora system on which I've been developing this has some
+  SELINUX impediment to naively using the `docker -v ...` bind mount
+  option. I need the `:z` suffix for bind mounting. I don't know how
+  common an issue this is. On Fedora, building the arm variants of the
+  .syso file can be performed as follows:
+```
+$ docker run --rm -v $PWD/c:/shared:z -h debian -u $(id -u) -it expt shared/build.sh
+```
+
+## Reporting bugs
+
+Please report issues or offer improvements to this example via the
+[Fully Capable `libcap`](https://sites.google.com/site/fullycapable/)
+website.
diff --git a/contrib/bug216610/c/build.sh b/contrib/bug216610/c/build.sh
new file mode 100755
index 0000000..7458fb1
--- /dev/null
+++ b/contrib/bug216610/c/build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# Builds the following .syso files to the directory containing this script:
+#
+#   fib_linux_arm.syso
+#   fib_linux_arm64.syso
+
+cd ${0%/*}
+GCC=arm-linux-gnueabi-gcc ./gcc.sh -O3 fib.c -c -o fib_linux_arm.syso
+GCC=aarch64-linux-gnu-gcc ./gcc.sh -O3 fib.c -c -o fib_linux_arm64.syso
diff --git a/contrib/bug216610/c/fib.c b/contrib/bug216610/c/fib.c
new file mode 100644
index 0000000..bd665c7
--- /dev/null
+++ b/contrib/bug216610/c/fib.c
@@ -0,0 +1,20 @@
+#include <inttypes.h>
+
+struct state {
+    uint32_t b, a;
+};
+
+void fib_init(struct state *s);
+void fib_init(struct state *s)
+{
+    s->a = 0;
+    s->b = 1;
+}
+
+void fib_next(struct state *s);
+void fib_next(struct state *s)
+{
+    uint32_t next = s->a + s->b;
+    s->a = s->b;
+    s->b = next;
+}
diff --git a/contrib/bug216610/c/gcc.sh b/contrib/bug216610/c/gcc.sh
new file mode 100755
index 0000000..33655d6
--- /dev/null
+++ b/contrib/bug216610/c/gcc.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# The Go linker does not seem to know what to do with relative
+# addressing of rodata.* offset from %rip. GCC likes to use this
+# addressing mode on this architecture, so we quickly run into
+# mis-computation when the relative addressing used in a .syso file of
+# symbol located data is resolved to completely the wrong place by the
+# Go (internal) linker.
+#
+# As a workaround for this, we can modify the assembly source code
+# generated by GCC to not point at problematic '.rodata.*' sections,
+# and place this data in the good old '.text' section where Go's
+# linker can make sense of it.
+#
+# This script exists to generate a '.syso' file from some '*.c' files.
+# It works by recognizing the '*.c' command line arguments and
+# converting them into fixed-up '*.s' files. It then performs the
+# compilation for the collection of the '*.s' files. Upon success, it
+# purges the intermediate '*.s' files.
+#
+# The fragile aspect of this present script is which compiler
+# arguments should be used for the compilation from '.c' -> '.s'
+# files. What we do is accumulate arguments until we encounter our
+# first '*.c' file and use those to perform the '.c' -> '.s'
+# compilation. We build up a complete command line for gcc
+# substituting '.s' files for '.c' files in the original command
+# line. Then with the new command line assembled we invoke gcc with
+# those. If that works, we remove all of the intermediate '.s' files.
+
+GCC="${GCC:=gcc}"
+setup=0
+args=()
+final=()
+ses=()
+
+for arg in "$@"; do
+    if [[ "${arg##*.}" = "c" ]]; then
+	setup=1
+	s="${arg%.*}.s"
+	"${GCC}" "${args[@]}" -S -o "${s}" "${arg}"
+	sed -i -e 's/.*\.rodata\..*/\t.text/' "${s}"
+	final+=("${s}")
+	ses+=("${s}")
+    else
+	if [[ $setup -eq 0 ]]; then
+	    args+=("${arg}")
+	fi
+	final+=("${arg}")
+    fi
+done
+
+#echo final: "${final[@]}"
+#echo args: "${args[@]}"
+#echo ses: "${ses[@]}"
+
+"${GCC}" "${final[@]}"
+if [[ $? -ne 0 ]]; then
+    echo "failed to compile"
+    exit 1
+fi
+rm -f "${ses[@]}"
diff --git a/contrib/bug216610/go/.gitignore b/contrib/bug216610/go/.gitignore
new file mode 100644
index 0000000..ae14305
--- /dev/null
+++ b/contrib/bug216610/go/.gitignore
@@ -0,0 +1,5 @@
+fib
+*.syso
+main
+go.sum
+linkage.go
diff --git a/contrib/bug216610/go/fibber/fib.go b/contrib/bug216610/go/fibber/fib.go
new file mode 100644
index 0000000..49757cd
--- /dev/null
+++ b/contrib/bug216610/go/fibber/fib.go
@@ -0,0 +1,32 @@
+// Package fibber implements a Fibonacci sequence generator using a C
+// coded compute kernel (a .syso file).
+package fibber
+
+import (
+	"unsafe"
+)
+
+// State is the native Go form of the C.state structure.
+type State struct {
+	B, A uint32
+}
+
+// cPtr converts State into a C pointer suitable as an argument for
+// sysoCaller.
+func (s *State) cPtr() unsafe.Pointer {
+	return unsafe.Pointer(&s.B)
+}
+
+// NewState initializes a Fibonacci Number sequence generator.  Upon
+// return s.A=0 and s.B=1 are the first two numbers in the sequence.
+func NewState() *State {
+	s := &State{}
+	syso__fib_init.call(s.cPtr())
+	return s
+}
+
+// Next advances the state to the next number in the sequence. Upon
+// return, s.B is the most recently calculated value.
+func (s *State) Next() {
+	syso__fib_next.call(s.cPtr())
+}
diff --git a/contrib/bug216610/go/fibber/fibs_linux_amd64.s b/contrib/bug216610/go/fibber/fibs_linux_amd64.s
new file mode 100644
index 0000000..5992d09
--- /dev/null
+++ b/contrib/bug216610/go/fibber/fibs_linux_amd64.s
@@ -0,0 +1,21 @@
+// To transition from a Go call to a C function call, we are skating
+// on really thin ice... Ceveat Emptor!
+//
+// Ref:
+//   https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home
+//
+// This is not strictly needed, but it makes gdb debugging less
+// confusing because spacer ends up being an alias for the TEXT
+// section start.
+TEXT ·spacer(SB),$0
+	RET
+
+#define RINDEX(n) (8*n)
+
+// Header to this function wrapper is the last time we can voluntarily
+// yield to some other goroutine.
+TEXT ·syso(SB),$0-16
+	MOVQ cFn+RINDEX(0)(FP), SI
+	MOVQ state+RINDEX(1)(FP), DI
+	CALL *SI
+	RET
diff --git a/contrib/bug216610/go/fibber/fibs_linux_arm.s b/contrib/bug216610/go/fibber/fibs_linux_arm.s
new file mode 100644
index 0000000..39640a5
--- /dev/null
+++ b/contrib/bug216610/go/fibber/fibs_linux_arm.s
@@ -0,0 +1,23 @@
+// To transition from a Go call to a C function call, we are skating
+// on really thin ice... Ceveat Emptor!
+//
+// Ref:
+//   https://stackoverflow.com/questions/261419/what-registers-to-save-in-the-arm-c-calling-convention
+//
+// This is not strictly needed, but it makes gdb debugging less
+// confusing because spacer ends up being an alias for the TEXT
+// section start.
+TEXT ·spacer(SB),$0
+	RET
+
+#define FINDEX(n) (8*n)
+
+// Header to this function wrapper is the last time we can voluntarily
+// yield to some other goroutine.
+//
+// Conventions: PC == R15, SP == R13, LR == R14, IP (scratch) = R12
+TEXT ·syso(SB),$0-8
+	MOVW	cFn+0(FP), R14
+	MOVW    state+4(FP), R0
+	BL	(R14)
+	RET
diff --git a/contrib/bug216610/go/go.mod b/contrib/bug216610/go/go.mod
new file mode 100644
index 0000000..8531994
--- /dev/null
+++ b/contrib/bug216610/go/go.mod
@@ -0,0 +1,5 @@
+module fib
+
+go 1.18
+
+require kernel.org/pub/linux/libs/security/libcap/psx v1.2.69
diff --git a/contrib/bug216610/go/main.go b/contrib/bug216610/go/main.go
new file mode 100644
index 0000000..65121f6
--- /dev/null
+++ b/contrib/bug216610/go/main.go
@@ -0,0 +1,29 @@
+// Program fib uses the psx package once, and then prints the first
+// ten Fibonacci numbers.
+package main
+
+import (
+	"fmt"
+	"log"
+	"syscall"
+
+	"fib/fibber"
+
+	"kernel.org/pub/linux/libs/security/libcap/psx"
+)
+
+func main() {
+	pid, _, err := psx.Syscall3(syscall.SYS_GETPID, 0, 0, 0)
+	if err != 0 {
+		log.Fatalf("failed to get PID via psx: %v", err)
+	}
+	fmt.Print("psx syscall result: PID=")
+	fmt.Println(pid)
+	s := fibber.NewState()
+	fmt.Print("fib: ", s.A, ", ", s.B)
+	for i := 0; i < 8; i++ {
+		s.Next()
+		fmt.Print(", ", s.B)
+	}
+	fmt.Println(", ...")
+}
diff --git a/contrib/bug216610/mkdocker.sh b/contrib/bug216610/mkdocker.sh
new file mode 100755
index 0000000..860c198
--- /dev/null
+++ b/contrib/bug216610/mkdocker.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# This script generates a Dockerfile to be used for cross-compilation
+cat <<EOF
+FROM debian:latest
+
+# A directory to share files via.
+RUN mkdir /shared
+
+RUN apt-get update
+RUN apt-get install -y gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi
+RUN apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
+
+# create a builder user
+RUN echo "builder:x:$(id -u):$(id -g):,,,:/home/builder:/bin/bash" >> /etc/passwd
+RUN echo "builder:*:19289:0:99999:7:::" >> /etc/shadow
+RUN mkdir -p /home/builder && chown builder.bin /home/builder
+EOF
diff --git a/contrib/bug216610/package_fns.sh b/contrib/bug216610/package_fns.sh
new file mode 100755
index 0000000..0f4b91c
--- /dev/null
+++ b/contrib/bug216610/package_fns.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Generate some Go code to make calling into the C code of the .syso
+# file easier.
+
+package="${1}"
+syso="${2}"
+
+if [[ -z "${syso}" ]]; then
+    echo "usage: $0 <package> <.....syso>" >&2
+    exit 1
+fi
+
+if [[ "${syso%.syso}" == "${syso}" ]]; then
+    echo "2nd argument should be a .syso file" >&2
+    exit 1
+fi
+
+cat<<EOF
+package ${package}
+
+import (
+	"unsafe"
+)
+
+// syso is how we call, indirectly, into the C-code.
+func syso(cFn, state unsafe.Pointer)
+
+type sysoCaller struct {
+	ptr unsafe.Pointer
+}
+
+// call calls the syso linked C-function, $sym().
+func (s *sysoCaller) call(data unsafe.Pointer) {
+	syso(s.ptr, data)
+}
+EOF
+
+for sym in $(objdump -x "${syso}" | grep -F 'g     F' | awk '{print $6}'); do
+    cat<<EOF
+
+//go:linkname _${sym} ${sym}
+var _${sym} byte
+var syso__${sym} = &sysoCaller{ptr: unsafe.Pointer(&_${sym})}
+
+EOF
+done
diff --git a/contrib/capso/.gitignore b/contrib/capso/.gitignore
new file mode 100644
index 0000000..222d35d
--- /dev/null
+++ b/contrib/capso/.gitignore
@@ -0,0 +1,2 @@
+capso.so
+bind
diff --git a/contrib/capso/README.md b/contrib/capso/README.md
index 64e9b43..df2e878 100644
--- a/contrib/capso/README.md
+++ b/contrib/capso/README.md
@@ -1,20 +1,21 @@
 # Leveraging file capabilities on shared libraries
 
-This directory contains an example of a shared library (capso.so) that
-can be installed with file capabilities. When the library is linked
-against an unprivileged program, it includes internal support for
-re-invoking itself as a child subprocess to execute a privileged
+This directory contains an example of a shared library (`capso.so`)
+that can be installed with file capabilities. When the library is
+linked against an unprivileged program, it includes internal support
+for re-invoking itself as a child subprocess to execute a privileged
 operation on bahalf of the parent.
 
-The idea for doing this was evolved from the way pam_unix.so is able
-to leverage a separate program, and libcap's recently added support
-for supporting binary execution of all the .so files built by the
+The idea for doing this was evolved from the way `pam_unix.so` is able
+to leverage a separate program, and `libcap`'s recently added support
+for supporting binary execution of all the `.so` files built by the
 package.
 
-The actual program example 'bind' leverages the
-"cap_net_bind_service=p" ./capso.so file to bind to the privileged
-port 80.
+The actual program example `./bind` leverages the
+`"cap_net_bind_service=p"` enabled `./capso.so` file to bind to the
+privileged port 80.
 
-A writeup of how to explore this example is provided here:
+A writeup of how to build and explore the behavior of this example is
+provided on the `libcap` distribution website:
 
 https://sites.google.com/site/fullycapable/capable-shared-objects
diff --git a/contrib/capso/capso.c b/contrib/capso/capso.c
index 2a357c0..7ca3427 100644
--- a/contrib/capso/capso.c
+++ b/contrib/capso/capso.c
@@ -26,6 +26,73 @@
 
 #include "capso.h"
 
+extern char **environ;
+
+/*
+ * fake_exploit is some dedicated code to simulate a shell escape type
+ * exploit. This is obviously not something serious to include in code
+ * that has actually been audited for security, but we use it to
+ * demonstrate an aspect of file capabilities vs. setuid root for
+ * granting privilege.
+ */
+static void fake_exploit(void) {
+#ifdef ALLOW_EXPLOIT
+    const char *exploit = getenv("TRIGGER_EXPLOIT");
+    if (exploit == NULL) {
+	return;
+    }
+
+    switch (*exploit) {
+    case '^':
+    case '%':
+	exploit++;
+	cap_value_t caps = CAP_NET_BIND_SERVICE;
+	cap_t c = cap_get_proc();
+	cap_set_flag(c, CAP_INHERITABLE, 1, &caps, CAP_SET);
+	if (cap_set_proc(c)) {
+	    perror("Failed to raise inheritable capability");
+	    exit(1);
+	}
+	if (*(exploit-1) == '%') {
+	    break;
+	}
+	cap_free(c);
+	if (cap_set_ambient(caps, CAP_SET) != 0) {
+	    perror("Unable to raise ambient capability");
+	    exit(1);
+	}
+	break;
+    }
+
+    char *ts = strdup(exploit);
+    if (ts == NULL) {
+	perror("Failed to duplicate exploit string");
+	exit(1);
+    }
+
+    int i, j, n = 1;
+    for (i = 0; ts[i]; i++) {
+	switch (ts[i]) {
+	case ' ':
+	case '\t':
+	    n++;
+	    ts[i] = '\0';
+	}
+    }
+    char **argv = calloc(n, sizeof(char *));
+    for (i = 0, j = 0; j < n; j++) {
+	char *s = ts+i;
+	argv[j] = s;
+	i += 1 + strlen(s);
+	printf("execv argv[%d] = \"%s\"\n", j, s);
+    }
+
+    execv(argv[0], argv);
+    perror("Execv failed");
+    exit(1);
+#endif /* def ALLOW_EXPLOIT */
+}
+
 /*
  * where_am_i determines the full path for the shared libary that
  * contains this function. It allocates the path in strdup()d memory
@@ -160,7 +227,7 @@ int bind80(const char *hostname)
      */
     path = where_am_i();
     if (path == NULL) {
-	perror("unable to find self");
+	perror("Unable to find self");
 	goto drop_alloc;
     }
 
@@ -168,7 +235,7 @@ int bind80(const char *hostname)
     args[1] = hostname;
     args[2] = NULL;
 
-    helper = cap_new_launcher(path, args, NULL);
+    helper = cap_new_launcher(path, args, (void *) environ);
     if (helper == NULL) {
 	goto drop_path;
     }
@@ -247,19 +314,19 @@ SO_MAIN(int argc, char **argv)
 
     working = cap_get_proc();
     if (working == NULL) {
-	perror("unable to read capabilities");
+	perror("Unable to read capabilities");
 	exit(1);
     }
 
     if (cap_set_flag(working, CAP_EFFECTIVE, 1,
 		     &cap_net_bind_service, CAP_SET) != 0) {
-	perror("unable to raise CAP_NET_BIND_SERVICE");
+	perror("Unable to raise CAP_NET_BIND_SERVICE");
 	exit(1);
     }
 
     if (cap_set_proc(working) != 0) {
-	perror("cap_set_proc problem");
-	fprintf(stderr, "try: sudo setcap cap_net_bind_service=p %s\n",
+	perror("Problem with cap_set_proc");
+	fprintf(stderr, "Try: sudo setcap cap_net_bind_service=p %s\n",
 		argv[0]);
 	exit(1);
     }
@@ -287,9 +354,11 @@ SO_MAIN(int argc, char **argv)
     *((int *) CMSG_DATA(ctrl)) = fd;
 
     if (sendmsg(3, &msg, 0) < 0) {
-	perror("failed to write fd");
+	perror("Failed to write fd");
     }
 
+    fake_exploit();
+
 #ifdef CAPSO_DEBUG
     printf("exiting standalone %s\n", argv[0]);
     sleep(30);
diff --git a/contrib/seccomp/go.mod b/contrib/seccomp/go.mod
index 250ce41..ecf18d9 100644
--- a/contrib/seccomp/go.mod
+++ b/contrib/seccomp/go.mod
@@ -2,4 +2,4 @@ module explore
 
 go 1.14
 
-require kernel.org/pub/linux/libs/security/libcap/psx v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/psx v1.2.69
diff --git a/contrib/sucap/README.md b/contrib/sucap/README.md
index 0808912..5cc0dcc 100644
--- a/contrib/sucap/README.md
+++ b/contrib/sucap/README.md
@@ -1,22 +1,21 @@
-This directory contains a port of the SimplePAMApp su to more
-aggressively use libcap.
+# A fully capable version of `su`
 
-The Makefile builds a binary called `su` that registers with PAM as
-the application `sucap`. We've provided a sample `/etc/pam.d/sucap`
-file in this directory named `sucap.pamconfig`.
+This directory contains a port of the `SimplePAMApp` `su` one that can
+work in a `PURE1E` `libcap`-_mode_ environment.
 
-The point of developing this is to better test the full libcap
+The point of developing this is to better test the full `libcap`
 implementation, and to also provide a non-setuid-root worked example
-for testing PAM interaction with libcap and pam_cap.so. The
-expectations for `pam_unix.so` are that it includes this commit:
+for testing PAM interaction with `libcap` and `pam_cap.so`. The
+required expectations for `pam_unix.so` are that it include this
+commit:
 
 https://github.com/linux-pam/linux-pam/pull/373/commits/bf9b1d8ad909634000a7356af2d865a79d3f86f3
 
-The original sources were found here:
+The original sources for this version of `su` were found here:
 
 https://kernel.org/pub/linux/libs/pam/pre/applications/SimplePAMApps-0.60.tar.gz
 
-The SimplePAMApps contain the same License as libcap (they were
+The `SimplePAMApps` contain the same License as `libcap` (they were
 originally started by the same authors!). The credited Authors in the
 above tarball were:
 
@@ -33,7 +32,7 @@ tar ball and is thus a derived work from that.
 Finally, Andrew would like to apologize to Andrey for removing all of
 the config support he worked to add all those decades ago..! I just
 wanted to make a quick tester for a potential workaround for this
-pam_cap issue:
+`pam_cap.so` issue:
 
 -  https://bugzilla.kernel.org/show_bug.cgi?id=212945
 
diff --git a/debian/changelog b/debian/changelog
index cb547f0..377983e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+libcap2 (1:2.69-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch Correct-the-check-of-pthread_create-s-return-value.patch, present
+    upstream.
+  * Drop patch Large-strings-can-confuse-libcap-s-internal-strdup-code.patch,
+    present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 20 Jun 2023 15:24:22 -0000
+
 libcap2 (1:2.66-4) unstable; urgency=medium
 
   * Apply upstream patches for CVE-2023-2602, CVE-2023-2603
diff --git a/debian/patches/Correct-the-check-of-pthread_create-s-return-value.patch b/debian/patches/Correct-the-check-of-pthread_create-s-return-value.patch
deleted file mode 100644
index afcc560..0000000
--- a/debian/patches/Correct-the-check-of-pthread_create-s-return-value.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From: "Andrew G. Morgan" <morgan@kernel.org>
-Date: Wed, 3 May 2023 19:18:36 -0700
-Subject: Correct the check of pthread_create()'s return value.
-
-This function returns a positive number (errno) on error, so the code
-wasn't previously freeing some memory in this situation.
-
-Discussion:
-
-  https://stackoverflow.com/a/3581020/14760867
-
-Credit for finding this bug in libpsx goes to David Gstir of
-X41 D-Sec GmbH (https://x41-dsec.de/) who performed a security
-audit of the libcap source code in April of 2023. The audit
-was sponsored by the Open Source Technology Improvement Fund
-(https://ostif.org/).
-
-Audit ref: LCAP-CR-23-01 (CVE-2023-2602)
-
-Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-
-Origin: upstream, https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=bc6b36682f188020ee4770fae1d41bde5b2c97bb
----
- psx/psx.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/psx/psx.c b/psx/psx.c
-index d9c0485..65eb2aa 100644
---- a/psx/psx.c
-+++ b/psx/psx.c
-@@ -516,7 +516,7 @@ int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
-     pthread_sigmask(SIG_BLOCK, &sigbit, NULL);
- 
-     int ret = __real_pthread_create(thread, attr, _psx_start_fn, starter);
--    if (ret == -1) {
-+    if (ret > 0) {
- 	psx_new_state(_PSX_CREATE, _PSX_IDLE);
- 	memset(starter, 0, sizeof(*starter));
- 	free(starter);
diff --git a/debian/patches/Filter-out-PIE-flags-when-building-shared-objects.patch b/debian/patches/Filter-out-PIE-flags-when-building-shared-objects.patch
index 1012342..5c033a5 100644
--- a/debian/patches/Filter-out-PIE-flags-when-building-shared-objects.patch
+++ b/debian/patches/Filter-out-PIE-flags-when-building-shared-objects.patch
@@ -12,10 +12,10 @@ Last-Update: 2022-03-04
  pam_cap/Makefile | 3 +++
  2 files changed, 5 insertions(+)
 
-Index: libcap2/libcap/Makefile
+Index: libcap2.git/libcap/Makefile
 ===================================================================
---- libcap2.orig/libcap/Makefile
-+++ libcap2/libcap/Makefile
+--- libcap2.git.orig/libcap/Makefile
++++ libcap2.git/libcap/Makefile
 @@ -18,6 +18,10 @@ CAPMAGICOBJ=cap_magic.o
  PSXFILES=../psx/psx
  PSXMAGICOBJ=psx_magic.o
@@ -27,10 +27,10 @@ Index: libcap2/libcap/Makefile
  # Always build libcap sources this way:
  CFLAGS += -fPIC
  
-Index: libcap2/pam_cap/Makefile
+Index: libcap2.git/pam_cap/Makefile
 ===================================================================
---- libcap2.orig/pam_cap/Makefile
-+++ libcap2/pam_cap/Makefile
+--- libcap2.git.orig/pam_cap/Makefile
++++ libcap2.git/pam_cap/Makefile
 @@ -3,6 +3,10 @@
  topdir=$(shell pwd)/..
  include ../Make.Rules
diff --git a/debian/patches/Hide-private-symbols.patch b/debian/patches/Hide-private-symbols.patch
index 8c451dd..d76627c 100644
--- a/debian/patches/Hide-private-symbols.patch
+++ b/debian/patches/Hide-private-symbols.patch
@@ -15,10 +15,10 @@ Last-Update: 2022-03-04
  libcap/Makefile | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-Index: libcap2/libcap/cap_names.header
+Index: libcap2.git/libcap/cap_names.header
 ===================================================================
---- libcap2.orig/libcap/cap_names.header
-+++ libcap2/libcap/cap_names.header
+--- libcap2.git.orig/libcap/cap_names.header
++++ libcap2.git/libcap/cap_names.header
 @@ -1,5 +1,5 @@
  struct __cap_token_s { const char *name; int index; };
  %{
diff --git a/debian/patches/Large-strings-can-confuse-libcap-s-internal-strdup-code.patch b/debian/patches/Large-strings-can-confuse-libcap-s-internal-strdup-code.patch
deleted file mode 100644
index f5a8b02..0000000
--- a/debian/patches/Large-strings-can-confuse-libcap-s-internal-strdup-code.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From: "Andrew G. Morgan" <morgan@kernel.org>
-Date: Wed, 3 May 2023 19:44:22 -0700
-Subject: Large strings can confuse libcap's internal strdup code.
-
-Avoid something subtle with really long strings: 1073741823 should
-be enough for anybody. This is an improved fix over something attempted
-in libcap-2.55 to address some static analysis findings.
-
-Reviewing the library, cap_proc_root() and cap_launcher_set_chroot()
-are the only two calls where the library is potentially exposed to a
-user controlled string input.
-
-Credit for finding this bug in libcap goes to Richard Weinberger of
-X41 D-Sec GmbH (https://x41-dsec.de/) who performed a security audit
-of the libcap source code in April of 2023. The audit was sponsored
-by the Open Source Technology Improvement Fund (https://ostif.org/).
-
-Audit ref: LCAP-CR-23-02 (CVE-2023-2603)
-
-Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-
-Origin: upstream, https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=422bec25ae4a1ab03fd4d6f728695ed279173b18
----
- libcap/cap_alloc.c | 12 +++++++-----
- 1 file changed, 7 insertions(+), 5 deletions(-)
-
-diff --git a/libcap/cap_alloc.c b/libcap/cap_alloc.c
-index c826e7a..25f9981 100644
---- a/libcap/cap_alloc.c
-+++ b/libcap/cap_alloc.c
-@@ -105,15 +105,17 @@ char *_libcap_strdup(const char *old)
- 	errno = EINVAL;
- 	return NULL;
-     }
--    len = strlen(old) + 1 + 2*sizeof(__u32);
--    if (len < sizeof(struct _cap_alloc_s)) {
--	len = sizeof(struct _cap_alloc_s);
--    }
--    if ((len & 0xffffffff) != len) {
-+
-+    len = strlen(old);
-+    if ((len & 0x3fffffff) != len) {
- 	_cap_debug("len is too long for libcap to manage");
- 	errno = EINVAL;
- 	return NULL;
-     }
-+    len += 1 + 2*sizeof(__u32);
-+    if (len < sizeof(struct _cap_alloc_s)) {
-+	len = sizeof(struct _cap_alloc_s);
-+    }
- 
-     raw_data = calloc(1, len);
-     if (raw_data == NULL) {
diff --git a/debian/patches/series b/debian/patches/series
index b4f05ad..bc258c3 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,2 @@
 Hide-private-symbols.patch
 Filter-out-PIE-flags-when-building-shared-objects.patch
-Correct-the-check-of-pthread_create-s-return-value.patch
-Large-strings-can-confuse-libcap-s-internal-strdup-code.patch
diff --git a/doc/Makefile b/doc/Makefile
index c7d50e0..6919488 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -11,7 +11,9 @@ MAN3S = cap_init.3 cap_free.3 cap_dup.3 \
 	cap_fill.3 cap_fill_flag.3 cap_max_bits.3 \
 	cap_compare.3 cap_get_proc.3 cap_get_pid.3 cap_set_proc.3 \
 	cap_get_file.3 cap_get_fd.3 cap_set_file.3 cap_set_fd.3 \
+	cap_set_nsowner.3 cap_get_nsowner.3 \
 	cap_copy_ext.3 cap_size.3 cap_copy_int.3 cap_mode.3 \
+	cap_copy_int_check.3 cap_set_syscall.3 \
 	cap_from_text.3 cap_to_text.3 cap_from_name.3 cap_to_name.3 \
 	capsetp.3 capgetp.3 libcap.3 \
 	cap_get_bound.3 cap_drop_bound.3 \
@@ -25,8 +27,10 @@ MAN3S = cap_init.3 cap_free.3 cap_dup.3 \
 	cap_iab.3 cap_iab_init.3 cap_iab_dup.3 cap_iab_compare.3 \
 	cap_iab_get_proc.3 cap_iab_get_pid.3 cap_iab_set_proc.3 \
 	cap_iab_to_text.3 cap_iab_from_text.3 cap_iab_get_vector.3 \
-	cap_iab_set_vector.3 cap_iab_fill.3 \
+	cap_iab_set_vector.3 cap_iab_fill.3 cap_proc_root.3 \
+	cap_prctl.3 cap_prctlw.3 \
 	psx_syscall.3 psx_syscall3.3 psx_syscall6.3 psx_set_sensitivity.3 \
+	psx_load_syscalls.3 __psx_syscall.3 \
 	libpsx.3
 MAN8S = getcap.8 setcap.8 getpcaps.8 captree.8
 
diff --git a/doc/__psx_syscall.3 b/doc/__psx_syscall.3
new file mode 100644
index 0000000..663420c
--- /dev/null
+++ b/doc/__psx_syscall.3
@@ -0,0 +1 @@
+.so man3/libpsx.3
diff --git a/doc/cap_clear.3 b/doc/cap_clear.3
index 19a736a..b8dbc30 100644
--- a/doc/cap_clear.3
+++ b/doc/cap_clear.3
@@ -1,6 +1,6 @@
-.TH CAP_CLEAR 3 "2021-10-01" "" "Linux Programmer's Manual"
+.TH CAP_CLEAR 3 "2022-10-16" "" "Linux Programmer's Manual"
 .SH NAME
-cap_clear, cap_clear_flag, cap_get_flag, cap_set_flag, cap_fill_flag, cap_fill, cap_compare \- capability data object manipulation
+cap_clear, cap_clear_flag, cap_get_flag, cap_set_flag, cap_fill_flag, cap_fill, cap_compare, cap_max_bits \- capability data object manipulation
 .SH SYNOPSIS
 .nf
 #include <sys/capability.h>
diff --git a/doc/cap_copy_ext.3 b/doc/cap_copy_ext.3
index 0965ad1..b863442 100644
--- a/doc/cap_copy_ext.3
+++ b/doc/cap_copy_ext.3
@@ -9,6 +9,7 @@ external representation translation
 ssize_t cap_size(cap_t cap_p);
 ssize_t cap_copy_ext(void *ext_p, cap_t cap_p, ssize_t size);
 cap_t cap_copy_int(const void * ext_p);
+cap_t cap_copy_int_check(const void *cap_ext, ssize_t length);
 .fi
 .sp
 Link with \fI\-lcap\fP.
@@ -56,9 +57,9 @@ state.  The function initializes the capability state and then copies
 the capability state from the record pointed to by
 .I ext_p
 into the capability state, converting, if necessary, the data from a
-contiguous, persistent format to an undefined, internal format.  Once
-copied into internal format, the object can be manipulated by the capability
-state manipulation functions (see
+contiguous, persistent format to an opaque, internal format.  Once
+copied into internal format, the object can be manipulated by the
+capability state manipulation functions (see
 .BR cap_clear (3)).
 Note that the record pointed to by
 .I ext_p
@@ -71,6 +72,12 @@ longer required, by calling
 with the
 .I cap_t
 as an argument.
+.PP
+.BR cap_copy_int_check ()
+performs the same operation as
+.BR cap_copy_int ()
+but additionally checks that the provided external data's size is not
+larger than the noted length.
 .SH "RETURN VALUE"
 .BR cap_size ()
 returns the length required to hold a capability data record on success,
@@ -82,8 +89,10 @@ returns the number of bytes placed in the user managed space pointed to by
 on success, and \-1 on failure.
 .PP
 .BR cap_copy_int ()
-returns a pointer to the newly created capability state in working storage
-on success, and NULL on failure.
+and
+.BR cap_copy_int_check ()
+return a pointer to the newly created capability state in working
+storage on success, and NULL on failure.
 .PP
 On failure,
 .BR errno
diff --git a/doc/cap_copy_int_check.3 b/doc/cap_copy_int_check.3
new file mode 100644
index 0000000..2e6e89c
--- /dev/null
+++ b/doc/cap_copy_int_check.3
@@ -0,0 +1 @@
+.so man3/cap_copy_ext.3
diff --git a/doc/cap_from_text.3 b/doc/cap_from_text.3
index a0c9282..1a01c7c 100644
--- a/doc/cap_from_text.3
+++ b/doc/cap_from_text.3
@@ -9,9 +9,9 @@ state textual representation translation
 .nf
 #include <sys/capability.h>
 
-cap_t cap_from_text(const char* buf_p );
-char *cap_to_text(cap_t caps, ssize_t * len_p);
-int cap_from_name(const char* name , cap_value_t* cap_p);
+cap_t cap_from_text(const char *buf_p);
+char *cap_to_text(cap_t caps, ssize_t *len_p);
+int cap_from_name(const char *name, cap_value_t *cap_p);
 char *cap_to_name(cap_value_t cap);
 .fi
 .sp
diff --git a/doc/cap_get_file.3 b/doc/cap_get_file.3
index 4c812fe..985236c 100644
--- a/doc/cap_get_file.3
+++ b/doc/cap_get_file.3
@@ -1,10 +1,10 @@
 .\"
 .\" written by Andrew Main <zefram@dcs.warwick.ac.uk>
 .\"
-.TH CAP_GET_FILE 3 "2021-03-06" "" "Linux Programmer's Manual"
+.TH CAP_GET_FILE 3 "2022-10-16" "" "Linux Programmer's Manual"
 .SH NAME
-cap_get_file, cap_set_file, cap_get_fd, cap_set_fd \- capability
-manipulation on files
+cap_get_file, cap_set_file, cap_get_fd, cap_set_fd, cap_get_nsowner, \
+cap_set_nsowner \- capability manipulation on files
 .SH SYNOPSIS
 .nf
 #include <sys/capability.h>
diff --git a/doc/cap_get_nsowner.3 b/doc/cap_get_nsowner.3
new file mode 100644
index 0000000..3970c34
--- /dev/null
+++ b/doc/cap_get_nsowner.3
@@ -0,0 +1 @@
+.so man3/cap_get_file.3
diff --git a/doc/cap_iab.3 b/doc/cap_iab.3
index 8bec5fd..3e6282d 100644
--- a/doc/cap_iab.3
+++ b/doc/cap_iab.3
@@ -1,33 +1,28 @@
-.TH CAP_IAB 3 "2021-11-17" "" "Linux Programmer's Manual"
+.TH CAP_IAB 3 "2022-10-16" "" "Linux Programmer's Manual"
 .SH NAME
+cap_iab_init, cap_iab_dup, cap_iab_get_proc, cap_iab_get_pid, \
+cap_iab_set_proc, cap_iab_to_text, cap_iab_from_text, \
+cap_iab_get_vector, cap_iab_compare, cap_iab_set_vector, \
+cap_iab_fill, cap_proc_root \- inheritable IAB tuple support functions
+.SH SYNOPSIS
 .nf
 #include <sys/capability.h>
 
 cap_iab_t cap_iab_init(void);
-
 cap_iab_t cap_iab_dup(cap_iab_t iab);
-
 cap_iab_t cap_iab_get_proc(void);
-
 cap_iab_t cap_iab_get_pid(pid_t pid);
-
 int cap_iab_set_proc(cap_iab_t iab);
-
 char *cap_iab_to_text(cap_iab_t iab);
-
 cap_iab_t cap_iab_from_text(const char *text);
-
 cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec,
     cap_value_t val);
-
 int cap_iab_compare(cap_iab_t a, cap_iab_t b);
-
 int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t val,
     cap_flag_value_t enable);
-
 int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec,
     cap_t set, cap_flag_t flag);
-
+char *cap_proc_root(const char *root);
 .fi
 .sp
 Link with \fI\-lcap\fP.
@@ -88,6 +83,11 @@ cap_iab_t should be freed with
 returns a copy of the IAB value for the specified process.  The returned
 cap_iab_t should be freed with
 .BR cap_free (3).
+This function defaults to searching
+.BR /proc/ <PID> /status
+for the IAB information, but that location can be overridden using the
+.BR cap_proc_root ()
+function.
 .sp
 .BR cap_iab_set_proc ()
 can be used to set the IAB value carried by the current process. Such
@@ -137,6 +137,19 @@ of the IAB tuple. Copying into Amb in this way may implicitly raise Inh
 values in the IAB tuple. Similarly copying into the Inh vector may
 implicitly lower Amb values that are not present in the resulting Inh
 vector.
+.sp
+.BR cap_proc_root ()
+can be used to determine the current location queried by
+.BR cap_iab_get_pid ().
+Returned values should be released with
+.BR cap_free (3).
+If the argument to
+.BR cap_proc_root ()
+is not \fBNULL\fP, a copy of it will become the replacement for
+.BR /proc .
+Note, this function is \fInot\fP thread safe with respect to
+concurrent calls to
+.BR cap_iab_get_pid ().
 .SH "ERRORS"
 The functions returning \fIcap_iab_t\fP values or allocated memory in
 the form of a string return NULL on error.
diff --git a/doc/cap_launch.3 b/doc/cap_launch.3
index 95313ec..2d186eb 100644
--- a/doc/cap_launch.3
+++ b/doc/cap_launch.3
@@ -1,10 +1,15 @@
 .TH CAP_LAUNCH 3 "2021-08-01" "" "Linux Programmer's Manual"
 .SH NAME
+cap_new_launcher, cap_func_launcher, cap_launcher_callback, \
+cap_launcher_set_mode, cap_launcher_set_iab, cap_launcher_set_chroot, \
+cap_launch, cap_launcher_setuid, cap_launcher_setgroups \
+\- libcap launch functionality
+.SH SYNOPSYS
 .nf
 #include <sys/capability.h>
 
-cap_launch_t cap_new_launcher(const char *arg0, const char * const *argv,
-    const char * const *envp);
+cap_launch_t cap_new_launcher(const char *arg0, const char *const *argv,
+    const char *const *envp);
 
 cap_launch_t cap_func_launcher(int (callback_fn)(void *detail));
 
@@ -77,9 +82,9 @@ outside the main process of the calling application. An example of
 this would be to allocate detail as follows:
 .nf
 
-   const *char[] args = { "echo", "hello", NULL };
+   const char *args[] = { "echo", "hello", NULL };
    cap_launch_t cmd = cap_new_launcher("/usr/bin/echo", args, NULL);
-   int *detail = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, 
+   int *detail = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
                       MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    cap_launcher_callback(cmd, &answer_detail_fn);
    *detail = 41;
diff --git a/doc/cap_prctl.3 b/doc/cap_prctl.3
new file mode 100644
index 0000000..65ea3e4
--- /dev/null
+++ b/doc/cap_prctl.3
@@ -0,0 +1 @@
+.so man3/cap_get_proc.3
diff --git a/doc/cap_prctlw.3 b/doc/cap_prctlw.3
new file mode 100644
index 0000000..65ea3e4
--- /dev/null
+++ b/doc/cap_prctlw.3
@@ -0,0 +1 @@
+.so man3/cap_get_proc.3
diff --git a/doc/cap_proc_root.3 b/doc/cap_proc_root.3
new file mode 100644
index 0000000..3e730b1
--- /dev/null
+++ b/doc/cap_proc_root.3
@@ -0,0 +1 @@
+.so man3/cap_iab.3
diff --git a/doc/cap_set_nsowner.3 b/doc/cap_set_nsowner.3
new file mode 100644
index 0000000..3970c34
--- /dev/null
+++ b/doc/cap_set_nsowner.3
@@ -0,0 +1 @@
+.so man3/cap_get_file.3
diff --git a/doc/cap_set_syscall.3 b/doc/cap_set_syscall.3
new file mode 100644
index 0000000..48a44fa
--- /dev/null
+++ b/doc/cap_set_syscall.3
@@ -0,0 +1 @@
+.so man3/libcap.3
diff --git a/doc/capability.notes b/doc/capability.md
similarity index 51%
rename from doc/capability.notes
rename to doc/capability.md
index 4087c80..cfad4c0 100644
--- a/doc/capability.notes
+++ b/doc/capability.md
@@ -1,5 +1,12 @@
-Overview
---------
+# Notes concerning wider use of capabilities
+
+## Overview
+
+**NOTE** These notes were added to the libcap package in
+libcap-1.03. They pre-date file capability support, but fully
+anticipate it. They are some thoughts on how to restructure a system
+to better leverage capability support. I've updated them to render as
+an `.md` formatted file.
 
 As of Linux 2.2.0, the power of the superuser has been partitioned
 into a set of discrete capabilities (in other places, these
@@ -11,48 +18,46 @@ can be protected (with wrappers) or rewritten to take advantage of
 this fine grained approach to constraining the danger to your system
 from programs running as 'root'.
 
-Notes on securing your system
------------------------------
+## Notes on securing your system
 
-Adopting a role approach to system security:
+### Adopting a role approach to system security
 
-changing all of the system binaries and directories to be owned by
+Changing all of the system binaries and directories to be owned by
 some user that cannot log on. You might like to create a user with
 the name 'system' who's account is locked with a '*' password. This
 user can be made the owner of all of the system directories on your
 system and critical system binaries too.
 
-Why is this a good idea? In a simple case, the CAP_FUSER capability is
-required for the superuser to delete files owned by a non-root user in
-a 'sticky-bit' protected non-root owned directory. Thus, the sticky
-bit can help you protect the /lib/ directory from an compromized
+Why is this a good idea? In a simple case, the `CAP_FOWNER` capability
+is required for the superuser to delete files owned by a non-root user
+in a _sticky-bit_ protected non-root owned directory. Thus, the sticky
+bit can help you protect the `/lib/` directory from a compromized
 daemon where the directory and the files it contains are owned by the
-system user. It can be protected by using a wrapper like execcap to
-ensure that the daemon is not running with the CAP_FUSER capability...
+system user. It can be protected to ensure that the daemon is not
+running with the `CAP_FOWNER` capability...
 
-
-Limiting the damage:
+### Limiting the damage
 
 If your daemon only needs to be setuid-root in order to bind to a low
 numbered port. You should restrict it to only having access to the
-CAP_NET_BIND_SERVICE capability. Coupled with not having any files on
-the system owned by root, it becomes significantly harder for such a
-daemon to damage your system.
+`CAP_NET_BIND_SERVICE` capability. Coupled with not having any files
+on the system owned by root, it becomes significantly harder for such
+a daemon to damage your system.
 
 Note, you should think of this kind of trick as making things harder
 for a potential attacker to exploit a hole in a daemon of this
 type. Being able to bind to any privileged port is still a formidable
-privilege and can lead to difficult but 'interesting' man in the
-middle attacks -- hijack the telnet port for example and masquerade as
-the login program... Collecting passwords for another day.
-
+privilege and can lead to difficult but _interesting_
+man-in-the-middle attacks -- hijack the telnet port for example and
+masquerade as the login program... Collecting passwords for another
+day.
 
-The /proc/ filesystem:
+### The /proc/ filesystem
 
 This Linux-specific directory tree holds most of the state of the
 system in a form that can sometimes be manipulated by file
 read/writes.  Take care to ensure that the filesystem is not mounted
 with uid=0, since root (with no capabilities) would still be able to
-read sensitive files in the /proc/ tree - kcore for example.
+read sensitive files in the `/proc/` tree - `kcore` for example.
 
 [Patch is available for 2.2.1 - I just wrote it!]
diff --git a/doc/crosslink.sh b/doc/crosslink.sh
new file mode 100755
index 0000000..d701522
--- /dev/null
+++ b/doc/crosslink.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# So many cross links to maintain. Here is a script that I've used to
+# validate things at least conform to some structure:
+#
+for x in *.? ; do
+    y=$(grep -F '.so m' ${x} | awk '{print $2}' | sed -e 's/man..//')
+    if [ -z "${y}" ]; then
+	continue
+    fi
+    echo
+    echo "###########"
+    echo "${x} => ${y}"
+    grep -F "${x%.*}" "${y}"
+done
diff --git a/doc/libcap.3 b/doc/libcap.3
index c1ae4ba..a91cf7e 100644
--- a/doc/libcap.3
+++ b/doc/libcap.3
@@ -1,15 +1,21 @@
-.TH LIBCAP 3 "2021-03-06" "" "Linux Programmer's Manual"
+.TH LIBCAP 3 "2022-10-16" "" "Linux Programmer's Manual"
 .SH NAME
 cap_clear, cap_clear_flag, cap_compare, cap_copy_ext, cap_copy_int, \
-cap_free, cap_from_name, cap_from_text, cap_get_fd, cap_get_file, \
-cap_get_flag, cap_get_pid, cap_get_proc, cap_set_fd, cap_set_file, \
-cap_set_flag, cap_set_proc, cap_size, cap_to_name, cap_to_text, \
-cap_get_pid, cap_dup \- capability data object manipulation
+cap_drop_bound, cap_dup, cap_fill, cap_fill_flag, cap_free, cap_from_name, \
+cap_from_text, cap_get_ambient, cap_get_bound, cap_get_fd, \
+cap_get_file, cap_get_flag, cap_get_mode, cap_get_nsowner, cap_get_pid, \
+cap_get_pid, cap_get_proc, cap_get_secbits, cap_init, cap_max_bits, \
+cap_prctl, cap_prctlw, cap_proc_root, cap_reset_ambient, \
+cap_set_ambient, cap_set_fd, cap_set_file, cap_set_flag, cap_setgroups, \
+cap_set_mode, cap_set_nsowner, cap_set_proc, cap_set_secbits, \
+cap_setuid, cap_size, cap_to_name, cap_to_text \- capability data object manipulation
 .SH SYNOPSIS
 .nf
 #include <sys/capability.h>
 
 int cap_clear(cap_t cap_p);
+int cap_fill(cap_t cap_p, cap_flag_t to, cap_flag_t from);
+int cap_fill_flag(cap_t cap_p, cap_flag_t to, const cap_t ref, cap_flag_t from);
 int cap_clear_flag(cap_t cap_p, cap_flag_t flag);
 int cap_compare(cap_t cap_a, cap_t cap_b);
 ssize_t cap_copy_ext(void *ext_p, cap_t cap_p, ssize_t size);
@@ -36,18 +42,42 @@ ssize_t cap_size(cap_t cap_p);
 char *cap_to_name(cap_value_t cap);
 char *cap_to_text(cap_t caps, ssize_t *length_p);
 cap_t cap_get_pid(pid_t pid);
+cap_t cap_init();
 cap_t cap_dup(cap_t cap_p);
+
+char *cap_proc_root(const char *root);
+int cap_get_nsowner(cap_t cap_p);
+int cap_set_nsowner(cap_t cap_p, uid_t rootuid);
+int cap_get_bound(cap_value_t cap);
+int cap_drop_bound(cap_value_t cap);
+int cap_get_ambient(cap_value_t cap);
+int cap_set_ambient(cap_value_t cap, cap_flag_value_t value);
+int cap_reset_ambient(void);
+int cap_set_mode(cap_mode_t flavor);
+cap_mode_t cap_get_mode(void);
+const char *cap_mode_name(cap_mode_t flavor);
+unsigned cap_get_secbits();
+int cap_set_secbits(unsigned bits);
+int cap_prctl(long int pr_cmd, long int arg1, long int arg2, long int arg3,
+              long int arg4, long int arg5);
+int cap_prctlw(long int pr_cmd, long int arg1, long int arg2, long int arg3,
+               long int arg4, long int arg5);
+int cap_setuid(uid_t uid);
+int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]);
 .fi
 .sp
 Link with \fI\-lcap\fP.
 .fi
 .SH DESCRIPTION
-These functions work on a capability state held in working storage.
+These primary functions work on a capability state held in working
+storage and attempt to complete the POSIX.1e (draft) user space API
+for Capability based privilege.
+.PP
 A
 .I cap_t
 holds information about the capabilities in each of the three sets,
-Permitted, Inheritable, and Effective.
-Each capability in a set may be clear (disabled, 0) or set (enabled, 1).
+Permitted, Inheritable, and Effective.  Each capability in a set may
+be clear (disabled, 0) or set (enabled, 1).
 .PP
 These functions work with the following data types:
 .TP 18
@@ -80,10 +110,65 @@ is set appropriately.
 These functions are as per the withdrawn POSIX.1e draft specification.
 The following functions are Linux extensions:
 .BR cap_clear_flag (),
+.BR cap_drop_bound (),
+.BR cap_fill (),
+.BR cap_fill_flag (),
 .BR cap_from_name (),
-.BR cap_to_name (),
+.BR cap_get_ambient (),
+.BR cap_get_bound (),
+.BR cap_get_mode (),
+.BR cap_get_nsowner (),
+.BR cap_get_secbits (),
+.BR cap_mode_name (),
+.BR cap_proc_root (),
+.BR cap_prctl (),
+.BR cap_prctlw (),
+.BR cap_reset_ambient (),
+.BR cap_setgroups (),
+.BR cap_setuid (),
+.BR cap_set_ambient (),
+.BR cap_set_mode (),
+.BR cap_set_nsowner (),
+.BR cap_set_secbits (),
+.BR cap_to_name ()
 and
 .BR cap_compare ().
+.PP
+A Linux, \fIIAB\fP, extension of Inheritable, Bounding and Ambient
+tuple capability vectors are also supported by \fBlibcap\fP. Those
+functions are described in a companion man page:
+.BR cap_iab (3).
+Further, for managing the complexity of launching a sub-process,
+\fBlibcap\fP supports the abstraction:
+.BR cap_launch (3).
+.PP
+In addition to the \fBcap_\fP prefixed \fBlibcap\fP API, the library
+also provides prototypes for the Linux system calls that provide the
+native API for process capabilities. These prototypes are:
+.sp
+.nf
+int capget(cap_user_header_t header, cap_user_data_t data);
+int capset(cap_user_header_t header, const cap_user_data_t data);
+.fi
+.sp
+Further, \fBlibcap\fP provides a set-up function,
+.sp
+.nf
+void cap_set_syscall(
+        long int (*new_syscall)(long int, long int, long int, long int),
+        long int (*new_syscall6)(long int,
+                                 long int, long int, long int,
+                                 long int, long int, long int));
+.fi
+.sp
+which can be used to redirect its use of the
+.BR capset ()
+and other system calls that write kernel managed state. This is
+especially useful when supporting POSIX semantics for security
+state. When a program is linked against
+.BR libpsx (3)
+as described in that man page, this function is used to connect
+\fBlibcap\fP to POSIX semantics system calls.
 .SH "REPORTING BUGS"
 The
 .B libcap
@@ -99,9 +184,15 @@ https://bugzilla.kernel.org/buglist.cgi?component=libcap&list_id=1090757
 .BR cap_from_text (3),
 .BR cap_get_file (3),
 .BR cap_get_proc (3),
+.BR cap_iab (3),
 .BR cap_init (3),
+.BR cap_launch (3),
 .BR capabilities (7),
 .BR getpid (2),
-.BR capsh (1)
+.BR capsh (1),
+.BR captree (8),
+.BR getcap (8),
+.BR getpcaps (8),
+.BR setcap (8)
 and
 .BR libpsx (3).
diff --git a/doc/libpsx.3 b/doc/libpsx.3
index d117ae8..4a0b5b6 100644
--- a/doc/libpsx.3
+++ b/doc/libpsx.3
@@ -5,9 +5,17 @@ psx_syscall3, psx_syscall6, psx_set_sensitivity \- POSIX semantics for system ca
 .nf
 #include <sys/psx_syscall.h>
 
-long int psx_syscall3(long int syscall_nr, long int arg1, long int arg2, long int arg3);
-long int psx_syscall6(long int syscall_nr, long int arg1, long int arg2, long int arg3, long int arg4, long int arg5, long int arg6);
+long int psx_syscall3(long int syscall_nr,
+                      long int arg1, long int arg2, long int arg3);
+long int psx_syscall6(long int syscall_nr,
+                      long int arg1, long int arg2, long int arg3,
+                      long int arg4, long int arg5, long int arg6);
 int psx_set_sensitivity(psx_sensitivity_t sensitivity);
+void psx_load_syscalls(long int (**syscall_fn)(long int,
+                                    long int, long int, long int),
+                       long int (**syscall6_fn)(long int,
+                                    long int, long int, long int,
+                                    long int, long int, long int));
 .fi
 .sp
 Link with one of these:
@@ -22,7 +30,7 @@ library attempts to fill a gap left by the
 .BR pthreads (7)
 implementation on Linux. To be compliant POSIX threads, via the
 .BR nptl "(7) " setxid
-mechanism glibc maintains consistent UID and GID credentials amongst
+mechanism, glibc maintains consistent UID and GID credentials amongst
 all of the threads associated with the current process. However, other
 credential state is not supported by this abstraction. To support
 these extended kernel managed security attributes,
@@ -35,10 +43,12 @@ mechanism, the coordination of thread state is mediated by a realtime
 signal. Whereas the
 .B nptl:setxid
 mechanism uses signo=33 (which is hidden by glibc below a redefined
-SIGRTMIN),
-.B libpsx
-inserts itself in the SIGSYS handler stack. It goes to great length to
-be the first such handler but acts as a pass-through for other SIGSYS
+.BR SIGRTMIN "), " libpsx
+inserts itself in the
+.B SIGSYS
+handler stack. It goes to great length to be the first such handler
+but acts as a pass-through for other
+.B SIGSYS
 uses.
 .PP
 A linker trick of
@@ -52,7 +62,9 @@ can keep track of all pthreads.
 An inefficient macrology trick supports the
 .BR psx_syscall ()
 pseudo function which takes 1 to 7 arguments, depending on the needs
-of the caller. The macrology pads out the call to actually use
+of the caller. The macrology (which ultimately invokes
+.BR __psx_syscall ())
+pads out the call to actually use
 .BR psx_syscall3 ()
 or
 .BR psx_syscall6 ()
@@ -74,6 +86,21 @@ prints a stderr notification about how the results differ; and
 prints the error details and generates a
 .B SIGSYS
 signal.
+.PP
+.BR psx_load_syscalls ()
+can be used to set caller defined function pointers for invoking 3 and
+6 argument syscalls. This function can be used to configure a library,
+or program to change behavior when linked against
+.BR libpsx .
+Indeed,
+.B libcap
+uses this function from
+.B libpsx
+to override its thread scoped default system call based API. When
+linked with
+.BR libpsx ", " libcap
+can operate on all the threads of a multithreaded program to operate
+with POSIX semantics.
 .SH RETURN VALUE
 The return value for system call functions is generally the value
 returned by the kernel, or \-1 in the case of an error. In such cases
diff --git a/doc/mkmd.sh b/doc/mkmd.sh
index 39beac9..ce8baa2 100755
--- a/doc/mkmd.sh
+++ b/doc/mkmd.sh
@@ -50,6 +50,14 @@ function do_page () {
 cat > "${index}" <<EOF
 # Manpages for libcap and libpsx
 
+EOF
+
+if [[ -f "local-md.preamble" ]]; then
+    cat "local-md.preamble" >> "${index}"
+fi
+
+cat >> "${index}" <<EOF
+
 ## Individual reference pages
 EOF
 
@@ -69,6 +77,14 @@ cat >> "${index}" <<EOF
 
 ## More information
 
+EOF
+
+if [[ -f "local-md.postscript" ]]; then
+    cat "local-md.postscript" >> "${index}"
+fi
+
+cat >> "${index}" <<EOF
+
 For further information, see the
 [FullyCapable](https://sites.google.com/site/fullycapable/) homepage
 for libcap.
diff --git a/doc/psx_load_syscalls.3 b/doc/psx_load_syscalls.3
new file mode 100644
index 0000000..663420c
--- /dev/null
+++ b/doc/psx_load_syscalls.3
@@ -0,0 +1 @@
+.so man3/libpsx.3
diff --git a/doc/values/3.txt b/doc/values/3.txt
index 8a605c2..2d68efd 100644
--- a/doc/values/3.txt
+++ b/doc/values/3.txt
@@ -2,3 +2,7 @@ Allows a process to perform operations on files, even
 where file owner ID should otherwise need be equal to
 the UID, except where CAP_FSETID is applicable. It
 doesn't override MAC and DAC restrictions.
+
+This capability permits the deletion of a file owned
+by another UID in a directory protected by the sticky
+(t) bit.
diff --git a/go/compare-cap.go b/go/compare-cap.go
index 5e489e5..064d5fa 100644
--- a/go/compare-cap.go
+++ b/go/compare-cap.go
@@ -116,16 +116,18 @@ func tryFileCaps() {
 	if err := want.SetFd(f); err != nil {
 		log.Fatalf("failed to fset file capability: %v", err)
 	}
-	if err := saved.SetProc(); err != nil {
-		log.Fatalf("failed to lower effective capability: %v", err)
-	}
-	// End of critical section.
-
 	if got, err := cap.GetFd(f); err != nil {
 		log.Fatalf("failed to fread caps: %v", err)
 	} else if is, was := got.String(), want.String(); is != was {
 		log.Fatalf("fread file caps do not match desired: got=%q want=%q", is, was)
 	}
+	if err := empty.SetFd(f); err != nil && err != syscall.ENODATA {
+		log.Fatalf("blocked from cleanup fremoving filecaps: %v", err)
+	}
+	if err := saved.SetProc(); err != nil {
+		log.Fatalf("failed to lower effective capability: %v", err)
+	}
+	// End of critical section.
 }
 
 // tryProcCaps performs a set of convenience functions and compares
diff --git a/go/go.mod b/go/go.mod
index ac949c5..378b218 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -3,6 +3,6 @@ module main
 go 1.11
 
 require (
-	kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
-	kernel.org/pub/linux/libs/security/libcap/psx v1.2.66
+	kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
+	kernel.org/pub/linux/libs/security/libcap/psx v1.2.69
 )
diff --git a/go/mknames.go b/go/mknames.go
index ff07218..ef348ae 100644
--- a/go/mknames.go
+++ b/go/mknames.go
@@ -52,8 +52,8 @@ func main() {
 
 /* ** DO NOT EDIT THIS FILE. IT WAS AUTO-GENERATED BY LIBCAP'S GO BUILDER (mknames.go) ** */
 
-// NamedCount holds the number of capability values with official
-// names known at the time this libcap/cap version, was released. The
+// NamedCount holds the number of capability values, with official
+// names, known at the time this libcap/cap version was released. The
 // "../libcap/cap" package is fully able to manipulate higher numbered
 // capability values by numerical value. However, if you find
 // cap.NamedCount < cap.MaxBits(), it is probably time to upgrade this
diff --git a/goapps/captrace/go.mod b/goapps/captrace/go.mod
index 51a3b74..9817252 100644
--- a/goapps/captrace/go.mod
+++ b/goapps/captrace/go.mod
@@ -2,4 +2,4 @@ module captrace
 
 go 1.16
 
-require kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
diff --git a/goapps/captree/captree.go b/goapps/captree/captree.go
index 5313cb4..7768b11 100644
--- a/goapps/captree/captree.go
+++ b/goapps/captree/captree.go
@@ -448,7 +448,7 @@ func main() {
 	}
 
 	var noted []string
-	for pid, _ := range wanted {
+	for pid := range wanted {
 		noted = append(noted, pid)
 	}
 	sort.Slice(noted, func(i, j int) bool {
diff --git a/goapps/captree/go.mod b/goapps/captree/go.mod
index 327826e..09e579c 100644
--- a/goapps/captree/go.mod
+++ b/goapps/captree/go.mod
@@ -2,4 +2,4 @@ module captree
 
 go 1.16
 
-require kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
diff --git a/goapps/gowns/go.mod b/goapps/gowns/go.mod
index 455bbee..0e867ed 100644
--- a/goapps/gowns/go.mod
+++ b/goapps/gowns/go.mod
@@ -2,4 +2,4 @@ module gowns
 
 go 1.15
 
-require kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
diff --git a/goapps/setid/go.mod b/goapps/setid/go.mod
index 6741535..09595b4 100644
--- a/goapps/setid/go.mod
+++ b/goapps/setid/go.mod
@@ -3,6 +3,6 @@ module setid
 go 1.11
 
 require (
-	kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
-	kernel.org/pub/linux/libs/security/libcap/psx v1.2.66
+	kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
+	kernel.org/pub/linux/libs/security/libcap/psx v1.2.69
 )
diff --git a/goapps/web/README b/goapps/web/README
deleted file mode 100644
index cbabd5d..0000000
--- a/goapps/web/README
+++ /dev/null
@@ -1,18 +0,0 @@
-This sample program needs to be built as follows (when built with Go
-prior to 1.15):
-
-   CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*" go build web.go
-
-go1.15+ does not require the CGO_LDFLAGS_ALLOW variable and can build
-this code with
-
-   go build web.go
-
-A more complete walk through of what this code does is provided here:
-
-   https://sites.google.com/site/fullycapable/getting-started-with-go/building-go-programs-that-manipulate-capabilities
-
-Go compilers prior to go1.11.13 are not expected to work. Report more
-recent issues to:
-
-   https://bugzilla.kernel.org/buglist.cgi?component=libcap&list_id=1065141&product=Tools&resolution=---
diff --git a/goapps/web/README.md b/goapps/web/README.md
new file mode 100644
index 0000000..970d10e
--- /dev/null
+++ b/goapps/web/README.md
@@ -0,0 +1,28 @@
+# Web serving with/without privilege
+
+## Building
+
+This sample program needs to be built as follows (when built with Go
+prior to 1.15):
+```
+   export CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*"
+   go mod tidy
+   go build web.go
+```
+go1.15+ does not require the `CGO_LDFLAGS_ALLOW` environment variable
+and can build this code with:
+```
+   go mod tidy
+   go build web.go
+```
+
+## Further discussion
+
+A more complete walk through of what this code does is provided on the
+[Fully Capable
+website](https://sites.google.com/site/fullycapable/getting-started-with-go/building-go-programs-that-manipulate-capabilities).
+
+## Reporting bugs
+
+Go compilers prior to go1.11.13 are not expected to work. Report more
+recent issues to the [`libcap` bug tracker](https://bugzilla.kernel.org/buglist.cgi?component=libcap&list_id=1065141&product=Tools&resolution=---).
diff --git a/goapps/web/go.mod b/goapps/web/go.mod
index 0318099..054357b 100644
--- a/goapps/web/go.mod
+++ b/goapps/web/go.mod
@@ -2,4 +2,4 @@ module web
 
 go 1.11
 
-require kernel.org/pub/linux/libs/security/libcap/cap v1.2.66
+require kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
diff --git a/goapps/web/web.go b/goapps/web/web.go
index c96e745..5f9c5cb 100644
--- a/goapps/web/web.go
+++ b/goapps/web/web.go
@@ -13,6 +13,8 @@
 // package - go versions prior to 1.15 need some environment variable
 // workarounds):
 //
+//   go mod init web
+//   go mod tidy
 //   go build web.go
 //   sudo setcap cap_setpcap,cap_net_bind_service=p web
 //   ./web --port=80
diff --git a/kdebug/test-kernel.sh b/kdebug/test-kernel.sh
index 0962ce5..391bb8a 100755
--- a/kdebug/test-kernel.sh
+++ b/kdebug/test-kernel.sh
@@ -13,8 +13,8 @@ function die {
 }
 
 pushd ..
-make all test || die "failed to make all test of libcap tree"
-make -C progs tcapsh-static || die "failed to make progs/tcapsh-static"
+make LIBCSTATIC=yes all test || die "failed to make all test of libcap tree"
+make LIBCSTATIC=yes -C progs tcapsh-static || die "failed to make progs/tcapsh-static"
 make -C tests uns_test
 popd
 
@@ -62,7 +62,7 @@ if [ -f "$HERE/interactive" ]; then
     echo "file /root/interactive $HERE/interactive 0755 0 0" >> fs.conf
 fi
 
-COMMANDS="awk cat chmod cp dmesg fgrep id less ln ls mkdir mount pwd rm rmdir sh sort umount uniq vi"
+COMMANDS="awk cat chmod cp dmesg grep id less ln ls mkdir mount pwd rm rmdir sh sort umount uniq vi"
 for f in $COMMANDS; do
     echo slink /bin/$f /sbin/busybox 0755 0 0 >> fs.conf
 done
diff --git a/libcap/cap_alloc.c b/libcap/cap_alloc.c
index c826e7a..504abd2 100644
--- a/libcap/cap_alloc.c
+++ b/libcap/cap_alloc.c
@@ -17,7 +17,8 @@ static __u8 __libcap_mutex;
  */
 static cap_value_t _cap_max_bits;
 
-__attribute__((constructor (300))) void _libcap_initialize()
+__attribute__((visibility ("hidden")))
+__attribute__((constructor (300))) void _libcap_initialize(void)
 {
     int errno_saved = errno;
     _cap_mu_lock(&__libcap_mutex);
@@ -95,7 +96,7 @@ cap_t cap_init(void)
  * This is an internal library function to duplicate a string and
  * tag the result as something cap_free can handle.
  */
-char *_libcap_strdup(const char *old)
+__attribute__((visibility ("hidden"))) char *_libcap_strdup(const char *old)
 {
     struct _cap_alloc_s *header;
     char *raw_data;
@@ -105,15 +106,17 @@ char *_libcap_strdup(const char *old)
 	errno = EINVAL;
 	return NULL;
     }
-    len = strlen(old) + 1 + 2*sizeof(__u32);
-    if (len < sizeof(struct _cap_alloc_s)) {
-	len = sizeof(struct _cap_alloc_s);
-    }
-    if ((len & 0xffffffff) != len) {
+
+    len = strlen(old);
+    if ((len & 0x3fffffff) != len) {
 	_cap_debug("len is too long for libcap to manage");
 	errno = EINVAL;
 	return NULL;
     }
+    len += 1 + 2*sizeof(__u32);
+    if (len < sizeof(struct _cap_alloc_s)) {
+	len = sizeof(struct _cap_alloc_s);
+    }
 
     raw_data = calloc(1, len);
     if (raw_data == NULL) {
diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c
index 0ce5db7..24bc274 100644
--- a/libcap/cap_proc.c
+++ b/libcap/cap_proc.c
@@ -363,7 +363,7 @@ static int _cap_reset_ambient(struct syscaller_s *sc)
  * case where the set is empty already but the ambient cap API is
  * locked.
  */
-int cap_reset_ambient()
+int cap_reset_ambient(void)
 {
     return _cap_reset_ambient(&multithread);
 }
diff --git a/libcap/execable.c b/libcap/execable.c
index 9d3ae7f..9f7062e 100644
--- a/libcap/execable.c
+++ b/libcap/execable.c
@@ -18,6 +18,7 @@ static void summary(void)
     printf("\nCurrent mode: %s\n", cap_mode_name(mode));
     printf("Number of cap values known to: this libcap=%d, running kernel=%d\n",
 	   CAP_LAST_CAP+1, bits);
+
     if (bits > CAP_LAST_CAP+1) {
 	printf("=> Consider upgrading libcap to name:");
 	for (c = CAP_LAST_CAP+1; c < bits; c++) {
@@ -30,6 +31,8 @@ static void summary(void)
 	    printf(" %s", name);
 	    cap_free(name);
 	}
+    } else {
+	return;
     }
     printf("\n");
 }
diff --git a/libcap/execable.h b/libcap/execable.h
index fee17b4..7a2d247 100644
--- a/libcap/execable.h
+++ b/libcap/execable.h
@@ -93,7 +93,8 @@ static void __execable_parse_args(int *argc_p, char ***argv_p)
  */
 #define SO_MAIN							\
 static void __execable_main(int, char**);			\
-extern void __so_start(void);					\
+__attribute__((visibility ("hidden")))                          \
+void __so_start(void);					        \
 __SO_FORCE_ARG_ALIGNMENT					\
 void __so_start(void)						\
 {								\
diff --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h
index 6205598..2db9972 100644
--- a/libcap/include/sys/capability.h
+++ b/libcap/include/sys/capability.h
@@ -18,7 +18,7 @@ extern "C" {
  * Provide a programmatic way to #ifdef around features.
  */
 #define LIBCAP_MAJOR 2
-#define LIBCAP_MINOR 66
+#define LIBCAP_MINOR 69
 
 /*
  * This file complements the kernel file by providing prototype
diff --git a/pam_cap/.gitignore b/pam_cap/.gitignore
index 87f4186..dac617b 100644
--- a/pam_cap/.gitignore
+++ b/pam_cap/.gitignore
@@ -4,3 +4,4 @@ test_pam_cap
 lazylink.so
 pam_cap_linkopts
 LIBCAP
+incapable.conf
diff --git a/pam_cap/License b/pam_cap/License
index e88aa3f..6c20dc0 100644
--- a/pam_cap/License
+++ b/pam_cap/License
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR LGPL-2.0-or-later */
+
 Unless otherwise *explicitly* stated the following text describes the
 licensed conditions under which the contents of this module release
 may be distributed:
diff --git a/pam_cap/Makefile b/pam_cap/Makefile
index d852101..258e519 100644
--- a/pam_cap/Makefile
+++ b/pam_cap/Makefile
@@ -70,13 +70,16 @@ test_pam_cap: test_pam_cap.c pam_cap.c ../libcap/libcap.a
 testlink: test.o pam_cap.o
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lpam -ldl $(LIBCAPLIB)
 
-test: testlink test_pam_cap pam_cap.so
+incapable.conf:
+	echo "^cap_setuid  alpha" > $@ && chmod o+w $@
+
+test: testlink test_pam_cap pam_cap.so incapable.conf
 	./test_pam_cap
 	LD_LIBRARY_PATH=../libcap ./pam_cap.so
 	LD_LIBRARY_PATH=../libcap ./pam_cap.so --help
 	@echo "module can be run as an executable!"
 
-sudotest: test_pam_cap
+sudotest: test_pam_cap incapable.conf
 	$(SUDO) ./test_pam_cap root 0x0 0x0 0x0 config=./capability.conf
 	$(SUDO) ./test_pam_cap root 0x0 0x0 0x0 config=./sudotest.conf
 	$(SUDO) ./test_pam_cap alpha 0x0 0x0 0x0 config=./capability.conf
@@ -87,4 +90,4 @@ sudotest: test_pam_cap
 
 clean:
 	rm -f *.o *.so testlink lazylink.so test_pam_cap pam_cap_linkopts *~
-	rm -f LIBCAP
+	rm -f LIBCAP incapable.conf
diff --git a/pam_cap/execable.c b/pam_cap/execable.c
index f826a57..17276b4 100644
--- a/pam_cap/execable.c
+++ b/pam_cap/execable.c
@@ -26,7 +26,7 @@ SO_MAIN(int argc, char **argv)
     printf(
 	"%s (version " LIBCAP_VERSION ") is a PAM module to specify\n"
 	"inheritable (IAB) capabilities via the libpam authentication\n"
-	"abstraction. See the libcap License file for licensing information.\n"
+	"abstraction. See the pam_cap License file for licensing information.\n"
 	"\n"
 	"Release notes and feature documentation for libcap and pam_cap.so\n"
 	"can be found at:\n"
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
index 7e8cade..b9419cb 100644
--- a/pam_cap/pam_cap.c
+++ b/pam_cap/pam_cap.c
@@ -12,6 +12,7 @@
 #endif
 
 #include <errno.h>
+#include <fcntl.h>
 #include <grp.h>
 #include <limits.h>
 #include <pwd.h>
@@ -22,6 +23,7 @@
 #include <syslog.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <linux/limits.h>
 
@@ -106,6 +108,27 @@ static char *read_capabilities_for_user(const char *user, const char *source)
 	D(("failed to open capability file"));
 	goto defer;
     }
+    /*
+     * In all cases other than "/dev/null", the config file should not
+     * be world writable. We do not check for ownership limitations or
+     * group write restrictions as these represent legitimate local
+     * administration choices. Especially in a system operating in
+     * CAP_MODE_PURE1E.
+     */
+    if (strcmp(source, "/dev/null") != 0) {
+	struct stat sb;
+	D(("validate filehandle [for opened %s] does not point to a world"
+	   " writable file", source));
+	if (fstat(fileno(cap_file), &sb) != 0) {
+	    D(("unable to fstat config file: %d", errno));
+	    goto close_out_file;
+	}
+	if ((sb.st_mode & S_IWOTH) != 0) {
+	    D(("open failed [%s] is world writable test: security hole",
+	       source));
+	    goto close_out_file;
+	}
+    }
 
     int found_one = 0;
     while (!found_one &&
@@ -167,6 +190,7 @@ static char *read_capabilities_for_user(const char *user, const char *source)
 	line = NULL;
     }
 
+close_out_file:
     fclose(cap_file);
 
 defer:
@@ -290,7 +314,12 @@ static int set_capabilities(struct pam_cap_s *cs)
 
     if (cs->defer) {
 	D(("configured to delay applying IAB"));
-	pam_set_data(cs->pamh, "pam_cap_iab", iab, iab_apply);
+	int ret = pam_set_data(cs->pamh, "pam_cap_iab", iab, iab_apply);
+	if (ret != PAM_SUCCESS) {
+	    D(("unable to cache capabilities for delayed setting: %d", ret));
+	    /* since ok=0, the module will return PAM_IGNORE */
+	    cap_free(iab);
+	}
 	iab = NULL;
     } else if (!cap_iab_set_proc(iab)) {
 	D(("able to set the IAB [%s] value", conf_caps));
@@ -390,7 +419,7 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags,
     }
 
     if (retval != PAM_SUCCESS) {
-	D(("pam_get_user failed: %s", pam_strerror(pamh, retval)));
+	D(("pam_get_user failed: pam error=%d", retval));
 	memset(&pcs, 0, sizeof(pcs));
 	return PAM_AUTH_ERR;
     }
diff --git a/pam_cap/test_pam_cap.c b/pam_cap/test_pam_cap.c
index 886888e..4bcf236 100644
--- a/pam_cap/test_pam_cap.c
+++ b/pam_cap/test_pam_cap.c
@@ -237,10 +237,21 @@ int main(int argc, char *argv[]) {
 	printf("failed to parse arguments\n");
 	exit(1);
     }
-    if (read_capabilities_for_user("morgan", "/dev/null") != NULL) {
+    if (read_capabilities_for_user("alpha", "/dev/null") != NULL) {
 	printf("/dev/null should return no capabilities\n");
 	exit(1);
     }
+    if (read_capabilities_for_user("unknown", "capability.conf") != NULL) {
+	printf("capability.conf should return no capabilities for unknown\n");
+	exit(1);
+    }
+    char *iab_text = read_capabilities_for_user("alpha", "./incapable.conf");
+    if (iab_text != NULL) {
+	printf("./incapable.conf should grant no capabilities: got=%s\n",
+	       iab_text);
+	free(iab_text);
+	exit(1);
+    }
 
     /*
      * Start out with a cleared inheritable set.
diff --git a/progs/Makefile b/progs/Makefile
index 2cb7520..80f890a 100644
--- a/progs/Makefile
+++ b/progs/Makefile
@@ -14,11 +14,18 @@ ifeq ($(DYNAMIC),yes)
 LDPATH = LD_LIBRARY_PATH=../libcap
 DEPS = ../libcap/libcap.so
 else
-# For this build variant override the LDFLAGS to link statically from
-# libraries within the build tree. If you never want this, use
-# make DYNAMIC=yes ...
+ifeq ($(LIBCSTATIC),yes)
 LDFLAGS = --static
 DEPS = ../libcap/libcap.a
+else
+# For this build variant override the LDFLAGS to link statically from
+# libraries within the build tree. If you never want this, use make
+# DYNAMIC=yes . Note, we can't reliably link statically against glibc
+# becasuse of https://sourceware.org/bugzilla/show_bug.cgi?id=12491 .
+LDFLAGS = -Wl,-Bstatic
+LDFLAGS_SUFFIX = -Wl,-Bdynamic
+DEPS = ../libcap/libcap.a
+endif
 endif
 
 ../libcap/libcap.a:
@@ -28,7 +35,7 @@ endif
 	$(MAKE) -C ../libcap libcap.so
 
 $(BUILD): %: %.o $(DEPS)
-	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBCAPLIB)
+	$(CC) $(CFLAGS) $(LDFLAGS) $< $(LIBCAPLIB) $(LDFLAGS_SUFFIX) -o $@
 
 %.o: %.c $(INCS)
 	$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
@@ -50,7 +57,7 @@ capshdoc.c.cf: capshdoc.c ./mkcapshdoc.sh
 	diff -u capshdoc.c $@ || (rm $@ ; exit 1)
 
 capsh: capsh.c capshdoc.c.cf capshdoc.h $(DEPS)
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(CAPSH_SHELL) $(LDFLAGS) -o $@ $< capshdoc.c $(LIBCAPLIB)
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(CAPSH_SHELL) $(LDFLAGS) $< capshdoc.c $(LIBCAPLIB) $(LDFLAGS_SUFFIX) -o $@
 
 # Statically linked with minimal linkage flags to enable running in a
 # chroot and in other in-tree testing contexts.
diff --git a/progs/capshdoc.c b/progs/capshdoc.c
index ee7e974..5560ef9 100644
--- a/progs/capshdoc.c
+++ b/progs/capshdoc.c
@@ -30,6 +30,10 @@ static const char *explanation3[] = {  /* cap_fowner = 3 */
     "where file owner ID should otherwise need be equal to",
     "the UID, except where CAP_FSETID is applicable. It",
     "doesn't override MAC and DAC restrictions.",
+    "",
+    "This capability permits the deletion of a file owned",
+    "by another UID in a directory protected by the sticky",
+    "(t) bit.",
     NULL
 };
 static const char *explanation4[] = {  /* cap_fsetid = 4 */
diff --git a/progs/mkcapshdoc.sh b/progs/mkcapshdoc.sh
index 8421685..d2ee4bd 100755
--- a/progs/mkcapshdoc.sh
+++ b/progs/mkcapshdoc.sh
@@ -15,7 +15,7 @@ EOF
 
 let x=0
 while [ -f "../doc/values/${x}.txt" ]; do
-    name=$(fgrep ",${x}}" ../libcap/cap_names.list.h|sed -e 's/{"//' -e 's/",/ = /' -e 's/},//')
+    name=$(grep -F ",${x}}" ../libcap/cap_names.list.h|sed -e 's/{"//' -e 's/",/ = /' -e 's/},//')
     echo "static const char *explanation${x}[] = {  /* ${name} */"
     sed -e 's/"/\\"/g' -e 's/^/    "/' -e 's/$/",/' "../doc/values/${x}.txt"
     let x=1+${x}
diff --git a/progs/quicktest.sh b/progs/quicktest.sh
index 776b175..59e16b0 100755
--- a/progs/quicktest.sh
+++ b/progs/quicktest.sh
@@ -256,7 +256,7 @@ rm -f nsprivileged
 cp ./tcapsh-static ./nsprivileged && /bin/chmod -s ./nsprivileged
 ./setcap -n 1 all=ep ./nsprivileged
 if [ $? -eq 0 ]; then
-    ./getcap -n ./nsprivileged | fgrep "[rootid=1]"
+    ./getcap -n ./nsprivileged | grep -F "[rootid=1]"
     if [ $? -ne 0 ]; then
 	echo "FAILED setting ns rootid on file"
 	exit 1
@@ -283,6 +283,7 @@ if [ -f ../go/compare-cap ]; then
 	grep "skipping file cap tests"
     if [ $? -eq 0 ]; then
 	echo "FAILED not engaging file cap tests"
+	exit 1
     fi
     echo "PASSED"
 else
diff --git a/psx/License b/psx/License
index 2645a87..39108c2 100644
--- a/psx/License
+++ b/psx/License
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */
+
 Unless otherwise *explicitly* stated, the following text describes the
 licensed conditions under which the contents of this libcap/psx release
 may be used and distributed.
diff --git a/psx/psx.c b/psx/psx.c
index d9c0485..65eb2aa 100644
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -516,7 +516,7 @@ int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
     pthread_sigmask(SIG_BLOCK, &sigbit, NULL);
 
     int ret = __real_pthread_create(thread, attr, _psx_start_fn, starter);
-    if (ret == -1) {
+    if (ret > 0) {
 	psx_new_state(_PSX_CREATE, _PSX_IDLE);
 	memset(starter, 0, sizeof(*starter));
 	free(starter);
diff --git a/psx/psx_test.go b/psx/psx_test.go
index 4b90f63..40a543f 100644
--- a/psx/psx_test.go
+++ b/psx/psx_test.go
@@ -2,6 +2,7 @@ package psx
 
 import (
 	"runtime"
+	"sync"
 	"syscall"
 	"testing"
 )
@@ -41,9 +42,65 @@ func killAThread(c <-chan struct{}) {
 	<-c
 }
 
+// Test state is mirrored as expected.
+func TestShared(t *testing.T) {
+	const prGetKeepCaps = 7
+	const prSetKeepCaps = 8
+
+	var wg sync.WaitGroup
+
+	newTracker := func() chan<- uintptr {
+		ch := make(chan uintptr)
+		go func() {
+			runtime.LockOSThread()
+			defer wg.Done()
+			tid := syscall.Gettid()
+			for {
+				if _, ok := <-ch; !ok {
+					break
+				}
+				val, ok := <-ch
+				if !ok {
+					break
+				}
+				got, _, e := Syscall3(syscall.SYS_PRCTL, prGetKeepCaps, 0, 0)
+				if e != 0 {
+					t.Fatalf("[%d] psx:prctl(GET_KEEPCAPS) ?= %d failed: %v", tid, val, syscall.Errno(e))
+				}
+				if got != val {
+					t.Errorf("[%d] bad keepcaps value: got=%d, want=%d", tid, got, val)
+				}
+				if _, ok := <-ch; !ok {
+					break
+				}
+			}
+		}()
+		return ch
+	}
+
+	var tracked []chan<- uintptr
+	for i := 0; i <= 10; i++ {
+		val := uintptr(i & 1)
+		if _, _, e := Syscall3(syscall.SYS_PRCTL, prSetKeepCaps, val, 0); e != 0 {
+			t.Fatalf("[%d] psx:prctl(SET_KEEPCAPS, %d) failed: %v", i, i&1, syscall.Errno(e))
+		}
+		wg.Add(1)
+		tracked = append(tracked, newTracker())
+		for _, ch := range tracked {
+			ch <- 2   // start serialization.
+			ch <- val // definitely written after change.
+			ch <- 3   // end serialization.
+		}
+	}
+	for _, ch := range tracked {
+		close(ch)
+	}
+	wg.Wait()
+}
+
 // Test to confirm no regression against:
 //
-//   https://github.com/golang/go/issues/42494
+//	https://github.com/golang/go/issues/42494
 func TestThreadChurn(t *testing.T) {
 	const prSetKeepCaps = 8
 

More details

Full run details

Historical runs