sd-varlink: enforce some queuing limits + document associated api functions (#37289)

This commit is contained in:
Lennart Poettering
2025-04-30 12:19:21 +02:00
committed by GitHub
7 changed files with 301 additions and 5 deletions

2
TODO
View File

@@ -383,8 +383,6 @@ Features:
credential (would probably require some binary that converts credential to
User= parameter?
* sd-varlink should probably enforce a limit on queued outgoing replies
* systemd-firstboot: optionally install an ssh key for root for offline use.
* Allocate UIDs/GIDs automatically in userdbctl load-credentials if none are

View File

@@ -889,6 +889,8 @@ manpages = [
'sd_uid_get_sessions',
'sd_uid_is_on_seat'],
'HAVE_PAM'],
['sd_varlink_push_fd', '3', ['sd_varlink_push_dup_fd'], ''],
['sd_varlink_send', '3', ['sd_varlink_sendb', 'sd_varlink_sendbo'], ''],
['sd_varlink_set_description', '3', ['sd_varlink_get_description'], ''],
['sd_watchdog_enabled', '3', [], ''],
['shutdown', '8', [], ''],

134
man/sd_varlink_push_fd.xml Normal file
View File

@@ -0,0 +1,134 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd_varlink_push_fd" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_varlink_push_fd</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_varlink_push_fd</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_varlink_push_fd</refname>
<refname>sd_varlink_push_dup_fd</refname>
<refpurpose>Submit a file descriptor to send along with the next outgoing Varlink message</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-varlink.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_varlink_push_fd</function></funcdef>
<paramdef>sd_varlink *<parameter>link</parameter></paramdef>
<paramdef>int <parameter>fd</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_varlink_push_dup_fd</function></funcdef>
<paramdef>sd_varlink *<parameter>link</parameter></paramdef>
<paramdef>int <parameter>fd</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_varlink_push_fd()</function> submits a file descriptor to send along with the next
outgoing Varlink message. Takes a Varlink connection object and a file descriptor as parameter. The file
descriptor is not duplicated, and hence ownership of the file descriptor is passed to the Varlink
connection object (only on success; on failure the caller retains ownership). Once the file descriptor
has been written to the underlying transport socket it is automatically closed. The calling application
code should not touch the file descriptor or close it on its own, otherwise it will interfere with the
Varlink protocol implementation. This call is only supported if the backing transport supports file
descriptor passing (effectively this means the functionality is supported on local
<constant>AF_UNIX</constant> only), and the concept is not part of the Varlink protocol, but simply a
feature of the underlying transport.</para>
<para><function>sd_varlink_push_dup_fd()</function> is identical to
<function>sd_varlink_push_fd()</function>, except that the file descriptor is duplicated automatically,
and the calling application code hence retains ownership of the provided file descriptor, and must close
it on its own.</para>
<para>Note that file descriptor passing is only permitted after a call to
<function>sd_varlink_set_allow_fd_passing_output()</function> that enables it, otherwise these calls will
fail with <constant>-EPERM</constant>.</para>
<para>Note that on Linux a maximum of 253 file descriptors may be enqueued on
<constant>AF_UNIX</constant> sockets at once. Attempting to enqueue more on a single Varlink message will
fail with <constant>-ENOBUFS</constant>.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_varlink_push_fd()</function> and
<function>sd_varlink_push_dup_fd()</function> return a non-negative integer. On failure, they return a
negative errno-style error code. </para>
<refsect2>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>An argument is invalid.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EBADF</constant></term>
<listitem><para>The provided file descriptor is not valid.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EPERM</constant></term>
<listitem><para>File descriptor passing has not been enabled via
<function>sd_varlink_set_allow_fd_passing_output()</function>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOBUFS</constant></term>
<listitem><para>The maximum of 253 file descriptors have already been submitted for the next
outgoing Varlink message, no further descriptors may be enqueued for this message.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Memory allocation failed.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>History</title>
<para><function>sd_varlink_push_fd()</function> and <function>sd_varlink_push_dup_fd()</function> were added in version 257.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

151
man/sd_varlink_send.xml Normal file
View File

@@ -0,0 +1,151 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd_varlink_send" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_varlink_send</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_varlink_send</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_varlink_send</refname>
<refname>sd_varlink_sendb</refname>
<refname>sd_varlink_sendbo</refname>
<refpurpose>Enqueues a Varlink method call, not expecting a reply</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-varlink.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_varlink_send</function></funcdef>
<paramdef>sd_varlink *<parameter>link</parameter></paramdef>
<paramdef>const char *<parameter>method</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>parameters</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_varlink_sendb</function></funcdef>
<paramdef>sd_varlink *<parameter>link</parameter></paramdef>
<paramdef>const char *<parameter>method</parameter></paramdef>
<paramdef></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_varlink_sendbo</function></funcdef>
<paramdef>sd_varlink *<parameter>link</parameter></paramdef>
<paramdef>const char *<parameter>method</parameter></paramdef>
<paramdef></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_varlink_send()</function> submits a method call via a Varlink connection. It takes the
Varlink connection object, a method name as string parameter, and a JSON object containing the parameters
to pass as function parameters. This call is asynchronous: the message will not be delivered immediately
but only once
<citerefentry><refentrytitle>sd_varlink_process</refentrytitle><manvolnum>3</manvolnum></citerefentry> is
invoked (which will happen automatically in one of the following event loop iterations if the Varlink
connection is attached to an even loop).</para>
<para><function>sd_varlink_sendb()</function> is similar to <function>sd_varlink_send()</function>, but
instead of expecting a fully constructed <type>sd_json_variant</type> object carrying the parameters,
this object is constructed on-the-fly directly from the parameter list, in a style identical to
<citerefentry><refentrytitle>sd_json_build</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
<para><function>sd_varlink_sendbo()</function> is identical to <function>sd_varlink_sendb()</function>,
but an enclosing object is implicitly added, so that the parameter list is expected to consist of field
pairs only. For details about the expected argument list, see
<citerefentry><refentrytitle>sd_json_buildo</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
<para>Use <function>sd_varlink_send()</function>, <function>sd_varlink_sendb()</function> and
<function>sd_varlink_sendbo()</function> only if no method call results are required, as they neither
provide return parameters nor success/failure information. Use
<citerefentry><refentrytitle>sd_varlink_call</refentrytitle><manvolnum>3</manvolnum></citerefentry> (and
related calls) to submit a method call synchronously, returning the server's response.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_varlink_send()</function>, <function>sd_varlink_sendb()</function> and
<function>sd_varlink_sendbo()</function> return a non-negative integer. On failure, they return a
negative errno-style error code.</para>
<refsect2>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>An argument is invalid.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Memory allocation failed.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOTCONN</constant></term>
<listitem><para>The Varlink connection object is not connected.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EBUSY</constant></term>
<listitem><para>The Varlink connection object is already used for other purposes, i.e. executing a
method call or similar.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOBUFS</constant></term>
<listitem><para>The internal limit of queued messages for the Varlink connection has been
reached. This limit is set very high, and hitting it typically indicates that the Varlink
connection object is stalled — possibly because <function>sd_varlink_process()</function> has not
been called regularly enough, or because the peer is not processing any queued messages. This limit
is a safety precaution to ensure a stalled peer will not result in unbounded memory allocations on
the client side.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>History</title>
<para><function>sd_varlink_send()</function>, <function>sd_varlink_sendb()</function>,
<function>sd_varlink_sendbo()</function> were added in version 257.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_varlink_call</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_varlink_build</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -24,7 +24,7 @@
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-link.h&gt;</funcsynopsisinfo>
<funcsynopsisinfo>#include &lt;systemd/sd-varlink.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_varlink_set_description</function></funcdef>

View File

@@ -41,6 +41,7 @@
#define VARLINK_BUFFER_MAX (16U*1024U*1024U)
#define VARLINK_READ_SIZE (64U*1024U)
#define VARLINK_COLLECT_MAX 1024U
#define VARLINK_QUEUE_MAX (64U*1024U)
static const char* const varlink_state_table[_VARLINK_STATE_MAX] = {
[VARLINK_IDLE_CLIENT] = "idle-client",
@@ -631,6 +632,7 @@ static void varlink_clear(sd_varlink *v) {
LIST_CLEAR(queue, v->output_queue, varlink_json_queue_item_free);
v->output_queue_tail = NULL;
v->n_output_queue = 0;
v->event = sd_event_unref(v->event);
@@ -1946,6 +1948,9 @@ static int varlink_enqueue_json(sd_varlink *v, sd_json_variant *m) {
if (v->n_pushed_fds == 0 && !v->output_queue)
return varlink_format_json(v, m);
if (v->n_output_queue >= VARLINK_QUEUE_MAX)
return -ENOBUFS;
/* Otherwise add a queue entry for this */
q = varlink_json_queue_item_new(m, v->pushed_fds, v->n_pushed_fds);
if (!q)
@@ -1955,6 +1960,7 @@ static int varlink_enqueue_json(sd_varlink *v, sd_json_variant *m) {
LIST_INSERT_AFTER(queue, v->output_queue, v->output_queue_tail, q);
v->output_queue_tail = q;
v->n_output_queue++;
return 0;
}
@@ -1968,6 +1974,9 @@ static int varlink_format_queue(sd_varlink *v) {
while (v->output_queue) {
_cleanup_free_ int *array = NULL;
assert(v->n_output_queue > 0);
VarlinkJsonQueueItem *q = v->output_queue;
if (v->n_output_fds > 0) /* unwritten fds? if we'd add more we'd corrupt the fd message boundaries, hence wait */
@@ -1992,6 +2001,7 @@ static int varlink_format_queue(sd_varlink *v) {
LIST_REMOVE(queue, v->output_queue, q);
if (!v->output_queue)
v->output_queue_tail = NULL;
v->n_output_queue--;
varlink_json_queue_item_free(q);
}
@@ -3112,8 +3122,8 @@ _public_ int sd_varlink_push_fd(sd_varlink *v, int fd) {
if (!v->allow_fd_passing_output)
return -EPERM;
if (v->n_pushed_fds >= INT_MAX)
return -ENOMEM;
if (v->n_pushed_fds >= SCM_MAX_FD) /* Kernel doesn't support more than 253 fds per message, refuse early hence */
return -ENOBUFS;
if (!GREEDY_REALLOC(v->pushed_fds, v->n_pushed_fds + 1))
return -ENOMEM;

View File

@@ -142,6 +142,7 @@ struct sd_varlink {
* with preceding or following messages. */
LIST_HEAD(VarlinkJsonQueueItem, output_queue);
VarlinkJsonQueueItem *output_queue_tail;
size_t n_output_queue;
/* The fds to associate with the next message that is about to be enqueued. The user first pushes the
* fds it intends to send via varlink_push_fd() into this queue, and then once the message data is