]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-daemon: add fd array size safety check to sd_notify_with_fds() 35011/head
authorLennart Poettering <lennart@poettering.net>
Mon, 4 Nov 2024 10:18:29 +0000 (11:18 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 4 Nov 2024 11:10:09 +0000 (12:10 +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.

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

index 5e6c9d6dbcdb429d5129ab1e9bc9c2344238fc21..0c993e31f77bd10819d9b534f2c0d47f294d4e65 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 55de40a321618a9ed764d45f9dada2c167184178..8460ce13bf33a6823df142eb49be109451ada3d3 100644 (file)
 #ifndef IPV6_RECVFRAGSIZE
 #define IPV6_RECVFRAGSIZE 77
 #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 faf45c27509298a7b6e6c1c2159a2d19ad6bd065..4d3d678cb451b1fe0597a765abffe69408207b42 100644 (file)
@@ -472,6 +472,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 8937457ec99f842e93b8f48a32fa29b066edc2cc..6afb9560c659642c7f6a728a70dd148bb0305a83 100644 (file)
@@ -453,6 +453,8 @@ static int run(int argc, char* argv[]) {
                 r = sd_pid_notify_with_fds(arg_pid.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)