rtsock: fix panic in rtsock_msg_buffer()

The rtsock_msg_buffer() can be called without walkarg, just to calculate
required length.  It can also be called with a degenerate walkarg, that
doesn't have a w_req.  The latter happens when the function is called from
update_rtm_from_info() for the second time.

Zero init walkarg in update_rtm_from_info() and don't pass random stack
garbage as w_req.

In rtsock_msg_buffer() initialize compat32 boolean only once and take of
possible empty w_req.  Simplify the rest of code once compat32 is already
set.

Reviewed by:		melifaro
Differential Revision:	https://reviews.freebsd.org/D47662
Reported-by: syzbot+d4a2682059e23179e76e@syzkaller.appspotmail.com
Reported-by: syzbot+66d7c9b3062e27a56f3f@syzkaller.appspotmail.com
This commit is contained in:
Gleb Smirnoff 2024-11-18 14:12:42 -08:00
parent 43e045c173
commit dae64402b3

View File

@ -921,8 +921,10 @@ update_rtm_from_info(struct rt_addrinfo *info, struct rt_msghdr **prtm,
*/
}
w.w_tmem = (caddr_t)rtm;
w.w_tmemsize = alloc_len;
w = (struct walkarg ){
.w_tmem = (caddr_t)rtm,
.w_tmemsize = alloc_len,
};
rtsock_msg_buffer(rtm->rtm_type, info, &w, &len);
rtm->rtm_addrs = info->rti_addrs;
@ -1774,7 +1776,10 @@ rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *
struct sockaddr_in6 *sin6;
#endif
#ifdef COMPAT_FREEBSD32
bool compat32 = false;
bool compat32;
compat32 = w != NULL && w->w_req != NULL &&
(w->w_req->flags & SCTL_MASK32);
#endif
switch (type) {
@ -1782,10 +1787,9 @@ rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *
case RTM_NEWADDR:
if (w != NULL && w->w_op == NET_RT_IFLISTL) {
#ifdef COMPAT_FREEBSD32
if (w->w_req->flags & SCTL_MASK32) {
if (compat32)
len = sizeof(struct ifa_msghdrl32);
compat32 = true;
} else
else
#endif
len = sizeof(struct ifa_msghdrl);
} else
@ -1793,20 +1797,21 @@ rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *
break;
case RTM_IFINFO:
if (w != NULL && w->w_op == NET_RT_IFLISTL) {
#ifdef COMPAT_FREEBSD32
if (w != NULL && w->w_req->flags & SCTL_MASK32) {
if (w->w_op == NET_RT_IFLISTL)
if (compat32)
len = sizeof(struct if_msghdrl32);
else
len = sizeof(struct if_msghdr32);
compat32 = true;
break;
}
#endif
if (w != NULL && w->w_op == NET_RT_IFLISTL)
len = sizeof(struct if_msghdrl);
else
len = sizeof(struct if_msghdr);
len = sizeof(struct if_msghdrl);
} else {
#ifdef COMPAT_FREEBSD32
if (compat32)
len = sizeof(struct if_msghdr32);
else
#endif
len = sizeof(struct if_msghdr);
}
break;
case RTM_NEWMADDR: