]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-daemon: add fd array size safety check to sd_notify_with_fds()
authorLennart Poettering <lennart@poettering.net>
Mon, 4 Nov 2024 10:18:29 +0000 (11:18 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Sat, 17 May 2025 11:43:20 +0000 (12:43 +0100)
The previous commit removed the UINT_MAX check for the fd array. Let's
now re-add one, but at a better place, and with a more useful limit. As
it turns out the kernel does not allow passing more than 253 fds at the
same time, hence use that as limit. And do so immediately before
calculating the control buffer size, so that we catch multiplication
overflows.

(cherry picked from commit cb42df5310e701b751331ae62432e3fb0df2f660)

man/sd_notify.xml
src/basic/missing_socket.h
src/libsystemd/sd-daemon/sd-daemon.c
src/notify/notify.c

index af45fbd1a6065305dc9067ebcf25431e0ffd0832..f1a1742c0d89294004faf82539027635bd29e52b 100644 (file)
     successfully. Specifically, no error is returned when a file descriptor is attempted to be stored using
     <varname>FDSTORE=1</varname> but the service is not actually configured to permit storing of file
     descriptors (see above).</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-E2BIG</constant></term>
+
+          <listitem><para>More file descriptors passed at once than the system allows. On Linux the number of
+          file descriptors that may be passed across <constant>AF_UNIX</constant> sockets at once is 253, see
+          <citerefentry
+          project='man-pages'><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+          details.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
   </refsect1>
 
   <refsect1>
index 47cc7626aa357a94638ca17cd77f373580f166da..cece89ee72fbd834633d6fa3abab6fc12dff55e9 100644 (file)
@@ -102,3 +102,10 @@ struct sockaddr_vm {
 #ifndef SIOCGSKNS
 #define SIOCGSKNS 0x894C
 #endif
+
+/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much
+ * useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define
+ * here. */
+#ifndef SCM_MAX_FD
+#define SCM_MAX_FD 253U
+#endif
index 82e677b21509ed55eccc80ae0fbd1334f6355aba..fd50aa73d76a5ec8d23c331f1636a6dfcd0e2954 100644 (file)
@@ -470,6 +470,12 @@ static int pid_notify_with_fds_internal(
         assert_return(state, -EINVAL);
         assert_return(fds || n_fds == 0, -EINVAL);
 
+        /* Let's make sure the multiplications below don't overflow, and also return a recognizable error in
+         * case the caller tries to send more fds than the kernel limit. The kernel would return EINVAL which
+         * is not too useful I'd say. */
+        if (n_fds > SCM_MAX_FD)
+                return -E2BIG;
+
         const char *e = getenv("NOTIFY_SOCKET");
         if (!e)
                 return 0;
index 32bd6e6a7ae9a044635ccee11a6c49904d4dc90d..c01d5b05be808328651bc74600a69f59ae2aceaa 100644 (file)
@@ -431,6 +431,8 @@ static int run(int argc, char* argv[]) {
                 r = sd_pid_notify_with_fds(arg_pid, /* unset_environment= */ false, msg, a, k);
 
         }
+        if (r == -E2BIG)
+                return log_error_errno(r, "Too many file descriptors passed.");
         if (r < 0)
                 return log_error_errno(r, "Failed to notify init system: %m");
         if (r == 0)