Skip to contentSkip to conversation

unix.7: Socket Options and SCM_* fixes

PATCH Superseded Günther Noack 4 participants 38 messages Oct 21 – Nov 3 Man-pages: manual pages for Linux -- Sections 2, 3, 4, 5, and 7

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover unix.7: Socket Options and SCM_* fixes

Hello!

This is patch set (B) spun off from the "Documentation fixes in unix(7)" patch set [1], which is now split into these three parts:

(A) Miscellaneous fixes, mostly in unix.7, one in socket.7
(B) Socket options and Socket-level Control Messages (SCM_*) <-- this one
(C) sock_diag.7

This patch set fixes unix(7) documentation related to socket options and SCM_* control messages. The first 5 commits are fixes and factual corrections, the last two commits are documenting two relevant new features (PIDFD passing and SO_PASSRIGHTS).

The patch set is based on the "miscellaneous" patch set (A) ("General Unix Domain Socket fixes"), since it is touching some error code documentation in the same corner as what is getting changed in (A).

Thank you for your reviews on v1 and v2 of the still-joined patch set. Compared to v2, the following things happened:

* Some commit message rewording (especially quoted the kernel code more directly).
* I removed the commit that changed the wording about Out-of-Band messaging, per Kuniyuki's suggestion in [2].
* The ETOOMANYREFS commit is a SCM_RIGHTS-specific error; the Autobind documentation change is included here because it refers to SO_PASSPIDFD.
* I am not 100% happy with how verbose the SCM_SECURITY change has become, but I don't see a good way to shorten it without cutting away from the substance.

More details are attached in the commit notes.

This patch set has been produced with the help of a LLM, which was helpful for spotting a long list of inaccuracies, comparing the documentation with the kernel implementation. (More details in [1])

–Günther

P.S.: Part (C) will come soon, but not today any more. :)
That part is more independent again and, being about "sock_diag", it is outside of the main unix(7) documentation.

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/CAAVpQUBh+J-aBLbkpzT7qmt7B714O529HWcTuQsVLT4wLBnDJA@mail.gmail.com/

Günther Noack (7):
man/man7/unix.7: ERRORS: ETOOMANYREFS: Fix the conditions man/man7/unix.7: Multiple same-typed SCM_* messages may be sent in one
message
man/man7/unix.7: Autobind: Clarify when it happens, and mention
SO_PASSPIDFD
man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just
SELinux
man/man7/unix.7: SO_PASSSEC: wfix
man/man7/unix.7: Document SO_PASSPIDFD, SO_PEERPIDFD, and SCM_PIDFD man/man7/unix.7: Document SO_PASSRIGHTS

 man/man7/unix.7 | 215 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 201 insertions(+), 14 deletions(-)
Signature · 1 line
2.56.0
#1 lore raw

PATCH 1/7 man/man7/unix.7: ERRORS: ETOOMANYREFS: Fix the conditions

The ETOOMANYREFS error is detected in too_many_unix_fds() in net/unix/af_unix.c when the user's (not the processes') in-flight FDs exceed RLIMIT_NOFILE, and the process has neither CAP_SYS_RESOURCE nor CAP_SYS_ADMIN. The error is raised when all of the following (simplified) conditions are true:

* user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)
* !capable(CAP_SYS_RESOURCE)
* !capable(CAP_SYS_ADMIN)

The check was introduced in linux.git 712f4aad406b (2016-01-11; "unix:
properly account for FDs passed over unix sockets").

Fixes dc4eea68b565 (2016-12-17; "unix.7: Document ETOOMANYREFS for SCM_RIGHTS send exceeding RLIMIT_NOFILE limit")
Cc Michael Kerrisk <[email protected]>
Cc Willy Tarreau <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
Double checked in code -- the condition under which this error is returned was not fully spelled out.

 man/man7/unix.7 | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
man/man7/unix.7 +6 -2
index 79198445c581..0d2ea8bda2ae 100644
@@ -785,10 +785,14 @@ when sending a file descriptor as ancillary data over
 a UNIX domain socket (see the description of
 .BR SCM_RIGHTS ,
 above).
-It occurs if the number of "in-flight" file descriptors exceeds the
+It occurs if the number of "in-flight" file descriptors
+sent by processes of the caller's real user ID
+exceeds the caller's
 .B RLIMIT_NOFILE
-resource limit and the caller does not have the
+resource limit and the caller has neither the
 .B CAP_SYS_RESOURCE
+nor the
+.B CAP_SYS_ADMIN
 capability.
 An in-flight file descriptor is one that has been sent using
 .BR sendmsg (2)
Signature · 1 line
2.56.0
maintainer Reviewed-by on v1 1/7
#2 lore raw

Re: [PATCH 1/7] man/man7/unix.7: ERRORS: ETOOMANYREFS: Fix the conditions

Hi Günther

Quoted 16 lines

Date: 2026-10-21 23:22:50+0200
From: Günther Noack <[email protected]>

The ETOOMANYREFS error is detected in too_many_unix_fds() in net/unix/af_unix.c when the user's (not the processes') in-flight FDs exceed RLIMIT_NOFILE, and the process has neither CAP_SYS_RESOURCE nor CAP_SYS_ADMIN. The error is raised when all of the following (simplified) conditions are true:

* user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)
* !capable(CAP_SYS_RESOURCE)
* !capable(CAP_SYS_ADMIN)

The check was introduced in linux.git 712f4aad406b (2016-01-11; "unix:
properly account for FDs passed over unix sockets").

Quoted 7 lines
Fixes dc4eea68b565 (2016-12-17; "unix.7: Document ETOOMANYREFS for SCM_RIGHTS send exceeding RLIMIT_NOFILE limit")
Cc Michael Kerrisk <[email protected]>
Cc Willy Tarreau <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

man/man7/unix.7 when sending a file descriptor as ancillary data over

Quoted 13 lines

Notes:
Double checked in code -- the condition under which this error is returned was not fully spelled out.

 man/man7/unix.7 | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
man/man7/unix.7 +0 -0
index 79198445c581..0d2ea8bda2ae 100644
@@ -785,10 +785,14 @@ when sending a file descriptor as ancillary data over

Would you mind having a look at CONTRIBUTING.d/git for improving the hunk context?

    git-diff(1), gitattributes(5)
	To produce useful hunk contexts in manual pages, we need to hack
	git(1)'s idea of a function name, and also to tell git what is a
	manual page.

		$ git config --global diff.man.xfuncname '^\.S[SHsh] .*$';
		$ mkdir -p ~/.config/git/;
		$ echo '*.[0-9]* diff=man' >>~/.config/git/attributes;

Cheers,
Alex

Quoted 20 lines
man/man7/unix.7 +6 -2
 a UNIX domain socket (see the description of
 .BR SCM_RIGHTS ,
 above).
-It occurs if the number of "in-flight" file descriptors exceeds the
+It occurs if the number of "in-flight" file descriptors
+sent by processes of the caller's real user ID
+exceeds the caller's
 .B RLIMIT_NOFILE
-resource limit and the caller does not have the
+resource limit and the caller has neither the
 .B CAP_SYS_RESOURCE
+nor the
+.B CAP_SYS_ADMIN
 capability.
 An in-flight file descriptor is one that has been sent using
 .BR sendmsg (2)
Signature · 1 line
2.56.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#3 lore raw

Re: [PATCH 1/7] man/man7/unix.7: ERRORS: ETOOMANYREFS: Fix the conditions

Quoted 9 lines · Alejandro Colomar

man/man7/unix.7 when sending a file descriptor as ancillary data over

Quoted 5 lines
man/man7/unix.7 +0 -0
index 79198445c581..0d2ea8bda2ae 100644
@@ -785,10 +785,14 @@ when sending a file descriptor as ancillary data over

Would you mind having a look at CONTRIBUTING.d/git for improving the hunk context?

Quoted 8 lines
    git-diff(1), gitattributes(5)
	To produce useful hunk contexts in manual pages, we need to hack
	git(1)'s idea of a function name, and also to tell git what is a
	manual page.

		$ git config --global diff.man.xfuncname '^\.S[SHsh] .*$';
		$ mkdir -p ~/.config/git/;
		$ echo '*.[0-9]* diff=man' >>~/.config/git/attributes;

Nice trick! Applied.

–Günther

Reviewed-by on v1 1/7
#4 lore raw

Re: [PATCH 1/7] man/man7/unix.7: ERRORS: ETOOMANYREFS: Fix the conditions

In reply to Günther Noack
Quoted 13 lines · Günther Noack

The ETOOMANYREFS error is detected in too_many_unix_fds() in net/unix/af_unix.c when the user's (not the processes') in-flight FDs exceed RLIMIT_NOFILE, and the process has neither CAP_SYS_RESOURCE nor CAP_SYS_ADMIN. The error is raised when all of the following (simplified) conditions are true:

* user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)
* !capable(CAP_SYS_RESOURCE)
* !capable(CAP_SYS_ADMIN)

The check was introduced in linux.git 712f4aad406b (2016-01-11; "unix:
properly account for FDs passed over unix sockets").

Quoted 6 lines
Fixes dc4eea68b565 (2016-12-17; "unix.7: Document ETOOMANYREFS for SCM_RIGHTS send exceeding RLIMIT_NOFILE limit")
Cc Michael Kerrisk <[email protected]>
Cc Willy Tarreau <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM
#5 lore raw

PATCH 2/7 man/man7/unix.7: Multiple same-typed SCM_* messages may be sent in one message

In reply to Günther Noack

Verified in Linux kernel code:

__scm_send() in linux.git net/core/scm.c walks all control messages in a loop like this:

	for_each_cmsghdr(cmsg, msg) {
		// ...
		switch (cmsg->cmsg_type)
		{
		case SCM_RIGHTS:
			// Append(!) file descriptors
			// up to the SCM_MAX_FD limit
			break;
		case SCM_CREDENTIALS:
			// Replace previously set credentials
			break;
		default:
			goto error;
		}
	}

So it is possible to pass multiple SCM_* messages -- they will compose if possible and override the previous value if not. Since their contents are being conflated that way, they will appear as one message again on the receiving side.

Keep the recommendation as portability advice, and describe the Linux behavior.

Fixes 5b5cb1958062 (2018-07-10; "unix.7: When sending ancillary data, only one item of each type may be sent")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
The factual analysis in the commit message is correct.

I kept the LLM phrasing; it did not seem overly wordy to me and the style is in line with the paragraph after it, which also documents Linux-only vs. portable APIs that way.

 man/man7/unix.7 | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
man/man7/unix.7 +18 -1
index 0d2ea8bda2ae..a1dce8c47246 100644
@@ -543,7 +543,24 @@ option must be enabled on the socket (see above).
 .P
 When sending ancillary data with
 .BR sendmsg (2),
-only one item of each of the above types may be included in the sent message.
+portable applications should include
+only one item of each of the above types in the sent message.
+On Linux,
+if several
+.B SCM_RIGHTS
+items are included,
+their file descriptors are combined into a single list,
+to which the
+.B SCM_MAX_FD
+limit applies as a whole,
+and the receiver gets them in a single
+.B SCM_RIGHTS
+item;
+if several
+.B SCM_CREDENTIALS
+items are included,
+each of them is checked,
+and the last one takes effect.
 .P
 At least one byte of real data should be sent when sending ancillary data.
 On Linux, this is required to successfully send ancillary data over
Signature · 1 line
2.56.0
#6 lore raw

Re: [PATCH 2/7] man/man7/unix.7: Multiple same-typed SCM_* messages may be sent in one message

Hi Günther,

Quoted 4 lines

Date: 2026-10-21 23:22:51+0200
From: Günther Noack <[email protected]>

Verified in Linux kernel code:

I think 'verified' in a commit message seems inappropriate. It should describe, rather than verify, and the verification would come only from the review tags. Maybe say something like "Relevant kernel code:".

Quoted 13 lines

__scm_send() in linux.git net/core/scm.c walks all control messages in a loop like this:

	for_each_cmsghdr(cmsg, msg) {
		// ...
		switch (cmsg->cmsg_type)
		{
		case SCM_RIGHTS:
			// Append(!) file descriptors
			// up to the SCM_MAX_FD limit
			break;
		case SCM_CREDENTIALS:
			// Replace previously set credentials

Since these are your comments, and not actual comments in the source code (are they yours, or from the LLM?), I'd format them clearly as such using invalid code rather than comments:

... replace previously set credentials ...

Quoted 48 lines
			break;
		default:
			goto error;
		}
	}

So it is possible to pass multiple SCM_* messages -- they will compose if possible and override the previous value if not. Since their contents are being conflated that way, they will appear as one message again on the receiving side.

Keep the recommendation as portability advice, and describe the Linux behavior.

Fixes 5b5cb1958062 (2018-07-10; "unix.7: When sending ancillary data, only one item of each type may be sent")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
The factual analysis in the commit message is correct.

I kept the LLM phrasing; it did not seem overly wordy to me and the style is in line with the paragraph after it, which also documents Linux-only vs. portable APIs that way.

 man/man7/unix.7 | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
man/man7/unix.7 +8 -1
index 0d2ea8bda2ae..a1dce8c47246 100644
@@ -543,7 +543,24 @@ option must be enabled on the socket (see above).
 .P
 When sending ancillary data with
 .BR sendmsg (2),
-only one item of each of the above types may be included in the sent message.
+portable applications should include
+only one item of each of the above types in the sent message.
+On Linux,
+if several
+.B SCM_RIGHTS
+items are included,
+their file descriptors are combined into a single list,
+to which the

man/man7/unix.7 option must be enabled on the socket (see above). · line 563

Quoted 10 lines
man/man7/unix.7 +10 -0
+.B SCM_MAX_FD
+limit applies as a whole,
+and the receiver gets them in a single
+.B SCM_RIGHTS
+item;
+if several
+.B SCM_CREDENTIALS
+items are included,
+each of them is checked,
+and the last one takes effect.

Is 'takes effect' appropriate? I think 'is sent' might be more appropriate, right? There's nothing taking effect, I think (but I'm not an expert in SCM_* stuff).

