Hi,
Protect all writers to ifm_cur with a mutex. ifmedia_match() does
not return any pointers without lock anymore.
ok?
bluhm
Index: dev/ic/if_wi.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/dev/ic/if_wi.c,v
retrieving revision 1.176
diff -u -p -r1.176 if_wi.c
--- dev/ic/if_wi.c 9 Jan 2022 05:42:38 -0000 1.176
+++ dev/ic/if_wi.c 13 Jul 2022 14:15:18 -0000
@@ -2707,7 +2707,7 @@ wi_sync_media(struct wi_softc *sc, int p
}
media = IFM_MAKEWORD(IFM_TYPE(media), subtype, options,
IFM_INST(media));
- if (ifmedia_match(&sc->sc_media, media, sc->sc_media.ifm_mask) == NULL)
+ if (!ifmedia_match(&sc->sc_media, media, sc->sc_media.ifm_mask))
return (EINVAL);
ifmedia_set(&sc->sc_media, media);
sc->wi_ptype = ptype;
Index: net/if_media.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_media.c,v
retrieving revision 1.35
diff -u -p -r1.35 if_media.c
--- net/if_media.c 12 Jul 2022 22:27:38 -0000 1.35
+++ net/if_media.c 13 Jul 2022 14:19:29 -0000
@@ -105,6 +105,8 @@ static void ifmedia_printword(uint64_t);
struct mutex ifmedia_mtx = MUTEX_INITIALIZER(IPL_NET);
+struct ifmedia_entry *ifmedia_match_get(struct ifmedia *, uint64_t, uint64_t);
+
/*
* Initialize if_media struct for a specific interface instance.
*/
@@ -181,7 +183,8 @@ ifmedia_set(struct ifmedia *ifm, uint64_
{
struct ifmedia_entry *match;
- match = ifmedia_match(ifm, target, ifm->ifm_mask);
+ mtx_enter(&ifmedia_mtx);
+ match = ifmedia_match_get(ifm, target, ifm->ifm_mask);
/*
* If we didn't find the requested media, then we try to fall
@@ -201,15 +204,20 @@ ifmedia_set(struct ifmedia *ifm, uint64_
printf("%s: no match for 0x%llx/0x%llx\n", __func__,
target, ~ifm->ifm_mask);
target = (target & IFM_NMASK) | IFM_NONE;
- match = ifmedia_match(ifm, target, ifm->ifm_mask);
+ match = ifmedia_match_get(ifm, target, ifm->ifm_mask);
if (match == NULL) {
+ mtx_leave(&ifmedia_mtx);
ifmedia_add(ifm, target, 0, NULL);
- match = ifmedia_match(ifm, target, ifm->ifm_mask);
- if (match == NULL)
+ mtx_enter(&ifmedia_mtx);
+ match = ifmedia_match_get(ifm, target, ifm->ifm_mask);
+ if (match == NULL) {
+ mtx_leave(&ifmedia_mtx);
panic("ifmedia_set failed");
+ }
}
}
ifm->ifm_cur = match;
+ mtx_leave(&ifmedia_mtx);
#ifdef IFMEDIA_DEBUG
if (ifmedia_debug) {
@@ -245,8 +253,10 @@ ifmedia_ioctl(struct ifnet *ifp, struct
uint64_t oldmedia;
uint64_t newmedia = ifr->ifr_media;
- match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
+ mtx_enter(&ifmedia_mtx);
+ match = ifmedia_match_get(ifm, newmedia, ifm->ifm_mask);
if (match == NULL) {
+ mtx_leave(&ifmedia_mtx);
#ifdef IFMEDIA_DEBUG
if (ifmedia_debug) {
printf("%s: no media found for 0x%llx\n",
@@ -264,8 +274,10 @@ ifmedia_ioctl(struct ifnet *ifp, struct
*/
if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
(newmedia == ifm->ifm_media) &&
- (match == ifm->ifm_cur))
+ (match == ifm->ifm_cur)) {
+ mtx_leave(&ifmedia_mtx);
return (0);
+ }
/*
* We found a match, now make the driver switch to it.
@@ -283,10 +295,16 @@ ifmedia_ioctl(struct ifnet *ifp, struct
oldmedia = ifm->ifm_media;
ifm->ifm_cur = match;
ifm->ifm_media = newmedia;
+ mtx_leave(&ifmedia_mtx);
+
error = (*ifm->ifm_change_cb)(ifp);
if (error && error != ENETRESET) {
- ifm->ifm_cur = oldentry;
- ifm->ifm_media = oldmedia;
+ mtx_enter(&ifmedia_mtx);
+ if (ifm->ifm_cur == match) {
+ ifm->ifm_cur = oldentry;
+ ifm->ifm_media = oldmedia;
+ }
+ mtx_leave(&ifmedia_mtx);
}
break;
}
@@ -302,10 +320,13 @@ ifmedia_ioctl(struct ifnet *ifp, struct
if (ifmr->ifm_count < 0)
return (EINVAL);
+ mtx_enter(&ifmedia_mtx);
ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
ifm->ifm_cur->ifm_media : IFM_NONE;
ifmr->ifm_mask = ifm->ifm_mask;
ifmr->ifm_status = 0;
+ mtx_leave(&ifmedia_mtx);
+
(*ifm->ifm_status_cb)(ifp, ifmr);
mtx_enter(&ifmedia_mtx);
@@ -368,17 +389,30 @@ ifmedia_ioctl(struct ifnet *ifp, struct
}
/*
- * Find media entry matching a given ifm word.
+ * Find media entry matching a given ifm word. Return 1 if found.
*/
-struct ifmedia_entry *
+int
ifmedia_match(struct ifmedia *ifm, uint64_t target, uint64_t mask)
{
+ struct ifmedia_entry *match;
+
+ mtx_enter(&ifmedia_mtx);
+ match = ifmedia_match_get(ifm, target, mask);
+ mtx_leave(&ifmedia_mtx);
+
+ return (match != NULL);
+}
+
+struct ifmedia_entry *
+ifmedia_match_get(struct ifmedia *ifm, uint64_t target, uint64_t mask)
+{
struct ifmedia_entry *match, *next;
+ MUTEX_ASSERT_LOCKED(&ifmedia_mtx);
+
match = NULL;
mask = ~mask;
- mtx_enter(&ifmedia_mtx);
TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) {
if ((next->ifm_media & mask) == (target & mask)) {
if (match) {
@@ -392,7 +426,6 @@ ifmedia_match(struct ifmedia *ifm, uint6
match = next;
}
}
- mtx_leave(&ifmedia_mtx);
return (match);
}
@@ -417,6 +450,7 @@ ifmedia_delete_instance(struct ifmedia *
TAILQ_INSERT_TAIL(&ifmlist, ife, ifm_list);
}
}
+ ifm->ifm_cur = NULL;
mtx_leave(&ifmedia_mtx);
/* Do not hold mutex longer than necessary, call free() without. */
Index: net/if_media.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_media.h,v
retrieving revision 1.44
diff -u -p -r1.44 if_media.h
--- net/if_media.h 12 Jul 2022 22:08:18 -0000 1.44
+++ net/if_media.h 13 Jul 2022 14:18:09 -0000
@@ -103,7 +103,7 @@ TAILQ_HEAD(ifmedia_list, ifmedia_entry);
struct ifmedia {
uint64_t ifm_mask; /* mask of changes we don't care about
*/
uint64_t ifm_media; /* current user-set media word */
- struct ifmedia_entry *ifm_cur; /* currently selected media */
+ struct ifmedia_entry *ifm_cur; /* [M] currently selected media */
struct ifmedia_list ifm_list; /* [M] list of all supported media */
size_t ifm_nwords; /* [M] number of ifm_list entries */
ifm_change_cb_t ifm_change_cb; /* media change driver callback */
@@ -129,7 +129,7 @@ int ifmedia_ioctl(struct ifnet *, struct
u_long);
/* Locate a media entry */
-struct ifmedia_entry *ifmedia_match(struct ifmedia *, uint64_t, uint64_t);
+int ifmedia_match(struct ifmedia *, uint64_t, uint64_t);
/* Delete all media for a given media instance */
void ifmedia_delete_instance(struct ifmedia *, uint64_t);