Print this page
1918 stack overflow from mac_promisc_dispatch()
        
*** 19,28 ****
--- 19,29 ----
   * CDDL HEADER END
   */
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
+  * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
   */
  
  /*
   * Data-Link Services Module
   */
*** 80,90 ****
  dls_close(dld_str_t *dsp)
  {
          dls_link_t              *dlp = dsp->ds_dlp;
          dls_multicst_addr_t     *p;
          dls_multicst_addr_t     *nextp;
-         uint32_t                old_flags;
  
          ASSERT(dsp->ds_datathr_cnt == 0);
          ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
  
          if (dsp->ds_local)
--- 81,90 ----
*** 117,129 ****
  
          /*
           * If the MAC has been set in promiscuous mode then disable it.
           * This needs to be done before resetting ds_rx.
           */
!         old_flags = dsp->ds_promisc;
!         dsp->ds_promisc = 0;
!         (void) dls_promisc(dsp, old_flags);
  
          /*
           * At this point we have cutoff inbound packet flow from the mac
           * for this 'dsp'. The dls_link_remove above cut off packets meant
           * for us and waited for upcalls to finish. Similarly the dls_promisc
--- 117,127 ----
  
          /*
           * If the MAC has been set in promiscuous mode then disable it.
           * This needs to be done before resetting ds_rx.
           */
!         (void) dls_promisc(dsp, 0);
  
          /*
           * At this point we have cutoff inbound packet flow from the mac
           * for this 'dsp'. The dls_link_remove above cut off packets meant
           * for us and waited for upcalls to finish. Similarly the dls_promisc
*** 232,295 ****
           */
          dls_link_remove(dsp->ds_dlp, dsp);
          dsp->ds_sap = 0;
  }
  
  int
! dls_promisc(dld_str_t *dsp, uint32_t old_flags)
  {
          int             err = 0;
  
          ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
!         ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
              DLS_PROMISC_PHYS)));
  
!         if (old_flags == 0 && dsp->ds_promisc != 0) {
                  /*
                   * If only DLS_PROMISC_SAP, we don't turn on the
                   * physical promisc mode
                   */
                  err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
                      dls_rx_promisc, dsp, &dsp->ds_mph,
!                     (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
                      MAC_PROMISC_FLAGS_NO_PHYS);
!                 if (err != 0)
                          return (err);
  
                  /* Remove vlan promisc handle to avoid sending dup copy up */
                  if (dsp->ds_vlan_mph != NULL) {
                          mac_promisc_remove(dsp->ds_vlan_mph);
                          dsp->ds_vlan_mph = NULL;
                  }
!         } else if (old_flags != 0 && dsp->ds_promisc == 0) {
                  ASSERT(dsp->ds_mph != NULL);
  
                  mac_promisc_remove(dsp->ds_mph);
                  dsp->ds_mph = NULL;
  
                  if (dsp->ds_sap == ETHERTYPE_VLAN &&
                      dsp->ds_dlstate != DL_UNBOUND) {
-                         int err;
- 
                          if (dsp->ds_vlan_mph != NULL)
                                  return (EINVAL);
                          err = mac_promisc_add(dsp->ds_mch,
                              MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
                              &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
-                         return (err);
                  }
!         } else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
!             dsp->ds_promisc != old_flags) {
                  /*
                   * If the old flag is PROMISC_SAP, but the current flag has
                   * changed to some new non-zero value, we need to turn the
                   * physical promiscuous mode.
                   */
                  ASSERT(dsp->ds_mph != NULL);
                  mac_promisc_remove(dsp->ds_mph);
                  err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
                      dls_rx_promisc, dsp, &dsp->ds_mph, 0);
          }
  
          return (err);
  }
  
--- 230,309 ----
           */
          dls_link_remove(dsp->ds_dlp, dsp);
          dsp->ds_sap = 0;
  }
  
+ /*
+  * In order to prevent promiscuous-mode processing with dsp->ds_promisc
+  * set to inaccurate values, this function sets dsp->ds_promisc with new
+  * flags.  For enabling (mac_promisc_add), the flags are set prior to the
+  * actual enabling.  For disabling (mac_promisc_remove), the flags are set
+  * after the actual disabling.
+  */
  int
! dls_promisc(dld_str_t *dsp, uint32_t new_flags)
  {
          int err = 0;
+         uint32_t old_flags = dsp->ds_promisc;
  
          ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
!         ASSERT(!(new_flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
              DLS_PROMISC_PHYS)));
  
!         if (dsp->ds_promisc == 0 && new_flags != 0) {
                  /*
                   * If only DLS_PROMISC_SAP, we don't turn on the
                   * physical promisc mode
                   */
+                 dsp->ds_promisc = new_flags;
                  err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
                      dls_rx_promisc, dsp, &dsp->ds_mph,
!                     (new_flags != DLS_PROMISC_SAP) ? 0 :
                      MAC_PROMISC_FLAGS_NO_PHYS);
!                 if (err != 0) {
!                         dsp->ds_promisc = old_flags;
                          return (err);
+                 }
  
                  /* Remove vlan promisc handle to avoid sending dup copy up */
                  if (dsp->ds_vlan_mph != NULL) {
                          mac_promisc_remove(dsp->ds_vlan_mph);
                          dsp->ds_vlan_mph = NULL;
                  }
!         } else if (dsp->ds_promisc != 0 && new_flags == 0) {
                  ASSERT(dsp->ds_mph != NULL);
  
                  mac_promisc_remove(dsp->ds_mph);
+                 dsp->ds_promisc = new_flags;
                  dsp->ds_mph = NULL;
  
                  if (dsp->ds_sap == ETHERTYPE_VLAN &&
                      dsp->ds_dlstate != DL_UNBOUND) {
                          if (dsp->ds_vlan_mph != NULL)
                                  return (EINVAL);
                          err = mac_promisc_add(dsp->ds_mch,
                              MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
                              &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
                  }
!         } else if (dsp->ds_promisc == DLS_PROMISC_SAP && new_flags != 0 &&
!             new_flags != dsp->ds_promisc) {
                  /*
                   * If the old flag is PROMISC_SAP, but the current flag has
                   * changed to some new non-zero value, we need to turn the
                   * physical promiscuous mode.
                   */
                  ASSERT(dsp->ds_mph != NULL);
                  mac_promisc_remove(dsp->ds_mph);
+                 /* Honors both after-remove and before-add semantics! */
+                 dsp->ds_promisc = new_flags;
                  err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
                      dls_rx_promisc, dsp, &dsp->ds_mph, 0);
+                 if (err != 0)
+                         dsp->ds_promisc = old_flags;
+         } else {
+                 /* No adding or removing, but record the new flags anyway. */
+                 dsp->ds_promisc = new_flags;
          }
  
          return (err);
  }