<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmo-netif/+/18628">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">src/stream: Work around more Linux kernel ABI breakage<br><br>Back in Change-Id Ia95dd1f9ffed9f743c049e05797b1a6f1f9f8c69 we tried<br>to work-around kernel ABI breakage introduced in kernel >= v5.5, but it<br>seems that there have already been similar ABI breakages in v4.11 and v4.12.<br><br>See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/include/uapi/linux/sctp.h?id=b6e6b5f1da7e8d092f86a4351802c27c0170c5a5<br>and https://marc.info/?l=linux-sctp&m=158729301516157&w=2 for the most<br>recent incarnation.<br><br>See https://osmocom.org/issues/4573#note-6 for all known cases<br>of SCTP_EVENTS ABI breakage.<br><br>Closes: OS#4573<br>Change-Id: Icc49f347cdc0bb77a5c0e230597d662ac35b4acc<br>---<br>M src/stream.c<br>1 file changed, 93 insertions(+), 20 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/stream.c b/src/stream.c</span><br><span>index 6e4c461..44ac1b4 100644</span><br><span>--- a/src/stream.c</span><br><span>+++ b/src/stream.c</span><br><span>@@ -73,6 +73,97 @@</span><br><span> #define MSG_NOSIGNAL 0</span><br><span> #endif</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* is any of the bytes from offset .. u8_size in 'u8' non-zero? return offset or -1 if all zero */</span><br><span style="color: hsl(120, 100%, 40%);">+static int byte_nonzero(const uint8_t *u8, unsigned int offset, unsigned int u8_size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int j;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      for (j = offset; j < u8_size; j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (u8[j] != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return j;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int sctp_sockopt_event_subscribe_size = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int determine_sctp_sockopt_event_subscribe_size(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t buf[256];</span><br><span style="color: hsl(120, 100%, 40%);">+     socklen_t buf_len = sizeof(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+      int sd, rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* only do this once */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (sctp_sockopt_event_subscribe_size != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (sd < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return sd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = getsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, buf, &buf_len);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  sctp_sockopt_event_subscribe_size = buf_len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DLINP, LOGL_INFO, "sizes of 'struct sctp_event_subscribe': compile-time %zu, kernel: %u\n",</span><br><span style="color: hsl(120, 100%, 40%);">+            sizeof(struct sctp_event_subscribe), sctp_sockopt_event_subscribe_size);</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Attempt to work around Linux kernel ABI breakage</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The Linux kernel ABI for the SCTP_EVENTS socket option has been broken repeatedly.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - until commit 35ea82d611da59f8bea44a37996b3b11bb1d3fd7 ( kernel < 4.11), the size is 10 bytes</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - in 4.11 it is 11 bytes</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - in 4.12 .. 5.4 it is 13 bytes</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - in kernels >= 5.5 it is 14 bytes</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This wouldn't be a problem if the kernel didn't have a "stupid" assumption that the structure</span><br><span style="color: hsl(120, 100%, 40%);">+ * size passed by userspace will match 1:1 the length of the structure at kernel compile time. In</span><br><span style="color: hsl(120, 100%, 40%);">+ * an ideal world, it would just use the known first bytes and assume the remainder is all zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ * But as it doesn't do that, let's try to work around this */</span><br><span style="color: hsl(120, 100%, 40%);">+static int sctp_setsockopt_events_linux_workaround(int fd, const struct sctp_event_subscribe *event)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   const unsigned int compiletime_size = sizeof(*event);</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (determine_sctp_sockopt_event_subscribe_size() < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DLINP, LOGL_ERROR, "Cannot determine SCTP_EVENTS socket option size\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (compiletime_size == sctp_sockopt_event_subscribe_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* no kernel workaround needed */</span><br><span style="color: hsl(120, 100%, 40%);">+             return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, compiletime_size);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (compiletime_size < sctp_sockopt_event_subscribe_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* we are using an older userspace with a more modern kernel and hence need</span><br><span style="color: hsl(120, 100%, 40%);">+            * to pad the data */</span><br><span style="color: hsl(120, 100%, 40%);">+         uint8_t buf[sctp_sockopt_event_subscribe_size];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             memcpy(buf, event, compiletime_size);</span><br><span style="color: hsl(120, 100%, 40%);">+         memset(buf + sizeof(*event), 0, sctp_sockopt_event_subscribe_size - compiletime_size);</span><br><span style="color: hsl(120, 100%, 40%);">+                return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, buf, sctp_sockopt_event_subscribe_size);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else /* if (compiletime_size > sctp_sockopt_event_subscribe_size) */ {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* we are using a newer userspace with an older kernel and hence need to truncate</span><br><span style="color: hsl(120, 100%, 40%);">+              * the data - but only if the caller didn't try to enable any of the events of the</span><br><span style="color: hsl(120, 100%, 40%);">+                 * truncated portion */</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = byte_nonzero((const uint8_t *)event, sctp_sockopt_event_subscribe_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                            compiletime_size);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     LOGP(DLINP, LOGL_ERROR, "Kernel only supports sctp_event_subscribe of %u bytes, "</span><br><span style="color: hsl(120, 100%, 40%);">+                           "but caller tried to enable more modern event at offset %u\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              sctp_sockopt_event_subscribe_size, rc);</span><br><span style="color: hsl(120, 100%, 40%);">+                       return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, sctp_sockopt_event_subscribe_size);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int sctp_sock_activate_events(int fd)</span><br><span> {</span><br><span> #ifdef HAVE_LIBSCTP</span><br><span>@@ -89,28 +180,10 @@</span><br><span>    event.sctp_shutdown_event = 1;</span><br><span>       /* IMPORTANT: Do NOT enable sender_dry_event here, see</span><br><span>        * https://bugzilla.redhat.com/show_bug.cgi?id=1442784 */</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS,</span><br><span style="color: hsl(0, 100%, 40%);">-                  &event, sizeof(event));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /*</span><br><span style="color: hsl(0, 100%, 40%);">-       * Attempt to work around kernel ABI breakage</span><br><span style="color: hsl(0, 100%, 40%);">-    *</span><br><span style="color: hsl(0, 100%, 40%);">-       * In kernel commit b6e6b5f1da7e8d092f86a4351802c27c0170c5a5, the</span><br><span style="color: hsl(0, 100%, 40%);">-        * struct sctp_event_subscribe had a u8 field added to it at the end, thus</span><br><span style="color: hsl(0, 100%, 40%);">-       * breaking ABI.</span><br><span style="color: hsl(0, 100%, 40%);">-         * See https://marc.info/?l=linux-sctp&m=158729301516157&w=2 for discussion.</span><br><span style="color: hsl(0, 100%, 40%);">-     *</span><br><span style="color: hsl(0, 100%, 40%);">-       * We attempt to work around the issue where the kernel header are new</span><br><span style="color: hsl(0, 100%, 40%);">-   * and running kernel is old, by forcing the size of the struct to 13 which</span><br><span style="color: hsl(0, 100%, 40%);">-      * is the "old" size</span><br><span style="color: hsl(0, 100%, 40%);">-   */</span><br><span style="color: hsl(0, 100%, 40%);">-     if ((rc < 0) && (sizeof(event) != 13))</span><br><span style="color: hsl(0, 100%, 40%);">-               rc = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS,</span><br><span style="color: hsl(0, 100%, 40%);">-                          &event, 13);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = sctp_setsockopt_events_linux_workaround(fd, &event);</span><br><span>        if (rc < 0)</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGP(DLINP, LOGL_ERROR, "couldn't activate SCTP events "</span><br><span style="color: hsl(0, 100%, 40%);">-               "on FD %u\n", fd);</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DLINP, LOGL_ERROR, "couldn't activate SCTP events on FD %u\n", fd);</span><br><span>       return rc;</span><br><span> #else</span><br><span>  return -1;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmo-netif/+/18628">change 18628</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmo-netif/+/18628"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmo-netif </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Icc49f347cdc0bb77a5c0e230597d662ac35b4acc </div>
<div style="display:none"> Gerrit-Change-Number: 18628 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-CC: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>