Have a lovely night!
Alex

Quoted 6 lines
man/man7/unix.7 +0 -0
 .P
 At least one byte of real data should be sent when sending ancillary data.
 On Linux, this is required to successfully send ancillary data over
Signature · 1 line
2.56.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#7 lore raw

Re: [PATCH 2/7] man/man7/unix.7: Multiple same-typed SCM_* messages may be sent in one message

Hello Alejandro!

Quoted 8 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-21 23:22:51+0200
From: Günther Noack <[email protected]>

Verified in Linux kernel code:

I think 'verified' in a commit message seems inappropriate. It should describe, rather than verify, and the verification would come only from the review tags. Maybe say something like "Relevant kernel code:".

Good point, replaced. I'll double check the other commits as well.

Quoted 14 lines
Quoted 13 lines

__scm_send() in linux.git net/core/scm.c walks all control messages in a loop like this:

	for_each_cmsghdr(cmsg, msg) {
		// ...
		switch (cmsg->cmsg_type)
		{
		case SCM_RIGHTS:
			// Append(!) file descriptors
			// up to the SCM_MAX_FD limit
			break;
		case SCM_CREDENTIALS:
			// Replace previously set credentials
Quoted 5 lines

Since these are your comments, and not actual comments in the source code (are they yours, or from the LLM?), I'd format them clearly as such using invalid code rather than comments:

... replace previously set credentials ...

Done.

Yes, I transcribed the source code by hand here, including these comments.

Quoted 54 lines
Quoted 54 lines
			break;
		default:
			goto error;
		}
	}

So it is possible to pass multiple SCM_* messages -- they will compose if possible and override the previous value if not. Since their contents are being conflated that way, they will appear as one message again on the receiving side.

Keep the recommendation as portability advice, and describe the Linux behavior.

Fixes 5b5cb1958062 (2018-07-10; "unix.7: When sending ancillary data, only one item of each type may be sent")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
The factual analysis in the commit message is correct.

I kept the LLM phrasing; it did not seem overly wordy to me and the style is in line with the paragraph after it, which also documents Linux-only vs. portable APIs that way.

 man/man7/unix.7 | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
man/man7/unix.7 +14 -1
index 0d2ea8bda2ae..a1dce8c47246 100644
@@ -543,7 +543,24 @@ option must be enabled on the socket (see above).
 .P
 When sending ancillary data with
 .BR sendmsg (2),
-only one item of each of the above types may be included in the sent message.
+portable applications should include
+only one item of each of the above types in the sent message.
+On Linux,
+if several
+.B SCM_RIGHTS
+items are included,
+their file descriptors are combined into a single list,
+to which the
+.B SCM_MAX_FD
+limit applies as a whole,
+and the receiver gets them in a single
+.B SCM_RIGHTS
+item;
+if several
Quoted 8 lines

man/man7/unix.7 option must be enabled on the socket (see above). · line 563

Quoted 4 lines
man/man7/unix.7 +4 -0
+.B SCM_CREDENTIALS
+items are included,
+each of them is checked,
+and the last one takes effect.

Is 'takes effect' appropriate? I think 'is sent' might be more appropriate, right? There's nothing taking effect, I think (but I'm not an expert in SCM_* stuff).

You are right, "takes effect" is maybe too much described from the angle of the implementation - I replaced it with "is sent".

What's happening is that the sender process specifies the "struct ucred", which contains a pid, uid and gid in the SCM_CREDENTIALS control message. When the kernel deals with these, it *checks* that they are valid (they describe the sending process *OR* the sending process is privileged and can send what it wants). Only the last of these SCM_CREDENTIALS messages survives that processing though and the receiving process then only sees that last one.

Thanks for the review!
–Günther

#8 lore raw

PATCH 3/7 man/man7/unix.7: Autobind: Clarify when it happens, and mention SO_PASSPIDFD

In reply to Günther Noack

Autobind happens if either one of SO_PASSCRED/SO_PASSPIDFD is set and either connect() or sendmsg() happens. Mention SO_PASSPIDFD and clarify that autobind does not happen immediately when setting these socket options.

In linux.git net/unix/af_unix.c, unix_autobind() is called by unix_dgram_connect(), unix_stream_connect() and unix_dgram_sendmsg(), if the socket has no address and unix_may_passcred() is true.

unix_may_passcred() is also true when SO_PASSPIDFD is set, which was added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD"), Linux 6.5.

Also, the old text said "not yet connected", but the condition is about the socket not being bound.

Fixes 9d1fd1157306 (1999-06-15, 2022-12-19; "man-pages 1.24")
Fixes 0cf2caa41ea6 (2010-10-10; "unix.7: Document the autobind feature")
Cc Michael Kerrisk <[email protected]>
Cc Alexander Mikhalitsyn <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
Rephrased the LLM-generated text so that the canonical description is in the "Autobind feature" section and the other place just refers to it.

SO_PASSPIDFD is unfortunately not documented yet at this point in time, but it's still correct to mention it here.

 man/man7/unix.7 | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
man/man7/unix.7 +20 -5
index a1dce8c47246..ded82768322b 100644
@@ -301,8 +301,14 @@ if the sender did not specify
 .B SCM_CREDENTIALS
 ancillary data.
 .IP
-When this option is set and the socket is not yet connected,
-a unique name in the abstract namespace will be generated automatically.
+When this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
 .IP
 The value given as an argument to
 .BR setsockopt (2)
@@ -370,10 +376,19 @@ call specifies
 as
 .IR sizeof(sa_family_t) ,
 .\" i.e., sizeof(short)
-or the
+or if
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on a socket that was
+not explicitly bound to an address
+and that has the
 .B SO_PASSCRED
-socket option was specified for a socket that was
-not explicitly bound to an address,
+or
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89
+.B SO_PASSPIDFD
+(since Linux 6.5)
+socket option enabled,
 then the socket is autobound to an abstract address.
 The address consists of a null byte
 followed by 5 bytes in the character set
Signature · 1 line
2.56.0
#9 lore raw

Re: [PATCH 3/7] man/man7/unix.7: Autobind: Clarify when it happens, and mention SO_PASSPIDFD

Quoted 64 lines · Günther Noack

Autobind happens if either one of SO_PASSCRED/SO_PASSPIDFD is set and either connect() or sendmsg() happens. Mention SO_PASSPIDFD and clarify that autobind does not happen immediately when setting these socket options.

In linux.git net/unix/af_unix.c, unix_autobind() is called by unix_dgram_connect(), unix_stream_connect() and unix_dgram_sendmsg(), if the socket has no address and unix_may_passcred() is true.

unix_may_passcred() is also true when SO_PASSPIDFD is set, which was added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD"), Linux 6.5.

Also, the old text said "not yet connected", but the condition is about the socket not being bound.

Fixes 9d1fd1157306 (1999-06-15, 2022-12-19; "man-pages 1.24")
Fixes 0cf2caa41ea6 (2010-10-10; "unix.7: Document the autobind feature")
Cc Michael Kerrisk <[email protected]>
Cc Alexander Mikhalitsyn <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
Rephrased the LLM-generated text so that the canonical description is in the "Autobind feature" section and the other place just refers to it.

SO_PASSPIDFD is unfortunately not documented yet at this point in time, but it's still correct to mention it here.

 man/man7/unix.7 | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
man/man7/unix.7 +10 -3
index a1dce8c47246..ded82768322b 100644
@@ -301,8 +301,14 @@ if the sender did not specify
 .B SCM_CREDENTIALS
 ancillary data.
 .IP
-When this option is set and the socket is not yet connected,
-a unique name in the abstract namespace will be generated automatically.
+When this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
 .IP
 The value given as an argument to
 .BR setsockopt (2)
@@ -370,10 +376,19 @@ call specifies
 as
 .IR sizeof(sa_family_t) ,
 .\" i.e., sizeof(short)
-or the
+or if
+.BR connect (2)

man/man7/unix.7 call specifies · line 388

Quoted 10 lines
man/man7/unix.7 +7 -2
+or
+.BR sendmsg (2)
+is called on a socket that was
+not explicitly bound to an address
+and that has the
 .B SO_PASSCRED
-socket option was specified for a socket that was
-not explicitly bound to an address,
+or
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Do we really need this SHA1 here ?

man/man7/unix.7 call specifies · line 391

Quoted 3 lines
man/man7/unix.7 +3 -0
+.B SO_PASSPIDFD
+(since Linux 6.5)
+socket option enabled,

You will need to update here with SO_PASSPIDFD_THREAD,
https://lore.kernel.org/netdev/[email protected]/

but I'm not sure to what extent you want to clarify things.

For example, is "the socket is autobound when it passes credentials or pidfd but is not yet bound." too vague ?

Quoted 6 lines
man/man7/unix.7 +0 -0
 then the socket is autobound to an abstract address.
 The address consists of a null byte
 followed by 5 bytes in the character set
Signature · 1 line
2.56.0
#10 lore raw

Re: [PATCH 3/7] man/man7/unix.7: Autobind: Clarify when it happens, and mention SO_PASSPIDFD

Quoted 69 lines · Kuniyuki Iwashima
Quoted 68 lines · Günther Noack

Autobind happens if either one of SO_PASSCRED/SO_PASSPIDFD is set and either connect() or sendmsg() happens. Mention SO_PASSPIDFD and clarify that autobind does not happen immediately when setting these socket options.

In linux.git net/unix/af_unix.c, unix_autobind() is called by unix_dgram_connect(), unix_stream_connect() and unix_dgram_sendmsg(), if the socket has no address and unix_may_passcred() is true.

unix_may_passcred() is also true when SO_PASSPIDFD is set, which was added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD"), Linux 6.5.

Also, the old text said "not yet connected", but the condition is about the socket not being bound.

Fixes 9d1fd1157306 (1999-06-15, 2022-12-19; "man-pages 1.24")
Fixes 0cf2caa41ea6 (2010-10-10; "unix.7: Document the autobind feature")
Cc Michael Kerrisk <[email protected]>
Cc Alexander Mikhalitsyn <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
Rephrased the LLM-generated text so that the canonical description is in the "Autobind feature" section and the other place just refers to it.

SO_PASSPIDFD is unfortunately not documented yet at this point in time, but it's still correct to mention it here.

 man/man7/unix.7 | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
man/man7/unix.7 +14 -3
index a1dce8c47246..ded82768322b 100644
@@ -301,8 +301,14 @@ if the sender did not specify
 .B SCM_CREDENTIALS
 ancillary data.
 .IP
-When this option is set and the socket is not yet connected,
-a unique name in the abstract namespace will be generated automatically.
+When this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
 .IP
 The value given as an argument to
 .BR setsockopt (2)
@@ -370,10 +376,19 @@ call specifies
 as
 .IR sizeof(sa_family_t) ,
 .\" i.e., sizeof(short)
-or the
+or if
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on a socket that was
+not explicitly bound to an address
Quoted 8 lines

man/man7/unix.7 call specifies · line 388

