<p>Neels Hofmeyr <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/13277">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  osmith: Looks good to me, but someone else must approve
  Harald Welte: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add caller-owns-msgb variant osmo_sccp_user_sap_down_nofree()<br><br>Add osmo_sccp_user_sap_down_nofree(), which is identical to<br>osmo_sccp_user_sap_down(), but doesn't imply a msgb_free().<br><br>To implement that, sccp_sclc_user_sap_down_nofree() with the same msgb<br>semantics is required.<br><br>Rationale:<br><br>Avoiding msgb leaks is easiest if the caller retains ownership of the msgb.<br>Take this hypothetical chain where leaks are obviously avoided:<br><br>  void send()<br>  {<br>         msg = msgb_alloc();<br>   dispatch(msg);<br>        msgb_free(msg);<br>  }<br><br>  void dispatch(msg)<br>  {<br>     osmo_fsm_inst_dispatch(fi, msg);<br>  }<br><br>  void fi_on_event(fi, data)<br>  {<br>    if (socket_is_ok)<br>             socket_write((struct msgb*)data);<br>  }<br><br>  void socket_write(msgb)<br>  {<br>      if (!ok1)<br>             return;<br>       if (ok2) {<br>            if (!ok3)<br>                     return;<br>               write(sock, msg->data);<br>    }<br>  }<br><br>However, if the caller passes ownership down to the msgb consumer, things<br>become nightmarishly complex:<br><br>  void send()<br>  {<br>      msg = msgb_alloc();<br>   rc = dispatch(msg);<br>   /* dispatching event failed? */<br>       if (rc)<br>               msgb_free(msg);<br>  }<br><br>  int dispatch(msg)<br>  {<br>      if (osmo_fsm_inst_dispatch(fi, msg))<br>          return -1;<br>    if (something_else())<br>         return -1; // <-- double free!<br>  }<br><br>  void fi_on_event(fi, data)<br>  {<br>   if (socket_is_ok) {<br>           socket_write((struct msgb*)data);<br>     else<br>          /* socket didn't consume? */<br>              msgb_free(data);<br>  }<br><br>  int socket_write(msgb)<br>  {<br>        if (!ok1)<br>             return -1; // <-- leak!<br>    if (ok2) {<br>            if (!ok3)<br>                     goto out;<br>             write(sock, msg->data);<br>    }<br>  out:<br>        msgb_free(msg);<br>    return -2;<br>  }<br><br>If any link in this call chain fails to be aware of the importance to return a<br>failed RC or to free a msgb if the chain is broken, or to not return a failed<br>RC if the msgb is consumed, we have a hidden msgb leak or double free.<br><br>This is the case with osmo_sccp_user_sap_down(). In new osmo-msc, passing data<br>through various FSM instances, there is high potential for leak/double-free<br>bugs. A very large brain is required to track down every msgb path.<br><br>osmo_sccp_user_sap_down_nofree() makes this problem trivial to solve even for<br>humans.<br><br>Change-Id: Ic818efa78b90f727e1a94c18b60d9a306644f340<br>---<br>M include/osmocom/sigtran/sccp_sap.h<br>M src/sccp_internal.h<br>M src/sccp_sclc.c<br>M src/sccp_scoc.c<br>4 files changed, 38 insertions(+), 23 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/sigtran/sccp_sap.h b/include/osmocom/sigtran/sccp_sap.h</span><br><span>index 84d762c..f64b7aa 100644</span><br><span>--- a/include/osmocom/sigtran/sccp_sap.h</span><br><span>+++ b/include/osmocom/sigtran/sccp_sap.h</span><br><span>@@ -263,6 +263,7 @@</span><br><span>                     osmo_prim_cb prim_cb, uint16_t ssn);</span><br><span> </span><br><span> int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_sccp_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);</span><br><span> </span><br><span> struct osmo_ss7_instance *</span><br><span> osmo_sccp_addr_by_name(struct osmo_sccp_addr *dest_addr,</span><br><span>diff --git a/src/sccp_internal.h b/src/sccp_internal.h</span><br><span>index ad8a6fd..8df6c9b 100644</span><br><span>--- a/src/sccp_internal.h</span><br><span>+++ b/src/sccp_internal.h</span><br><span>@@ -116,6 +116,7 @@</span><br><span> </span><br><span> /* SCU -> SCLC */</span><br><span> int sccp_sclc_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);</span><br><span> </span><br><span> struct msgb *sccp_msgb_alloc(const char *name);</span><br><span> </span><br><span>diff --git a/src/sccp_sclc.c b/src/sccp_sclc.c</span><br><span>index db67660..218fb56 100644</span><br><span>--- a/src/sccp_sclc.c</span><br><span>+++ b/src/sccp_sclc.c</span><br><span>@@ -115,15 +115,14 @@</span><br><span>      return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Main entrance function for primitives from SCCP User</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Main entrance function for primitives from SCCP User.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The caller is required to free oph->msg, otherwise the same as sccp_sclc_user_sap_down().</span><br><span>  *  \param[in] scu SCCP User who is sending the primitive</span><br><span>  *  \param[on] oph Osmocom primitive header of the primitive</span><br><span>  *  \returns 0 on success; negtive in case of error */</span><br><span style="color: hsl(0, 100%, 40%);">-int sccp_sclc_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span> {</span><br><span>      struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct msgb *msg = prim->oph.msg;</span><br><span style="color: hsl(0, 100%, 40%);">-    int rc = 0;</span><br><span> </span><br><span>      /* we get called from osmo_sccp_user_sap_down() which already</span><br><span>         * has debug-logged the primitive */</span><br><span>@@ -131,20 +130,26 @@</span><br><span>         switch (OSMO_PRIM_HDR(&prim->oph)) {</span><br><span>  case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST):</span><br><span>           /* Connectionless by-passes this altogether */</span><br><span style="color: hsl(0, 100%, 40%);">-          rc = xua_gen_encode_and_send(scu, -1, prim, SUA_CL_CLDT);</span><br><span style="color: hsl(0, 100%, 40%);">-               goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             return xua_gen_encode_and_send(scu, -1, prim, SUA_CL_CLDT);</span><br><span>  default:</span><br><span>             LOGP(DLSCCP, LOGL_ERROR, "Received unknown SCCP User "</span><br><span>                  "primitive %s from user\n",</span><br><span>                osmo_scu_prim_name(&prim->oph));</span><br><span style="color: hsl(0, 100%, 40%);">-            rc = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span>   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-out:</span><br><span style="color: hsl(0, 100%, 40%);">-    /* the SAP is supposed to consume the primitive/msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Main entrance function for primitives from SCCP User.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Implies a msgb_free(oph->msg), otherwise the same as sccp_sclc_user_sap_down_nofree().</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] scu SCCP User who is sending the primitive</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[on] oph Osmocom primitive header of the primitive</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 on success; negtive in case of error */</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_sclc_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg = prim->oph.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc = sccp_sclc_user_sap_down_nofree(scu, oph);</span><br><span>   msgb_free(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      return rc;</span><br><span> }</span><br><span> </span><br><span>diff --git a/src/sccp_scoc.c b/src/sccp_scoc.c</span><br><span>index 91a1ab7..7570764 100644</span><br><span>--- a/src/sccp_scoc.c</span><br><span>+++ b/src/sccp_scoc.c</span><br><span>@@ -1694,15 +1694,15 @@</span><br><span>       }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Main entrance function for primitives from SCCP User</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Main entrance function for primitives from SCCP User.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The caller is required to free oph->msg, otherwise the same as osmo_sccp_user_sap_down().</span><br><span>  *  \param[in] scu SCCP User sending us the primitive</span><br><span>  *  \param[in] oph Osmocom primitive sent by the user</span><br><span>  *  \returns 0 on success; negative on error */</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_sccp_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span> {</span><br><span>         struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;</span><br><span>   struct osmo_sccp_instance *inst = scu->inst;</span><br><span style="color: hsl(0, 100%, 40%);">- struct msgb *msg = prim->oph.msg;</span><br><span>         struct sccp_connection *conn;</span><br><span>        int rc = 0;</span><br><span>  int event;</span><br><span>@@ -1714,7 +1714,7 @@</span><br><span>   case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST):</span><br><span>   /* other CL primitives? */</span><br><span>           /* Connectionless by-passes this altogether */</span><br><span style="color: hsl(0, 100%, 40%);">-          return sccp_sclc_user_sap_down(scu, oph);</span><br><span style="color: hsl(120, 100%, 40%);">+             return sccp_sclc_user_sap_down_nofree(scu, oph);</span><br><span>     case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST):</span><br><span>            /* Allocate new connection structure */</span><br><span>              conn = conn_create_id(scu, prim->u.connect.conn_id);</span><br><span>@@ -1722,7 +1722,7 @@</span><br><span>                      /* FIXME: inform SCCP user with proper reply */</span><br><span>                      LOGP(DLSCCP, LOGL_ERROR, "Cannot create conn-id for primitive %s\n",</span><br><span>                            osmo_scu_prim_name(&prim->oph));</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+                     return rc;</span><br><span>           }</span><br><span>            break;</span><br><span>       case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_RESPONSE):</span><br><span>@@ -1735,25 +1735,33 @@</span><br><span>                         /* FIXME: inform SCCP user with proper reply */</span><br><span>                      LOGP(DLSCCP, LOGL_ERROR, "Received unknown conn-id %u for primitive %s\n",</span><br><span>                              scu_prim_conn_id(prim), osmo_scu_prim_name(&prim->oph));</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+                     return rc;</span><br><span>           }</span><br><span>            break;</span><br><span>       default:</span><br><span>             LOGP(DLSCCP, LOGL_ERROR, "Received unknown primitive %s\n",</span><br><span>                        osmo_scu_prim_name(&prim->oph));</span><br><span style="color: hsl(0, 100%, 40%);">-         rc = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span>   }</span><br><span> </span><br><span>        /* Map from primitive to event */</span><br><span>    event = osmo_event_for_prim(oph, scu_scoc_event_map);</span><br><span> </span><br><span>    /* Dispatch event into connection */</span><br><span style="color: hsl(0, 100%, 40%);">-    rc = osmo_fsm_inst_dispatch(conn->fi, event, prim);</span><br><span style="color: hsl(0, 100%, 40%);">-out:</span><br><span style="color: hsl(0, 100%, 40%);">-      /* the SAP is supposed to consume the primitive/msgb */</span><br><span style="color: hsl(0, 100%, 40%);">- msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       return osmo_fsm_inst_dispatch(conn->fi, event, prim);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Main entrance function for primitives from SCCP User.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Implies a msgb_free(oph->msg), otherwise the same as osmo_sccp_user_sap().</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] scu SCCP User sending us the primitive</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] oph Osmocom primitive sent by the user</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 on success; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg = prim->oph.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc = osmo_sccp_user_sap_down_nofree(scu, oph);</span><br><span style="color: hsl(120, 100%, 40%);">+    msgb_free(msg);</span><br><span>      return rc;</span><br><span> }</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/13277">change 13277</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/13277"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmo-sccp </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ic818efa78b90f727e1a94c18b60d9a306644f340 </div>
<div style="display:none"> Gerrit-Change-Number: 13277 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>