Quoted 6 lines
man/man7/unix.7 +3 -2
+and that has the
 .B SO_PASSCRED
-socket option was specified for a socket that was
-not explicitly bound to an address,
+or
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Do we really need this SHA1 here ?

I'll drop it, since it's already pointed out in the commit message. It seemed like the convention in these man pages, but apparently it's not needed any more, as Alejandro also remarked in [1].

[1] https://lore.kernel.org/all/atulJkR7fAgyKhPP@debian/

Quoted 6 lines

man/man7/unix.7 call specifies · line 391

Quoted 3 lines
man/man7/unix.7 +3 -0
+.B SO_PASSPIDFD
+(since Linux 6.5)
+socket option enabled,

You will need to update here with SO_PASSPIDFD_THREAD,
https://lore.kernel.org/netdev/[email protected]/

SO_PASSPIDFD_THREAD did not make it into Linux 7.3, and man pages document stable features only, so it will have to go separately at a later point in time.

Quoted 4 lines

but I'm not sure to what extent you want to clarify things.

For example, is "the socket is autobound when it passes credentials or pidfd but is not yet bound." too vague ?

In my ears, "passing credentials" might as well mean that the current credentials are being *sent* over the socket, rather than received. We could maybe say "when it is configured to receive credentials or pidfd"? (But at that point, it's similarly long and less precise than just listing the socket options.) I'd keep it as is for now, if that's OK.

–Günther

#11 lore raw

Re: [PATCH 3/7] man/man7/unix.7: Autobind: Clarify when it happens, and mention SO_PASSPIDFD

Quoted 103 lines · Günther Noack
Quoted 69 lines · Kuniyuki Iwashima
Quoted 68 lines · Günther Noack

Autobind happens if either one of SO_PASSCRED/SO_PASSPIDFD is set and either connect() or sendmsg() happens. Mention SO_PASSPIDFD and clarify that autobind does not happen immediately when setting these socket options.

In linux.git net/unix/af_unix.c, unix_autobind() is called by unix_dgram_connect(), unix_stream_connect() and unix_dgram_sendmsg(), if the socket has no address and unix_may_passcred() is true.

unix_may_passcred() is also true when SO_PASSPIDFD is set, which was added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD"), Linux 6.5.

Also, the old text said "not yet connected", but the condition is about the socket not being bound.

Fixes 9d1fd1157306 (1999-06-15, 2022-12-19; "man-pages 1.24")
Fixes 0cf2caa41ea6 (2010-10-10; "unix.7: Document the autobind feature")
Cc Michael Kerrisk <[email protected]>
Cc Alexander Mikhalitsyn <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
Rephrased the LLM-generated text so that the canonical description is in the "Autobind feature" section and the other place just refers to it.

SO_PASSPIDFD is unfortunately not documented yet at this point in time, but it's still correct to mention it here.

 man/man7/unix.7 | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
man/man7/unix.7 +14 -3
index a1dce8c47246..ded82768322b 100644
@@ -301,8 +301,14 @@ if the sender did not specify
 .B SCM_CREDENTIALS
 ancillary data.
 .IP
-When this option is set and the socket is not yet connected,
-a unique name in the abstract namespace will be generated automatically.
+When this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
 .IP
 The value given as an argument to
 .BR setsockopt (2)
@@ -370,10 +376,19 @@ call specifies
 as
 .IR sizeof(sa_family_t) ,
 .\" i.e., sizeof(short)
-or the
+or if
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on a socket that was
+not explicitly bound to an address
Quoted 8 lines

man/man7/unix.7 call specifies · line 388

Quoted 6 lines
man/man7/unix.7 +3 -2
+and that has the
 .B SO_PASSCRED
-socket option was specified for a socket that was
-not explicitly bound to an address,
+or
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Do we really need this SHA1 here ?

I'll drop it, since it's already pointed out in the commit message. It seemed like the convention in these man pages, but apparently it's not needed any more, as Alejandro also remarked in [1].

[1] https://lore.kernel.org/all/atulJkR7fAgyKhPP@debian/

Quoted 6 lines

man/man7/unix.7 call specifies · line 391

Quoted 3 lines
man/man7/unix.7 +3 -0
+.B SO_PASSPIDFD
+(since Linux 6.5)
+socket option enabled,

You will need to update here with SO_PASSPIDFD_THREAD,
https://lore.kernel.org/netdev/[email protected]/

SO_PASSPIDFD_THREAD did not make it into Linux 7.3, and man pages document stable features only, so it will have to go separately at a later point in time.

Quoted 4 lines

but I'm not sure to what extent you want to clarify things.

For example, is "the socket is autobound when it passes credentials or pidfd but is not yet bound." too vague ?

Quoted 6 lines

In my ears, "passing credentials" might as well mean that the current credentials are being *sent* over the socket, rather than received. We could maybe say "when it is configured to receive credentials or pidfd"? (But at that point, it's similarly long and less precise than just listing the socket options.) I'd keep it as is for now, if that's OK.

Fair enough :)

#12 lore raw

PATCH 4/7 man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

In reply to Günther Noack

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +19 -6
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;
+the length of the data is given by the
+.I cmsg_len
+field.
+There is no fixed limit on the length of the security context;
+allocating
 .B NAME_MAX
-bytes in the data portion of the ancillary message for this data.
+bytes in the data portion of the ancillary message
+is usually enough.
+If the buffer is too small,
+the data is truncated and the
+.B MSG_CTRUNC
+flag is set (see below).
 .IP
 To receive the security context, the
 .B SO_PASSSEC
Signature · 1 line
2.56.0
#13 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Günther,

Quoted 18 lines

Date: 2026-10-21 23:22:53+0200
From: Günther Noack <[email protected]>

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

...

Quoted 13 lines
Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

...

Quoted 26 lines

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +1 -3
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.

man/man7/unix.7 message, the · line 554

Quoted 10 lines
man/man7/unix.7 +7 -2
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;

This seems to contradict the commit message. The commit message says that the string is terminated, but that the reported length might or might not be off-by-one (i.e., not count or count the '\0').

This text, however, is saying that in some cases there might not be a '\0' at all.

Am I missing something?

Have a lovely day!
Alex

Quoted 20 lines
man/man7/unix.7 +11 -1
+the length of the data is given by the
+.I cmsg_len
+field.
+There is no fixed limit on the length of the security context;
+allocating
 .B NAME_MAX
-bytes in the data portion of the ancillary message for this data.
+bytes in the data portion of the ancillary message
+is usually enough.
+If the buffer is too small,
+the data is truncated and the
+.B MSG_CTRUNC
+flag is set (see below).
 .IP
 To receive the security context, the
 .B SO_PASSSEC
Signature · 1 line
2.56.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#14 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello Alejandro!

Quoted 15 lines · Alejandro Colomar
Quoted 15 lines

Date: 2026-10-21 23:22:53+0200
From: Günther Noack <[email protected]>

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

Quoted 5 lines
Quoted 3 lines

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

...

Ah, I can see that this is confusing; let me reformulate that without mentioning this strlen() implementation detail. Smack returns a buffer with a given size *without* including the null byte, roughly like this (simplified):

  int len = strlen(skp->smk_known);
  /* populate */
  cp->context = skp->smk_known;
  cp->len = len;    <-- does not span the null byte

Reformulating to:

...but smack_to_secctx() in security/smack/smack_lsm.c sends a security context that does not end in a null byte.

Quoted 57 lines
Quoted 13 lines
Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

...

Quoted 26 lines

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +1 -3
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.

man/man7/unix.7 message, the · line 554

Quoted 10 lines
man/man7/unix.7 +7 -2
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;

This seems to contradict the commit message. The commit message says that the string is terminated, but that the reported length might or might not be off-by-one (i.e., not count or count the '\0').

Quoted 2 lines

This text, however, is saying that in some cases there might not be a '\0' at all.

The TL;DR is that the byte after the returned buffer's declared length can't be trusted to be 0 - it is undefined and can be clobbered.

I have not read the full kernel implementation now, but it can already be seen in userspace that that adjacent byte can belong to a different data structue:

## How control messages are represented

The security context is returned to user space as a cmsghdr structure, as described in cmsg(3), where it is returned as a buffer of length cmsg_len:

struct cmsghdr {
	size_t cmsg_len;  /* data byte count including header */
        /* some other fields */

	/* followed by
           unsigned char cmsg_data[]; */
};

Reading beyond the declared cmsg_len is undefined, and callers don't get any guarantees about the byte following the csmg_data[] buffer.

## Iterating from userspace through multiple control messages

Iterating through the cmsgs is done with the API described in cmsg(3), CMSG_NXTHDR(). This is implemented as an inlined function in /usr/include/bits/socket.h. The function does a bunch of validation logic and then ends in this rather simple advancement logic:

__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
return __cmsg;

So, the cmsgs with their cmsg_data[] buffers are next to each other in memory. They are aligned, but that still means that the byte after cmsg_data[] might be part of the next cmsg header or beyond the list of control messages.

---

The more I think about this, the more unsure I am that this is even worth documenting. In the end, anyone reading and interpreting this data probably needs to read the corresponding LSM anyway, and trailing null bytes are probably the least of their worries...?

Maybe it would be better to just say something like "The format of the security context is determined by the LSM responsible for it". WDYT?

–Günther

#15 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Günther,

Quoted 99 lines

Date: 2026-10-23 19:57:16+0200
From: Günther Noack <[email protected]>

Hello Alejandro!

Quoted 15 lines · Alejandro Colomar
Quoted 15 lines

Date: 2026-10-21 23:22:53+0200
From: Günther Noack <[email protected]>

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

Quoted 5 lines
Quoted 3 lines

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

...

Ah, I can see that this is confusing; let me reformulate that without mentioning this strlen() implementation detail. Smack returns a buffer with a given size *without* including the null byte, roughly like this (simplified):

  int len = strlen(skp->smk_known);
  /* populate */
  cp->context = skp->smk_known;
  cp->len = len;    <-- does not span the null byte

Reformulating to:

...but smack_to_secctx() in security/smack/smack_lsm.c sends a security context that does not end in a null byte.

Quoted 57 lines
Quoted 13 lines
Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

...

Quoted 26 lines

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +1 -3
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.

man/man7/unix.7 message, the · line 554

Quoted 10 lines
man/man7/unix.7 +7 -2
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;

This seems to contradict the commit message. The commit message says that the string is terminated, but that the reported length might or might not be off-by-one (i.e., not count or count the '\0').

Quoted 5 lines
Quoted 2 lines

This text, however, is saying that in some cases there might not be a '\0' at all.

The TL;DR is that the byte after the returned buffer's declared length can't be trusted to be 0 - it is undefined and can be clobbered.

Huh! This is to me a bug. Would you mind reporting them this as a bug and CCing me? I'd rather get this fixed in the kernel than documented as broken.

Especially, if they use strlen(3) internally, this sounds very much like a kernel bug, but even if not, it is clearly a kernel bug.

In any case, if we were to document this bug, we should not use the term string, because anything not containing a '\0' is not a string, by definition.

Quoted 39 lines

I have not read the full kernel implementation now, but it can already be seen in userspace that that adjacent byte can belong to a different data structue:

## How control messages are represented

The security context is returned to user space as a cmsghdr structure, as described in cmsg(3), where it is returned as a buffer of length cmsg_len:

struct cmsghdr {
	size_t cmsg_len;  /* data byte count including header */
        /* some other fields */

	/* followed by
           unsigned char cmsg_data[]; */
};

Reading beyond the declared cmsg_len is undefined, and callers don't get any guarantees about the byte following the csmg_data[] buffer.

## Iterating from userspace through multiple control messages

Iterating through the cmsgs is done with the API described in cmsg(3), CMSG_NXTHDR(). This is implemented as an inlined function in /usr/include/bits/socket.h. The function does a bunch of validation logic and then ends in this rather simple advancement logic:

__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
return __cmsg;

So, the cmsgs with their cmsg_data[] buffers are next to each other in memory. They are aligned, but that still means that the byte after cmsg_data[] might be part of the next cmsg header or beyond the list of control messages.

Quoted 7 lines

---

The more I think about this, the more unsure I am that this is even worth documenting. In the end, anyone reading and interpreting this data probably needs to read the corresponding LSM anyway, and trailing null bytes are probably the least of their worries...?

Not only that it's worth documenting, but this might be a vulnerability.

Quoted 3 lines

Maybe it would be better to just say something like "The format of the security context is determined by the LSM responsible for it". WDYT?

I'm unwilling to accept a nonstring as a valid format, unless very good reasons are stated. And if those reasons are stated as "that ship sailed long ago", the way to document this might very well be BUGS.

Have a lovely night!
Alex

Quoted 2 lines

–Günther

Signature · 1 line
<https://www.alejandro-colomar.es>
#16 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello Alejandro, Casey!

Quoted 106 lines · Alejandro Colomar
Quoted 96 lines

Date: 2026-10-23 19:57:16+0200
From: Günther Noack <[email protected]>

Quoted 15 lines · Alejandro Colomar
Quoted 15 lines

Date: 2026-10-21 23:22:53+0200
From: Günther Noack <[email protected]>

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

Quoted 5 lines
Quoted 3 lines

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

...

Ah, I can see that this is confusing; let me reformulate that without mentioning this strlen() implementation detail. Smack returns a buffer with a given size *without* including the null byte, roughly like this (simplified):

  int len = strlen(skp->smk_known);
  /* populate */
  cp->context = skp->smk_known;
  cp->len = len;    <-- does not span the null byte

Reformulating to:

...but smack_to_secctx() in security/smack/smack_lsm.c sends a security context that does not end in a null byte.

Quoted 57 lines
Quoted 13 lines
Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

...

Quoted 26 lines

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +1 -3
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.

man/man7/unix.7 message, the · line 554

Quoted 10 lines
man/man7/unix.7 +7 -2
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;

This seems to contradict the commit message. The commit message says that the string is terminated, but that the reported length might or might not be off-by-one (i.e., not count or count the '\0').

Quoted 5 lines
Quoted 2 lines

This text, however, is saying that in some cases there might not be a '\0' at all.

The TL;DR is that the byte after the returned buffer's declared length can't be trusted to be 0 - it is undefined and can be clobbered.

Huh! This is to me a bug. Would you mind reporting them this as a bug and CCing me? I'd rather get this fixed in the kernel than documented as broken.

Quoted 6 lines

Especially, if they use strlen(3) internally, this sounds very much like a kernel bug, but even if not, it is clearly a kernel bug.

In any case, if we were to document this bug, we should not use the term string, because anything not containing a '\0' is not a string, by definition.

Casey, would you be able to confirm whether this constitutes a Smack bug or is intentional?

The TL;DR is that we came across the function smack_to_secctx() in smack_lsm.c, which passes a Smack label string in a buffer without a terminating null byte. This is inconsistent with what SELinux and AppArmor do.

Before we start explaining so many corner cases in the man pages, I wanted to check -- is this something that Smack should fix, by including the zero byte?

I tried to double check the Smack userspace utilities but couldn't find any use of SCM_SECURITY there. (Is the feature unused?)

Quoted 47 lines
Quoted 39 lines

I have not read the full kernel implementation now, but it can already be seen in userspace that that adjacent byte can belong to a different data structue:

## How control messages are represented

The security context is returned to user space as a cmsghdr structure, as described in cmsg(3), where it is returned as a buffer of length cmsg_len:

struct cmsghdr {
	size_t cmsg_len;  /* data byte count including header */
        /* some other fields */

	/* followed by
           unsigned char cmsg_data[]; */
};

Reading beyond the declared cmsg_len is undefined, and callers don't get any guarantees about the byte following the csmg_data[] buffer.

## Iterating from userspace through multiple control messages

Iterating through the cmsgs is done with the API described in cmsg(3), CMSG_NXTHDR(). This is implemented as an inlined function in /usr/include/bits/socket.h. The function does a bunch of validation logic and then ends in this rather simple advancement logic:

__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
return __cmsg;

So, the cmsgs with their cmsg_data[] buffers are next to each other in memory. They are aligned, but that still means that the byte after cmsg_data[] might be part of the next cmsg header or beyond the list of control messages.

Quoted 7 lines

---

The more I think about this, the more unsure I am that this is even worth documenting. In the end, anyone reading and interpreting this data probably needs to read the corresponding LSM anyway, and trailing null bytes are probably the least of their worries...?

Quoted 8 lines

Not only that it's worth documenting, but this might be a vulnerability.

Quoted 2 lines

Maybe it would be better to just say something like "The format of the security context is determined by the LSM responsible for it". WDYT?

I'm unwilling to accept a nonstring as a valid format, unless very good reasons are stated. And if those reasons are stated as "that ship sailed long ago", the way to document this might very well be BUGS.

Point taken and agreed that it would be better to call it a "buffer" rather than a "string" when it does not have the null terminator.

It does seem that this is used also outside of UNIX Domain Sockets:
IP_PASSSEC(2const) says:

The security context returned in the SCM_SECURITY ancillary message is of the same format as the one described in SO_PEERSEC(2const).

And looking this format up in SO_PEERSEC(2const), we find:

The security context string may include a terminating null character in the returned length, but is not guaranteed to do so: a security context "foo" might be represented as either {'f','o','o'} of length 3 or {'f','o','o','\0'} of length 4, which are considered to be interchangeable. The string is printable, does not contain non- terminating null characters, and is in an unspecified encoding (in particular, it is not guaranteed to be ASCII or UTF-8).

So it seems that these "buffer" (not "string") semantics exist there as well. Maybe it is intentional then that it is treated as a buffer rather than a string?

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

–Günther

#17 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Does that sound reasonable? Let me know what you think.

I removed the wording "There is no fixed limit ...", because it should be a given that callers allocate enough buffer space for receiving and check the truncation flag. (The more important part is that we remove the previous wording which suggested that it would fit into NAME_MAX.)

Given that SCM_SECURITY on "real" networks seems to have the same "buffer, not string" semantics, it feels fair to express it that way? People who want to read the security context will anyway need to read up on the exact format used by the LSM in order to attribute any meaning to it.

–Günther

P.S.: I had a hard time finding many usage examples. The main user of SCM_SECURITY seems to be systemd's journald, which uses it to log the SELinux security label. That code can deal with both null terminated strings and ones where it is missing (due to truncation), and there is a comment indicating that they are aware of possible truncation.

#18 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Günther,

Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Have a lovely night!
Alex

Quoted 20 lines

Does that sound reasonable? Let me know what you think.

I removed the wording "There is no fixed limit ...", because it should be a given that callers allocate enough buffer space for receiving and check the truncation flag. (The more important part is that we remove the previous wording which suggested that it would fit into NAME_MAX.)

Given that SCM_SECURITY on "real" networks seems to have the same "buffer, not string" semantics, it feels fair to express it that way? People who want to read the security context will anyway need to read up on the exact format used by the LSM in order to attribute any meaning to it.

–Günther

P.S.: I had a hard time finding many usage examples. The main user of SCM_SECURITY seems to be systemd's journald, which uses it to log the SELinux security label. That code can deal with both null terminated strings and ones where it is missing (due to truncation), and there is a comment indicating that they are aware of possible truncation.

Signature · 1 line
<https://www.alejandro-colomar.es>
#19 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello Alejandro!

Quoted 22 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Quoted 7 lines

Let's pause the discussion of the specific text for now.

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Other LSMs:

* Smack excludes the null byte for SCM_SECURITY (smack_to_secctx()) (but irritatingly it does count the null byte when a label is returned through SO_PEERSEC (smack_socket_getpeersec_stream()). Both functions are in security/smack/smack_lsm.c.

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

    len = aa_label_asxprint(&cp->context...)

and then sets

    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

SELinux, Smack and AppArmor are the only LSMs that implement the secid_to_secctx() hook, which is the hook that defines the payload contents. (For reference, the higher level common SCM logic like the truncation is in net/core/scm.c.)

All reasoning is based on reading the code (with some AI assistance to point me to the right places). I have not tried it out in a running system, but it seems to check out.

–Günther

#20 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Günther,

Quoted 30 lines

Date: 2026-10-27 23:30:59+0100
From: Günther Noack <[email protected]>

Hello Alejandro!

Quoted 24 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

Quoted 8 lines
Quoted 5 lines

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Ok.

Quoted 7 lines

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Ouch! So, no string guarantees here either, actually.

Quoted 6 lines

Other LSMs:

* Smack excludes the null byte for SCM_SECURITY (smack_to_secctx()) (but irritatingly it does count the null byte when a label is returned through SO_PEERSEC (smack_socket_getpeersec_stream()). Both functions are in security/smack/smack_lsm.c.

And as we've seen, no string guarantees, IIUC.

Quoted 5 lines

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

Quoted 8 lines
    len = aa_label_asxprint(&cp->context...)

and then sets

    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

How does it behave if there's not enough space? Does AppArmor guarantee a string?

Cheers,
Alex

Quoted 10 lines

SELinux, Smack and AppArmor are the only LSMs that implement the secid_to_secctx() hook, which is the hook that defines the payload contents. (For reference, the higher level common SCM logic like the truncation is in net/core/scm.c.)

All reasoning is based on reading the code (with some AI assistance to point me to the right places). I have not tried it out in a running system, but it seems to check out.

–Günther

Signature · 1 line
<https://www.alejandro-colomar.es>
#21 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello Alejandro!

Quoted 39 lines · Alejandro Colomar
Quoted 27 lines

Date: 2026-10-27 23:30:59+0100
From: Günther Noack <[email protected]>

Quoted 24 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

Quoted 8 lines
Quoted 5 lines

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Ok.

Quoted 1 line
Quoted 8 lines
Quoted 6 lines

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Ouch! So, no string guarantees here either, actually.

Correct. Callers have to deal with the possibility of truncation, which strips away the null byte.

Quoted 8 lines
Quoted 6 lines

Other LSMs:

* Smack excludes the null byte for SCM_SECURITY (smack_to_secctx()) (but irritatingly it does count the null byte when a label is returned through SO_PEERSEC (smack_socket_getpeersec_stream()). Both functions are in security/smack/smack_lsm.c.

And as we've seen, no string guarantees, IIUC.

Correct.

Quoted 9 lines
Quoted 9 lines

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

    len = aa_label_asxprint(&cp->context...)

and then sets

Quoted 7 lines
Quoted 4 lines
    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

How does it behave if there's not enough space? Does AppArmor guarantee a string?

The truncation case always strips off the null byte at the end of the buffer. This is handled **independent of the LSM** in the common net/core/scm.c layer one level above - it *always* kicks in when truncation happens.

To sum it all up in a table: The only case where the null byte is included in the buffer is in SELinux, when the generic SCM layer does not need to do truncation:

normal case truncation case
--------------------------------------------------------------

  SELinux       has trailing null byte     no trailing null byte
  AppArmor      no trailing null byte      no trailing null byte
  Smack         no trailing null byte      no trailing null byte

(other LSMs don't have the feature at all)

–Günther

#22 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Günther,

Quoted 52 lines

Date: 2026-10-28 07:19:28+0100
From: Günther Noack <[email protected]>

Hello Alejandro!

Quoted 46 lines · Alejandro Colomar
Quoted 27 lines

Date: 2026-10-27 23:30:59+0100
From: Günther Noack <[email protected]>

Quoted 24 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

Quoted 8 lines
Quoted 5 lines

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Ok.

Quoted 7 lines

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Quoted 4 lines
Quoted 1 line

Ouch! So, no string guarantees here either, actually.

Correct. Callers have to deal with the possibility of truncation, which strips away the null byte.

Is this true for every LSM? Do any LSMs guarantee a string even under truncation?

Quoted 25 lines
Quoted 8 lines
Quoted 6 lines

Other LSMs:

* Smack excludes the null byte for SCM_SECURITY (smack_to_secctx()) (but irritatingly it does count the null byte when a label is returned through SO_PEERSEC (smack_socket_getpeersec_stream()). Both functions are in security/smack/smack_lsm.c.

And as we've seen, no string guarantees, IIUC.

Correct.

Quoted 14 lines
Quoted 13 lines

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

    len = aa_label_asxprint(&cp->context...)

and then sets

    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

Quoted 7 lines
Quoted 2 lines

How does it behave if there's not enough space? Does AppArmor guarantee a string?

The truncation case always strips off the null byte at the end of the buffer. This is handled **independent of the LSM** in the common net/core/scm.c layer one level above - it *always* kicks in when truncation happens.

Hmmmm. I believe that layer should guarantee a null byte when it truncates the data. It should be like strscpy(), which guarantees a correct string even on truncation.

This seems to be another bug.

Quoted 11 lines

To sum it all up in a table: The only case where the null byte is included in the buffer is in SELinux, when the generic SCM layer does not need to do truncation:

normal case truncation case
--------------------------------------------------------------

  SELinux       has trailing null byte     no trailing null byte
  AppArmor      no trailing null byte      no trailing null byte
  Smack         no trailing null byte      no trailing null byte

(other LSMs don't have the feature at all)

Thanks for the table! This seems to me to be 3 bugs: one in AppArmor, the same one in Smack, and one in the upper layer which corrupts the string when truncating. I wonder if we can do better in the truncation case, or if it's too late for that.

Would you mind CCing the maintainers of the upper layer in net/core/scm.c?

Have a lovely day!
Alex

Quoted 2 lines

–Günther

Signature · 1 line
<https://www.alejandro-colomar.es>
#23 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello!

Quoted 47 lines · Alejandro Colomar
Quoted 47 lines · Alejandro Colomar
Quoted 46 lines
Quoted 27 lines

Date: 2026-10-27 23:30:59+0100
From: Günther Noack <[email protected]>

Quoted 24 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

Quoted 8 lines
Quoted 5 lines

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Ok.

Quoted 7 lines

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Ouch! So, no string guarantees here either, actually.

Correct. Callers have to deal with the possibility of truncation, which strips away the null byte.

Is this true for every LSM? Do any LSMs guarantee a string even under truncation?

Yes, it is true for every LSM (as discussed below) and none of the LSMs can guarantee a string under truncation.

Quoted 21 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

    len = aa_label_asxprint(&cp->context...)

and then sets

    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

Quoted 7 lines
Quoted 2 lines

How does it behave if there's not enough space? Does AppArmor guarantee a string?

The truncation case always strips off the null byte at the end of the buffer. This is handled **independent of the LSM** in the common net/core/scm.c layer one level above - it *always* kicks in when truncation happens.

Quoted 5 lines

Hmmmm. I believe that layer should guarantee a null byte when it truncates the data. It should be like strscpy(), which guarantees a correct string even on truncation.

This seems to be another bug.

(Bringing it up below together with the AA/Smack payloads which are also not null-terminated.)

Quoted 12 lines
Quoted 11 lines

To sum it all up in a table: The only case where the null byte is included in the buffer is in SELinux, when the generic SCM layer does not need to do truncation:

normal case truncation case
--------------------------------------------------------------

  SELinux       has trailing null byte     no trailing null byte
  AppArmor      no trailing null byte      no trailing null byte
  Smack         no trailing null byte      no trailing null byte

(other LSMs don't have the feature at all)

Quoted 7 lines

Thanks for the table! This seems to me to be 3 bugs: one in AppArmor, the same one in Smack, and one in the upper layer which corrupts the string when truncating. I wonder if we can do better in the truncation case, or if it's too late for that.

Would you mind CCing the maintainers of the upper layer in net/core/scm.c?

Adding Eric Dumazet, Paolo Abeni and Willem de Brujin. (Kuniyuki Iwashima is already in CC.)

Hello maintainers of NETWORKING [SOCKETS]! :)

We are unsure in this documentation review what
the intended format of payloads is which are returned by SCM_SECURITY:
They have previously been documented as zero-terminated strings (for the SELinux case), but in the case of truncation and in the case of the two other supported LSMs, there implementation does not return a trailing zero byte.

Could you please confirm what the intended interface for SCM_SECURITY is? The two variants we have been discussing are:

Variant A:
The returned payload is a byte buffer whose contents are defined by the respective LSM (and which are not necessarily zero-terminated).

Concrete wording proposal for this variant is in:
https://lore.kernel.org/all/[email protected]/

Variant B:
The returned payload is a string which should be zero-terminated.

(But in this case, the Smack and Apparmor implementation, as well as the generic truncation are arguably not doing that right.)

The documentation commit at the top of this dicussion is documenting the current implementation, but it becomes lengthy and it feels like it could be crisper if we could document the original intent:
https://lore.kernel.org/all/[email protected]/

I would appreciate your feedback to un-wedge the review. :)

Thanks!
–Günther

#24 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Quoted 124 lines · Günther Noack

Hello!

Quoted 47 lines · Alejandro Colomar
Quoted 47 lines · Alejandro Colomar
Quoted 46 lines
Quoted 27 lines

Date: 2026-10-27 23:30:59+0100
From: Günther Noack <[email protected]>

Quoted 24 lines · Alejandro Colomar
Quoted 14 lines

Date: 2026-10-27 09:54:51+0100
From: Günther Noack <[email protected]>

Quoted 4 lines · Günther Noack

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

To make a more concrete and constructive wording proposal:

SCM_SECURITY

Quoted 7 lines

Receive the security context (the security label) of the peer socket, as provided by the active Linux Security Module (LSM). The received ancillary data is a byte buffer.

The exact format of the security context buffer is defined by the active major LSM. For example, SELinux returns a SELinux label as a string (including a terminating null byte).

Let's pause the discussion of the specific text for now.

Quoted 8 lines
Quoted 5 lines

I have questions about the exact format used by each LSM. For example, you say that SELinux guarantees a string. Does it count the '\0' as part of the data size? Or is it guaranteed to exist but not counted?

Same question for the other LSMs.

Yes, SELinux counts the '\0' as part of the buffer size for the SCM_SECURITY security context.

Ok.

Quoted 7 lines

Examples:
* If the label is "foo", it'll return {'f', 'o', 'o', '\0'} with payload size 4.
* If the reader calls recvmsg() but preallocates too little space in the msg_control buffer, this can get truncated though, and then it won't end with '\0':
{'e', 'x', 'a', 'm', 'p'} with payload size 5 (truncated)

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Ouch! So, no string guarantees here either, actually.

Correct. Callers have to deal with the possibility of truncation, which strips away the null byte.

Is this true for every LSM? Do any LSMs guarantee a string even under truncation?

Yes, it is true for every LSM (as discussed below) and none of the LSMs can guarantee a string under truncation.

Quoted 21 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

* AppArmor excludes the null byte for SCM_SECURITY as well. Implementation is in apparmor_label_to_secctx(), where it populates the struct lsm_context *cp using:

    len = aa_label_asxprint(&cp->context...)

and then sets

    cp->len = len;

The function aa_label_asxprint() returns the length without trailing null byte, as can be seen in its implementation.

Quoted 7 lines
Quoted 2 lines

How does it behave if there's not enough space? Does AppArmor guarantee a string?

The truncation case always strips off the null byte at the end of the buffer. This is handled **independent of the LSM** in the common net/core/scm.c layer one level above - it *always* kicks in when truncation happens.

Quoted 5 lines

Hmmmm. I believe that layer should guarantee a null byte when it truncates the data. It should be like strscpy(), which guarantees a correct string even on truncation.

This seems to be another bug.

(Bringing it up below together with the AA/Smack payloads which are also not null-terminated.)

Quoted 12 lines
Quoted 11 lines

To sum it all up in a table: The only case where the null byte is included in the buffer is in SELinux, when the generic SCM layer does not need to do truncation:

normal case truncation case
--------------------------------------------------------------

  SELinux       has trailing null byte     no trailing null byte
  AppArmor      no trailing null byte      no trailing null byte
  Smack         no trailing null byte      no trailing null byte

(other LSMs don't have the feature at all)

Quoted 7 lines

Thanks for the table! This seems to me to be 3 bugs: one in AppArmor, the same one in Smack, and one in the upper layer which corrupts the string when truncating. I wonder if we can do better in the truncation case, or if it's too late for that.

Would you mind CCing the maintainers of the upper layer in net/core/scm.c?

Adding Eric Dumazet, Paolo Abeni and Willem de Brujin. (Kuniyuki Iwashima is already in CC.)

Hello maintainers of NETWORKING [SOCKETS]! :)

We are unsure in this documentation review what
the intended format of payloads is which are returned by SCM_SECURITY:
They have previously been documented as zero-terminated strings (for the SELinux case), but in the case of truncation and in the case of the two other supported LSMs, there implementation does not return a trailing zero byte.

Quoted 6 lines

Could you please confirm what the intended interface for SCM_SECURITY is? The two variants we have been discussing are:

Variant A:
The returned payload is a byte buffer whose contents are defined by the respective LSM (and which are not necessarily zero-terminated).

+1, basically each scm data is binary.

I think it's just man page was not updated for other LSMs.

Quoted 20 lines

Concrete wording proposal for this variant is in:
https://lore.kernel.org/all/[email protected]/

Variant B:
The returned payload is a string which should be zero-terminated.

(But in this case, the Smack and Apparmor implementation, as well as the generic truncation are arguably not doing that right.)

The documentation commit at the top of this dicussion is documenting the current implementation, but it becomes lengthy and it feels like it could be crisper if we could document the original intent:
https://lore.kernel.org/all/[email protected]/

I would appreciate your feedback to un-wedge the review. :)

Thanks!
–Günther

#25 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hello!

Quoted 37 lines · Kuniyuki Iwashima
Quoted 36 lines · Günther Noack
Quoted 12 lines · Alejandro Colomar
Quoted 11 lines

To sum it all up in a table: The only case where the null byte is included in the buffer is in SELinux, when the generic SCM layer does not need to do truncation:

normal case truncation case
--------------------------------------------------------------

  SELinux       has trailing null byte     no trailing null byte
  AppArmor      no trailing null byte      no trailing null byte
  Smack         no trailing null byte      no trailing null byte

(other LSMs don't have the feature at all)

Quoted 7 lines

Thanks for the table! This seems to me to be 3 bugs: one in AppArmor, the same one in Smack, and one in the upper layer which corrupts the string when truncating. I wonder if we can do better in the truncation case, or if it's too late for that.

Would you mind CCing the maintainers of the upper layer in net/core/scm.c?

Adding Eric Dumazet, Paolo Abeni and Willem de Brujin. (Kuniyuki Iwashima is already in CC.)

Hello maintainers of NETWORKING [SOCKETS]! :)

We are unsure in this documentation review what
the intended format of payloads is which are returned by SCM_SECURITY:
They have previously been documented as zero-terminated strings (for the SELinux case), but in the case of truncation and in the case of the two other supported LSMs, there implementation does not return a trailing zero byte.

Could you please confirm what the intended interface for SCM_SECURITY is? The two variants we have been discussing are:

Quoted 7 lines
Quoted 3 lines

Variant A:
The returned payload is a byte buffer whose contents are defined by the respective LSM (and which are not necessarily zero-terminated).

+1, basically each scm data is binary.

I think it's just man page was not updated for other LSMs.

Thank you for the confirmation, Kuniyuki!

Quoted 2 lines
Quoted 2 lines

Concrete wording proposal for this variant is in:
https://lore.kernel.org/all/[email protected]/

^ This

Alejandro, in this case, and to make some progress on this patch set, I'll resend the patch set with the "binary buffer" variant that I proposed in the other mail. Let's discuss the wording there, if needed.

–Günther

#26 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

In reply to Günther Noack
Quoted 103 lines · Günther Noack

Hello Alejandro, Casey!

Quoted 100 lines · Alejandro Colomar
Quoted 92 lines

Date: 2026-10-23 19:57:16+0200
From: Günther Noack <[email protected]>

Quoted 11 lines · Alejandro Colomar
Quoted 11 lines

Date: 2026-10-21 23:22:53+0200
From: Günther Noack <[email protected]>

Reword the description of SCM_SECURITY to not be specific to SELinux.

The security context is populated in scm_passec() in linux.git net/core/scm.c, through the security_secid_to_secctx() LSM hook, which is currently implemented by SELinux, Smack and AppArmor.

Also clarify the properties of the returned string:

Quoted 8 lines
Quoted 7 lines

* There is no NAME_MAX limit on the length; SELinux contexts can be longer than that. If the control buffer is too small, put_cmsg() truncates the data and sets MSG_CTRUNC.

* Whether the string is null-terminated also depends on the LSM:
SELinux includes the null byte in the length, but smack_to_secctx() in security/smack/smack_lsm.c uses strlen() and doesn't.

...

Ah, I can see that this is confusing; let me reformulate that without mentioning this strlen() implementation detail. Smack returns a buffer with a given size *without* including the null byte, roughly like this (simplified):

  int len = strlen(skp->smk_known);
  /* populate */
  cp->context = skp->smk_known;
  cp->len = len;    <-- does not span the null byte

Reformulating to:

...but smack_to_secctx() in security/smack/smack_lsm.c sends a security context that does not end in a null byte.

Quoted 55 lines
Quoted 13 lines
Fixes ffad6a017f91 (2018-07-28; "unix.7: Document SCM_SECURITY ancillary data")
Cc Michael Kerrisk <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM

---

Notes:
This feature is also available in other LSMs. Unfortunately, they implemented it inconsistently and that complicates the description of how string boundaries are handled. Linux does indeed neither guarantee that this terminates with a NUL byte, nor that NAME_MAX is large enough to hold it.

...

Quoted 26 lines

This is the verbose variant, but I am open for suggestions regarding what level of detail is appropriate here.

 man/man7/unix.7 | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
man/man7/unix.7 +1 -3
index ded82768322b..4abd216d19c7 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the SELinux security label of the peer socket
+Enables receiving of the security label of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
@@ -544,13 +544,26 @@ message, the
 option must be enabled on the socket.
 .TP
 .B SCM_SECURITY
-Receive the SELinux security context (the security label)
-of the peer socket.

man/man7/unix.7 message, the · line 554

Quoted 10 lines
man/man7/unix.7 +7 -2
-The received ancillary data is a null-terminated string containing
+Receive the security context (the security label)
+of the peer socket,
+as provided by the active Linux Security Module (LSM),
+such as SELinux or Smack.
+The received ancillary data is a string containing
 the security context.
-The receiver should allocate at least
+Whether the string includes a terminating null byte
+depends on the LSM;

This seems to contradict the commit message. The commit message says that the string is terminated, but that the reported length might or might not be off-by-one (i.e., not count or count the '\0').

Quoted 4 lines
Quoted 2 lines

This text, however, is saying that in some cases there might not be a '\0' at all.

The TL;DR is that the byte after the returned buffer's declared length can't be trusted to be 0 - it is undefined and can be clobbered.

Huh! This is to me a bug. Would you mind reporting them this as a bug and CCing me? I'd rather get this fixed in the kernel than documented as broken.

Quoted 8 lines
Quoted 6 lines

Especially, if they use strlen(3) internally, this sounds very much like a kernel bug, but even if not, it is clearly a kernel bug.

In any case, if we were to document this bug, we should not use the term string, because anything not containing a '\0' is not a string, by definition.

Casey, would you be able to confirm whether this constitutes a Smack bug or is intentional?

Inclusion of a null byte for C string interfaces has been a topic of debate since about 1980. At the time this code was written my understanding was that the data SCM_SECURITY returned could not be assumed to be a C string. Hence, the size of the data, not the size of a string. The null byte isn't considered a part of the string, as reported by strlen().

So, the existing behavior is intentional, but I'm willing to have it considered a bug if (as of today) everyone thinks the data should include a null byte.

Quoted 11 lines

The TL;DR is that we came across the function smack_to_secctx() in smack_lsm.c, which passes a Smack label string in a buffer without a terminating null byte. This is inconsistent with what SELinux and AppArmor do.

Before we start explaining so many corner cases in the man pages, I wanted to check -- is this something that Smack should fix, by including the zero byte?

I tried to double check the Smack userspace utilities but couldn't find any use of SCM_SECURITY there. (Is the feature unused?)

The Smack userspace utilities (github.com://smackteam/smack.git) represent a limited Smack system implementation. SCM_SECURITY is used in at least a couple of important embedded and enterprise implementations.

Quoted 71 lines
Quoted 46 lines
Quoted 39 lines

I have not read the full kernel implementation now, but it can already be seen in userspace that that adjacent byte can belong to a different data structue:

## How control messages are represented

The security context is returned to user space as a cmsghdr structure, as described in cmsg(3), where it is returned as a buffer of length cmsg_len:

struct cmsghdr {
	size_t cmsg_len;  /* data byte count including header */
        /* some other fields */

	/* followed by
           unsigned char cmsg_data[]; */
};

Reading beyond the declared cmsg_len is undefined, and callers don't get any guarantees about the byte following the csmg_data[] buffer.

## Iterating from userspace through multiple control messages

Iterating through the cmsgs is done with the API described in cmsg(3), CMSG_NXTHDR(). This is implemented as an inlined function in /usr/include/bits/socket.h. The function does a bunch of validation logic and then ends in this rather simple advancement logic:

__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
return __cmsg;

So, the cmsgs with their cmsg_data[] buffers are next to each other in memory. They are aligned, but that still means that the byte after cmsg_data[] might be part of the next cmsg header or beyond the list of control messages.

Quoted 7 lines

---

The more I think about this, the more unsure I am that this is even worth documenting. In the end, anyone reading and interpreting this data probably needs to read the corresponding LSM anyway, and trailing null bytes are probably the least of their worries...?

Quoted 6 lines

Not only that it's worth documenting, but this might be a vulnerability.

Quoted 2 lines

Maybe it would be better to just say something like "The format of the security context is determined by the LSM responsible for it". WDYT?

I'm unwilling to accept a nonstring as a valid format, unless very good reasons are stated. And if those reasons are stated as "that ship sailed long ago", the way to document this might very well be BUGS.

Point taken and agreed that it would be better to call it a "buffer" rather than a "string" when it does not have the null terminator.

It does seem that this is used also outside of UNIX Domain Sockets:
IP_PASSSEC(2const) says:

The security context returned in the SCM_SECURITY ancillary message is of the same format as the one described in SO_PEERSEC(2const).

And looking this format up in SO_PEERSEC(2const), we find:

The security context string may include a terminating null character in the returned length, but is not guaranteed to do so: a security context "foo" might be represented as either {'f','o','o'} of length 3 or {'f','o','o','\0'} of length 4, which are considered to be interchangeable. The string is printable, does not contain non- terminating null characters, and is in an unspecified encoding (in particular, it is not guaranteed to be ASCII or UTF-8).

Quoted 3 lines

So it seems that these "buffer" (not "string") semantics exist there as well. Maybe it is intentional then that it is treated as a buffer rather than a string?

I'm easy. If there is a strong desire to make SCM_SECURITY a string Smack can be changed easily enough.

Quoted 8 lines

I'm still leaning towards documenting it as a buffer with implementation-defined contents which are provided by a LSM. How these buffer contents are interpreted is depending on the LSM in use anyway.

–Günther

#27 lore raw

Re: [PATCH 4/7] man/man7/unix.7: SCM_SECURITY: Generalize to other LSMs, not just SELinux

Hi Casey,

Quoted 3 lines

Date: 2026-10-27 09:32:57-0700
From: Casey Schaufler <[email protected]>

[...]

Quoted 23 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

This text, however, is saying that in some cases there might not be a '\0' at all.

The TL;DR is that the byte after the returned buffer's declared length can't be trusted to be 0 - it is undefined and can be clobbered.

Huh! This is to me a bug. Would you mind reporting them this as a bug and CCing me? I'd rather get this fixed in the kernel than documented as broken.

Quoted 8 lines
Quoted 6 lines

Especially, if they use strlen(3) internally, this sounds very much like a kernel bug, but even if not, it is clearly a kernel bug.

In any case, if we were to document this bug, we should not use the term string, because anything not containing a '\0' is not a string, by definition.

Casey, would you be able to confirm whether this constitutes a Smack bug or is intentional?

Inclusion of a null byte for C string interfaces has been a topic of debate since about 1980. At the time this code was written my understanding was that the data SCM_SECURITY returned could not be assumed to be a C string. Hence, the size of the data, not the size of a string. The null byte isn't considered a part of the string, as reported by strlen().

Quoted 3 lines

So, the existing behavior is intentional, but I'm willing to have it considered a bug if (as of today) everyone thinks the data should include a null byte.

Yup, I think the issue of strings has more or less settled towards using C strings everywhere, with other formats --such as fixed-width buffers from utmp(5)-- being phased out. Most of libc takes C strings, and so using C strings everywhere is safer, as it avoids having to manually handle the bytes.

Quoted 88 lines
Quoted 11 lines

The TL;DR is that we came across the function smack_to_secctx() in smack_lsm.c, which passes a Smack label string in a buffer without a terminating null byte. This is inconsistent with what SELinux and AppArmor do.

Before we start explaining so many corner cases in the man pages, I wanted to check -- is this something that Smack should fix, by including the zero byte?

I tried to double check the Smack userspace utilities but couldn't find any use of SCM_SECURITY there. (Is the feature unused?)

The Smack userspace utilities (github.com://smackteam/smack.git) represent a limited Smack system implementation. SCM_SECURITY is used in at least a couple of important embedded and enterprise implementations.

Quoted 71 lines
Quoted 46 lines
Quoted 39 lines

I have not read the full kernel implementation now, but it can already be seen in userspace that that adjacent byte can belong to a different data structue:

## How control messages are represented

The security context is returned to user space as a cmsghdr structure, as described in cmsg(3), where it is returned as a buffer of length cmsg_len:

struct cmsghdr {
	size_t cmsg_len;  /* data byte count including header */
        /* some other fields */

	/* followed by
           unsigned char cmsg_data[]; */
};

Reading beyond the declared cmsg_len is undefined, and callers don't get any guarantees about the byte following the csmg_data[] buffer.

## Iterating from userspace through multiple control messages

Iterating through the cmsgs is done with the API described in cmsg(3), CMSG_NXTHDR(). This is implemented as an inlined function in /usr/include/bits/socket.h. The function does a bunch of validation logic and then ends in this rather simple advancement logic:

__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
return __cmsg;

So, the cmsgs with their cmsg_data[] buffers are next to each other in memory. They are aligned, but that still means that the byte after cmsg_data[] might be part of the next cmsg header or beyond the list of control messages.

Quoted 7 lines

---

The more I think about this, the more unsure I am that this is even worth documenting. In the end, anyone reading and interpreting this data probably needs to read the corresponding LSM anyway, and trailing null bytes are probably the least of their worries...?

Quoted 6 lines

Not only that it's worth documenting, but this might be a vulnerability.

Quoted 2 lines

Maybe it would be better to just say something like "The format of the security context is determined by the LSM responsible for it". WDYT?

I'm unwilling to accept a nonstring as a valid format, unless very good reasons are stated. And if those reasons are stated as "that ship sailed long ago", the way to document this might very well be BUGS.

Point taken and agreed that it would be better to call it a "buffer" rather than a "string" when it does not have the null terminator.

It does seem that this is used also outside of UNIX Domain Sockets:
IP_PASSSEC(2const) says:

The security context returned in the SCM_SECURITY ancillary message is of the same format as the one described in SO_PEERSEC(2const).

And looking this format up in SO_PEERSEC(2const), we find:

The security context string may include a terminating null character in the returned length, but is not guaranteed to do so: a security context "foo" might be represented as either {'f','o','o'} of length 3 or {'f','o','o','\0'} of length 4, which are considered to be interchangeable. The string is printable, does not contain non- terminating null characters, and is in an unspecified encoding (in particular, it is not guaranteed to be ASCII or UTF-8).

Quoted 6 lines
Quoted 3 lines

So it seems that these "buffer" (not "string") semantics exist there as well. Maybe it is intentional then that it is treated as a buffer rather than a string?

I'm easy. If there is a strong desire to make SCM_SECURITY a string Smack can be changed easily enough.

Yup. I think it'd be good to have guarantees that the label can be passed to strlen(3). Whether the reported size includes the '\0' or not, that's less important, but being able to pass the buffer to strlen(3) and any other string functions is essential for safety, IMO.

Thank you very much!

Have a lovely night!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#28 lore raw

PATCH 5/7 man/man7/unix.7: SO_PASSSEC: wfix

In reply to Günther Noack

Use "security context", as in the description of SCM_SECURITY.

Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
man/man7/unix.7 +1 -1
index 4abd216d19c7..67dd10cc146f 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the security label of the peer socket
+Enables receiving of the security context of the peer socket
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
Signature · 1 line
2.56.0
#29 lore raw

Re: [PATCH 5/7] man/man7/unix.7: SO_PASSSEC: wfix

Quoted 14 lines · Günther Noack

Use "security context", as in the description of SCM_SECURITY.

Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
man/man7/unix.7 +0 -0
index 4abd216d19c7..67dd10cc146f 100644

man/man7/unix.7 and returned as the result of · line 320

Quoted 5 lines
man/man7/unix.7 +1 -1
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the security label of the peer socket
+Enables receiving of the security context of the peer socket

Patch 4 touches here, why not squash ?

Quoted 6 lines
man/man7/unix.7 +0 -0
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
Signature · 1 line
2.56.0
#30 lore raw

Re: [PATCH 5/7] man/man7/unix.7: SO_PASSSEC: wfix

Hi Kuniyuki,

Quoted 13 lines

Date: 2026-10-24 05:02:06+0000
From: Kuniyuki Iwashima <[email protected]>

Quoted 9 lines · Günther Noack

Use "security context", as in the description of SCM_SECURITY.

Cc Kuniyuki Iwashima <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Quoted 12 lines

man/man7/unix.7 and returned as the result of · line 320

Quoted 10 lines
man/man7/unix.7 +1 -1
index 4abd216d19c7..67dd10cc146f 100644
@@ -317,7 +317,7 @@ and returned as the result of
 is an integer boolean flag.
 .TP
 .B SO_PASSSEC
-Enables receiving of the security label of the peer socket
+Enables receiving of the security context of the peer socket

Patch 4 touches here, why not squash ?

I prefer not squashing. It makes the commit messages more specific, which is easier to understand and review in the future.

Have a lovely day!
Alex

Quoted 8 lines
Quoted 8 lines
man/man7/unix.7 +0 -0
 in an ancillary message of type
 .B SCM_SECURITY
 (see below).
Signature · 1 line
2.56.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#31 lore raw

Re: [PATCH 5/7] man/man7/unix.7: SO_PASSSEC: wfix

Quoted 6 lines · Alejandro Colomar
Quoted 3 lines

Date: 2026-10-24 05:02:06+0000
From: Kuniyuki Iwashima <[email protected]>
Patch 4 touches here, why not squash ?

I prefer not squashing. It makes the commit messages more specific, which is easier to understand and review in the future.

Ack, leaving it unsquashed.

–Günther

Reviewed-by
#32 lore raw

Re: [PATCH 5/7] man/man7/unix.7: SO_PASSSEC: wfix

Quoted 9 lines · Günther Noack
Quoted 6 lines · Alejandro Colomar
Quoted 3 lines

Date: 2026-10-24 05:02:06+0000
From: Kuniyuki Iwashima <[email protected]>
Patch 4 touches here, why not squash ?

I prefer not squashing. It makes the commit messages more specific, which is easier to understand and review in the future.

Ack, leaving it unsquashed.

Okay, then

Thanks

#33 lore raw

PATCH 6/7 man/man7/unix.7: Document SO_PASSPIDFD, SO_PEERPIDFD, and SCM_PIDFD

In reply to Günther Noack

These were added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD") and 7b26952a91cf (2023-06-12; "net: core:
add getsockopt SO_PEERPIDFD"), both in Linux 6.5. They are the PID file descriptor counterparts of SO_PASSCRED/SCM_CREDENTIALS and SO_PEERCRED.

SO_PASSPIDFD
sk_setsockopt() and sk_getsockopt() in net/core/sock.c handle it as a boolean flag for AF_UNIX sockets (EOPNOTSUPP otherwise). Like SO_PASSCRED, it triggers autobind, which was already mentioned in "Autobind feature".

SCM_PIDFD
scm_pidfd_recv() in net/core/scm.c creates the PID file descriptor from scm->pid, which is the PID given in the sender's SCM_CREDENTIALS, or the sender's own PID by default. The fd is created with O_CLOEXEC by pidfd_prepare(). If pidfd_prepare() fails, its negative error number is put into the cmsg data. Sending SCM_PIDFD is rejected by __scm_send() (EINVAL).

Since Linux 6.17, with c679d17d3f2d (2025-07-04; "af_unix:
enable handing out pidfds for reaped tasks in SCM_PIDFD"), PIDFD_STALE is passed, so this also works for reaped processes. Before that, pidfd_prepare() returned -EINVAL for them.

SO_PEERPIDFD
sk_getsockopt() returns a PID file descriptor for sk_peer_pid, or fails with ENODATA if there is none.

Since Linux 6.16, with 20b70e58961b (2025-04-28; "net, pidfs:
enable handing out pidfds for reaped sk->sk_peer_pid"), this also works for reaped processes on AF_UNIX sockets. Before that, it failed with EINVAL.

Cc Alexander Mikhalitsyn <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Cc Eric Dumazet <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)
man/man7/unix.7 +101 -0
index 67dd10cc146f..a4457508fd2a 100644
@@ -316,6 +316,33 @@ and returned as the result of
 .BR getsockopt (2)
 is an integer boolean flag.
 .TP
+.BR SO_PASSPIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89
+Enabling this socket option causes receipt of a PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the sending process in an
+.B SCM_PIDFD
+ancillary message in each subsequently received message
+(see below).
+.IP
+As with
+.BR SO_PASSCRED ,
+when this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
+.IP
+The value given as an argument to
+.BR setsockopt (2)
+and returned as the result of
+.BR getsockopt (2)
+is an integer boolean flag.
+.TP
 .B SO_PASSSEC
 Enables receiving of the security context of the peer socket
 in an ancillary message of type
@@ -367,6 +394,43 @@ stream and sequenced-packet sockets, and for
 .B AF_UNIX
 socket pairs created using
 .BR socketpair (2).
+.TP
+.BR SO_PEERPIDFD " (since Linux 6.5)"
+.\" commit 7b26952a91cf65ff1cc867a2382a8964d8c0ee7d
+This read-only socket option returns a new PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the peer process connected to this socket.
+This is the process whose credentials are returned by
+.BR SO_PEERCRED .
+Unlike the PID returned by
+.BR SO_PEERCRED ,
+the PID file descriptor cannot come to refer to a different process
+if the peer process terminates and its PID is reused.
+.IP
+The argument to
+.BR getsockopt (2)
+is a pointer to an
+.IR int ,
+in which the PID file descriptor is returned.
+The close-on-exec flag
+.RB ( FD_CLOEXEC )
+is set on the new file descriptor.
+.IP
+If the socket has no peer process,
+.BR getsockopt (2)
+fails with the error
+.BR ENODATA .
+.IP
+Since Linux 6.16,
+.\" commit 20b70e58961b855e61f2c6a7a19a38a64ad1bed6
+a PID file descriptor is returned
+even if the peer process has already terminated and been reaped.
+Before Linux 6.16,
+.BR getsockopt (2)
+failed with the error
+.B EINVAL
+in this case.
 .\"
 .SS Autobind feature
 If a
@@ -543,6 +607,42 @@ message, the
 .B SO_PASSCRED
 option must be enabled on the socket.
 .TP
+.BR SCM_PIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89
+Receive a PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the sending process.
+The data portion contains a single
+.IR int .
+The PID file descriptor refers to the process whose PID would be
+received in an
+.B SCM_CREDENTIALS
+message;
+if the sender specified
+.B SCM_CREDENTIALS
+ancillary data,
+it refers to the process with the PID given there.
+The close-on-exec flag
+.RB ( FD_CLOEXEC )
+is set on the new file descriptor.
+.IP
+If the PID file descriptor cannot be created,
+the data portion contains a negative error number instead.
+Since Linux 6.17,
+.\" commit c679d17d3f2d895b34e660673141ad250889831f
+a PID file descriptor is created
+even if the sending process has already terminated and been reaped.
+Before Linux 6.17,
+.B \-EINVAL
+was received in this case.
+.IP
+This message cannot be sent;
+it is generated by the kernel.
+To receive it, the
+.B SO_PASSPIDFD
+option must be enabled on the socket.
+.TP
 .B SCM_SECURITY
 Receive the security context (the security label)
 of the peer socket,
@@ -1273,6 +1373,7 @@ see
 and
 .BR seccomp_unotify (2).
 .SH SEE ALSO
+.BR pidfd_open (2),
 .BR recvmsg (2),
 .BR sendmsg (2),
 .BR socket (2),
Signature · 1 line
2.56.0
#34 lore raw

Re: [PATCH 6/7] man/man7/unix.7: Document SO_PASSPIDFD, SO_PEERPIDFD, and SCM_PIDFD

Quoted 49 lines · Günther Noack

These were added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD") and 7b26952a91cf (2023-06-12; "net: core:
add getsockopt SO_PEERPIDFD"), both in Linux 6.5. They are the PID file descriptor counterparts of SO_PASSCRED/SCM_CREDENTIALS and SO_PEERCRED.

SO_PASSPIDFD
sk_setsockopt() and sk_getsockopt() in net/core/sock.c handle it as a boolean flag for AF_UNIX sockets (EOPNOTSUPP otherwise). Like SO_PASSCRED, it triggers autobind, which was already mentioned in "Autobind feature".

SCM_PIDFD
scm_pidfd_recv() in net/core/scm.c creates the PID file descriptor from scm->pid, which is the PID given in the sender's SCM_CREDENTIALS, or the sender's own PID by default. The fd is created with O_CLOEXEC by pidfd_prepare(). If pidfd_prepare() fails, its negative error number is put into the cmsg data. Sending SCM_PIDFD is rejected by __scm_send() (EINVAL).

Since Linux 6.17, with c679d17d3f2d (2025-07-04; "af_unix:
enable handing out pidfds for reaped tasks in SCM_PIDFD"), PIDFD_STALE is passed, so this also works for reaped processes. Before that, pidfd_prepare() returned -EINVAL for them.

SO_PEERPIDFD
sk_getsockopt() returns a PID file descriptor for sk_peer_pid, or fails with ENODATA if there is none.

Since Linux 6.16, with 20b70e58961b (2025-04-28; "net, pidfs:
enable handing out pidfds for reaped sk->sk_peer_pid"), this also works for reaped processes on AF_UNIX sockets. Before that, it failed with EINVAL.

Cc Alexander Mikhalitsyn <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Cc Eric Dumazet <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)
man/man7/unix.7 +0 -0
index 67dd10cc146f..a4457508fd2a 100644

man/man7/unix.7 and returned as the result of · line 320

Quoted 5 lines
man/man7/unix.7 +2 -0
@@ -316,6 +316,33 @@ and returned as the result of
 .BR getsockopt (2)
 is an integer boolean flag.
 .TP
+.BR SO_PASSPIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Why does pidfd only have SHA1 ?

Quoted 29 lines
man/man7/unix.7 +25 -0
+Enabling this socket option causes receipt of a PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the sending process in an
+.B SCM_PIDFD
+ancillary message in each subsequently received message
+(see below).
+.IP
+As with
+.BR SO_PASSCRED ,
+when this option is set on a socket that is not bound to an address,
+the socket is autobound to a unique name in the abstract namespace
+when
+.BR connect (2)
+or
+.BR sendmsg (2)
+is called on it;
+see "Autobind feature", below.
+.IP
+The value given as an argument to
+.BR setsockopt (2)
+and returned as the result of
+.BR getsockopt (2)
+is an integer boolean flag.
+.TP
 .B SO_PASSSEC
 Enables receiving of the security context of the peer socket
 in an ancillary message of type

man/man7/unix.7 stream and sequenced-packet sockets, and for · line 403

Quoted 10 lines
man/man7/unix.7 +7 -0
@@ -367,6 +394,43 @@ stream and sequenced-packet sockets, and for
 .B AF_UNIX
 socket pairs created using
 .BR socketpair (2).
+.TP
+.BR SO_PEERPIDFD " (since Linux 6.5)"
+.\" commit 7b26952a91cf65ff1cc867a2382a8964d8c0ee7d
+This read-only socket option returns a new PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the peer process connected to this socket.

Not sure if this is worth noting here, but "peer process" is a bit unclear.

For example, if process A creates socekt A, calls listen(), and pass it to process B and close()s the fd, and process C creates socket C and connects to socket A, then getsockopt(C, SO_PEERPIDFD) will return process A, not B.

Quoted 87 lines
man/man7/unix.7 +67 -0
+This is the process whose credentials are returned by
+.BR SO_PEERCRED .
+Unlike the PID returned by
+.BR SO_PEERCRED ,
+the PID file descriptor cannot come to refer to a different process
+if the peer process terminates and its PID is reused.
+.IP
+The argument to
+.BR getsockopt (2)
+is a pointer to an
+.IR int ,
+in which the PID file descriptor is returned.
+The close-on-exec flag
+.RB ( FD_CLOEXEC )
+is set on the new file descriptor.
+.IP
+If the socket has no peer process,
+.BR getsockopt (2)
+fails with the error
+.BR ENODATA .
+.IP
+Since Linux 6.16,
+.\" commit 20b70e58961b855e61f2c6a7a19a38a64ad1bed6
+a PID file descriptor is returned
+even if the peer process has already terminated and been reaped.
+Before Linux 6.16,
+.BR getsockopt (2)
+failed with the error
+.B EINVAL
+in this case.
 .\"
 .SS Autobind feature
 If a
@@ -543,6 +607,42 @@ message, the
 .B SO_PASSCRED
 option must be enabled on the socket.
 .TP
+.BR SCM_PIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89
+Receive a PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the sending process.
+The data portion contains a single
+.IR int .
+The PID file descriptor refers to the process whose PID would be
+received in an
+.B SCM_CREDENTIALS
+message;
+if the sender specified
+.B SCM_CREDENTIALS
+ancillary data,
+it refers to the process with the PID given there.
+The close-on-exec flag
+.RB ( FD_CLOEXEC )
+is set on the new file descriptor.
+.IP
+If the PID file descriptor cannot be created,
+the data portion contains a negative error number instead.
+Since Linux 6.17,
+.\" commit c679d17d3f2d895b34e660673141ad250889831f
+a PID file descriptor is created
+even if the sending process has already terminated and been reaped.
+Before Linux 6.17,
+.B \-EINVAL
+was received in this case.
+.IP
+This message cannot be sent;
+it is generated by the kernel.
+To receive it, the
+.B SO_PASSPIDFD
+option must be enabled on the socket.
+.TP
 .B SCM_SECURITY
 Receive the security context (the security label)
 of the peer socket,
@@ -1273,6 +1373,7 @@ see
 and
 .BR seccomp_unotify (2).
 .SH SEE ALSO
+.BR pidfd_open (2),
 .BR recvmsg (2),
 .BR sendmsg (2),
 .BR socket (2),
Signature · 1 line
2.56.0
#35 lore raw

Re: [PATCH 6/7] man/man7/unix.7: Document SO_PASSPIDFD, SO_PEERPIDFD, and SCM_PIDFD

Quoted 45 lines · Kuniyuki Iwashima
Quoted 44 lines · Günther Noack

These were added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD") and 7b26952a91cf (2023-06-12; "net: core:
add getsockopt SO_PEERPIDFD"), both in Linux 6.5. They are the PID file descriptor counterparts of SO_PASSCRED/SCM_CREDENTIALS and SO_PEERCRED.

SO_PASSPIDFD
sk_setsockopt() and sk_getsockopt() in net/core/sock.c handle it as a boolean flag for AF_UNIX sockets (EOPNOTSUPP otherwise). Like SO_PASSCRED, it triggers autobind, which was already mentioned in "Autobind feature".

SCM_PIDFD
scm_pidfd_recv() in net/core/scm.c creates the PID file descriptor from scm->pid, which is the PID given in the sender's SCM_CREDENTIALS, or the sender's own PID by default. The fd is created with O_CLOEXEC by pidfd_prepare(). If pidfd_prepare() fails, its negative error number is put into the cmsg data. Sending SCM_PIDFD is rejected by __scm_send() (EINVAL).

Since Linux 6.17, with c679d17d3f2d (2025-07-04; "af_unix:
enable handing out pidfds for reaped tasks in SCM_PIDFD"), PIDFD_STALE is passed, so this also works for reaped processes. Before that, pidfd_prepare() returned -EINVAL for them.

SO_PEERPIDFD
sk_getsockopt() returns a PID file descriptor for sk_peer_pid, or fails with ENODATA if there is none.

Since Linux 6.16, with 20b70e58961b (2025-04-28; "net, pidfs:
enable handing out pidfds for reaped sk->sk_peer_pid"), this also works for reaped processes on AF_UNIX sockets. Before that, it failed with EINVAL.

Cc Alexander Mikhalitsyn <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Cc Eric Dumazet <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)
Quoted 12 lines

man/man7/unix.7 and returned as the result of · line 320

Quoted 10 lines
man/man7/unix.7 +2 -0
index 67dd10cc146f..a4457508fd2a 100644
@@ -316,6 +316,33 @@ and returned as the result of
 .BR getsockopt (2)
 is an integer boolean flag.
 .TP
+.BR SO_PASSPIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Why does pidfd only have SHA1 ?

I'm removing it from the code and keeping it in the commit message, as in the other commits above.

Quoted 15 lines

man/man7/unix.7 stream and sequenced-packet sockets, and for · line 403

Quoted 11 lines
man/man7/unix.7 +7 -0
@@ -367,6 +394,43 @@ stream and sequenced-packet sockets, and for
 .B AF_UNIX
 socket pairs created using
 .BR socketpair (2).
+.TP
+.BR SO_PEERPIDFD " (since Linux 6.5)"
+.\" commit 7b26952a91cf65ff1cc867a2382a8964d8c0ee7d
+This read-only socket option returns a new PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the peer process connected to this socket.

Not sure if this is worth noting here, but "peer process" is a bit unclear.

Quoted 4 lines

For example, if process A creates socekt A, calls listen(), and pass it to process B and close()s the fd, and process C creates socket C and connects to socket A, then getsockopt(C, SO_PEERPIDFD) will return process A, not B.

I think it is OK; The phrase "peer process connected to this socket" is adapted from the description of SO_PEERCRED, and to clarify, the next sentence refers to the description of SO_PEERCRED in the same file:

Quoted 2 lines

man/man7/unix.7 stream and sequenced-packet sockets, and for · line 405

Quoted 2 lines
man/man7/unix.7 +2 -0
+This is the process whose credentials are returned by
+.BR SO_PEERCRED .

^ Here

I think the SO_PEERCRED description is good:

This read-only socket option returns the credentials of the peer process connected to this socket. The returned credentials are those that were in effect at the time of the call to connect(2), listen(2) or sockepair(2).

(It is good, because it puts the common use case first, where the entire BSD socket dance is done by the same process, and then clarifies the details for the people who need them.)

So this seems fair to me, imho. Let me know whether that seems acceptable to you.

–Günther

#36 lore raw

Re: [PATCH 6/7] man/man7/unix.7: Document SO_PASSPIDFD, SO_PEERPIDFD, and SCM_PIDFD

Quoted 100 lines · Günther Noack
Quoted 45 lines · Kuniyuki Iwashima
Quoted 44 lines · Günther Noack

These were added in linux.git 5e2ff6704a27 (2023-06-12; "scm: add SO_PASSPIDFD and SCM_PIDFD") and 7b26952a91cf (2023-06-12; "net: core:
add getsockopt SO_PEERPIDFD"), both in Linux 6.5. They are the PID file descriptor counterparts of SO_PASSCRED/SCM_CREDENTIALS and SO_PEERCRED.

SO_PASSPIDFD
sk_setsockopt() and sk_getsockopt() in net/core/sock.c handle it as a boolean flag for AF_UNIX sockets (EOPNOTSUPP otherwise). Like SO_PASSCRED, it triggers autobind, which was already mentioned in "Autobind feature".

SCM_PIDFD
scm_pidfd_recv() in net/core/scm.c creates the PID file descriptor from scm->pid, which is the PID given in the sender's SCM_CREDENTIALS, or the sender's own PID by default. The fd is created with O_CLOEXEC by pidfd_prepare(). If pidfd_prepare() fails, its negative error number is put into the cmsg data. Sending SCM_PIDFD is rejected by __scm_send() (EINVAL).

Since Linux 6.17, with c679d17d3f2d (2025-07-04; "af_unix:
enable handing out pidfds for reaped tasks in SCM_PIDFD"), PIDFD_STALE is passed, so this also works for reaped processes. Before that, pidfd_prepare() returned -EINVAL for them.

SO_PEERPIDFD
sk_getsockopt() returns a PID file descriptor for sk_peer_pid, or fails with ENODATA if there is none.

Since Linux 6.16, with 20b70e58961b (2025-04-28; "net, pidfs:
enable handing out pidfds for reaped sk->sk_peer_pid"), this also works for reaped processes on AF_UNIX sockets. Before that, it failed with EINVAL.

Cc Alexander Mikhalitsyn <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Kuniyuki Iwashima <[email protected]>
Cc Eric Dumazet <[email protected]>
Assisted-by LLM
---
 man/man7/unix.7 | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)
Quoted 12 lines

man/man7/unix.7 and returned as the result of · line 320

Quoted 10 lines
man/man7/unix.7 +2 -0
index 67dd10cc146f..a4457508fd2a 100644
@@ -316,6 +316,33 @@ and returned as the result of
 .BR getsockopt (2)
 is an integer boolean flag.
 .TP
+.BR SO_PASSPIDFD " (since Linux 6.5)"
+.\" commit 5e2ff6704a275be009be8979af17c52361b79b89

Why does pidfd only have SHA1 ?

I'm removing it from the code and keeping it in the commit message, as in the other commits above.

Quoted 15 lines

man/man7/unix.7 stream and sequenced-packet sockets, and for · line 403

Quoted 11 lines
man/man7/unix.7 +7 -0
@@ -367,6 +394,43 @@ stream and sequenced-packet sockets, and for
 .B AF_UNIX
 socket pairs created using
 .BR socketpair (2).
+.TP
+.BR SO_PEERPIDFD " (since Linux 6.5)"
+.\" commit 7b26952a91cf65ff1cc867a2382a8964d8c0ee7d
+This read-only socket option returns a new PID file descriptor
+(see
+.BR pidfd_open (2))
+referring to the peer process connected to this socket.

Not sure if this is worth noting here, but "peer process" is a bit unclear.

Quoted 4 lines

For example, if process A creates socekt A, calls listen(), and pass it to process B and close()s the fd, and process C creates socket C and connects to socket A, then getsockopt(C, SO_PEERPIDFD) will return process A, not B.

I think it is OK; The phrase "peer process connected to this socket" is adapted from the description of SO_PEERCRED, and to clarify, the next sentence refers to the description of SO_PEERCRED in the same file:

Quoted 2 lines

man/man7/unix.7 stream and sequenced-packet sockets, and for · line 405

Quoted 2 lines
man/man7/unix.7 +2 -0
+This is the process whose credentials are returned by
+.BR SO_PEERCRED .

^ Here

I think the SO_PEERCRED description is good:

This read-only socket option returns the credentials of the peer process connected to this socket. The returned credentials are those that were in effect at the time of the call to connect(2), listen(2) or sockepair(2).

Quoted 6 lines

(It is good, because it puts the common use case first, where the entire BSD socket dance is done by the same process, and then clarifies the details for the people who need them.)

So this seems fair to me, imho. Let me know whether that seems acceptable to you.

Probably okay as is. Maybe not many people is using socket activation w/ systemd.

#37 lore raw

PATCH 7/7 man/man7/unix.7: Document SO_PASSRIGHTS

In reply to Günther Noack

SO_PASSRIGHTS was added in linux.git 77cbe1a6d873 (2025-05-23; "af_unix: Introduce SO_PASSRIGHTS."), in Linux 6.16. It lets a receiver refuse file descriptors passed with SCM_RIGHTS, so that an untrusted peer cannot leave it with file descriptors whose final close() blocks for a long time (for example, a file on a hung NFS mount or a FUSE-controlled file).

The option is enabled by default (unix_create1()). When the receiving socket has it disabled, unix_dgram_sendmsg(), unix_stream_sendmsg(), and queue_oob() fail with EPERM if the message carries file descriptors. The check happens at send time, so messages that are already queued are unaffected.

Also add the new EPERM case to ERRORS.

---
 man/man7/unix.7 | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
man/man7/unix.7 +37 -0
index a4457508fd2a..1aa223b2c79d 100644
@@ -343,6 +343,35 @@ and returned as the result of
 .BR getsockopt (2)
 is an integer boolean flag.
 .TP
+.BR SO_PASSRIGHTS " (since Linux 6.16)"
+.\" commit 77cbe1a6d8730a07f99f9263c2d5f2304cf5e830
+This socket option controls whether the socket accepts
+file descriptors from its peers in
+.B SCM_RIGHTS
+ancillary messages (see below).
+It is enabled by default.
+When it is disabled,
+.BR sendmsg (2)
+calls that pass file descriptors to this socket
+fail with the error
+.BR EPERM .
+The option is checked when the message is sent;
+disabling it does not affect messages
+that are already queued on the socket.
+.IP
+Processes that do not expect to receive file descriptors
+can disable this option.
+Once the file descriptors have been passed,
+closing them is the responsibility of the receiving process,
+and this may block for a long time,
+for example for a file on an unresponsive network file system.
+.IP
+The value given as an argument to
+.BR setsockopt (2)
+and returned as the result of
+.BR getsockopt (2)
+is an integer boolean flag.
+.TP
 .B SO_PASSSEC
 Enables receiving of the security context of the peer socket
 in an ancillary message of type
@@ -893,6 +922,14 @@ or
 specified a datagram socket as the remote address,
 but that socket is connected to a different socket.
 .TP
+.B EPERM
+The sender passed file descriptors in an
+.B SCM_RIGHTS
+ancillary message,
+but the receiving socket has the
+.B SO_PASSRIGHTS
+option disabled.
+.TP
 .B EPIPE
 Remote socket was closed on a stream socket.
 If enabled, a
Signature · 1 line
2.56.0