Skip to content

Commit 9c3de61

Browse files
tohojoanakryiko
authored andcommitted
libbpf: Use dynamically allocated buffer when receiving netlink messages
When receiving netlink messages, libbpf was using a statically allocated stack buffer of 4k bytes. This happened to work fine on systems with a 4k page size, but on systems with larger page sizes it can lead to truncated messages. The user-visible impact of this was that libbpf would insist no XDP program was attached to some interfaces because that bit of the netlink message got chopped off. Fix this by switching to a dynamically allocated buffer; we borrow the approach from iproute2 of using recvmsg() with MSG_PEEK|MSG_TRUNC to get the actual size of the pending message before receiving it, adjusting the buffer as necessary. While we're at it, also add retries on interrupted system calls around the recvmsg() call. v2: - Move peek logic to libbpf_netlink_recv(), don't double free on ENOMEM. Fixes: 8bbb77b ("libbpf: Add various netlink helpers") Reported-by: Zhiqian Guan <[email protected]> Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Kumar Kartikeya Dwivedi <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent d130e95 commit 9c3de61

File tree

1 file changed

+51
-4
lines changed

1 file changed

+51
-4
lines changed

tools/lib/bpf/netlink.c

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,29 +87,75 @@ enum {
8787
NL_DONE,
8888
};
8989

90+
static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
91+
{
92+
int len;
93+
94+
do {
95+
len = recvmsg(sock, mhdr, flags);
96+
} while (len < 0 && (errno == EINTR || errno == EAGAIN));
97+
98+
if (len < 0)
99+
return -errno;
100+
return len;
101+
}
102+
103+
static int alloc_iov(struct iovec *iov, int len)
104+
{
105+
void *nbuf;
106+
107+
nbuf = realloc(iov->iov_base, len);
108+
if (!nbuf)
109+
return -ENOMEM;
110+
111+
iov->iov_base = nbuf;
112+
iov->iov_len = len;
113+
return 0;
114+
}
115+
90116
static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
91117
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
92118
void *cookie)
93119
{
120+
struct iovec iov = {};
121+
struct msghdr mhdr = {
122+
.msg_iov = &iov,
123+
.msg_iovlen = 1,
124+
};
94125
bool multipart = true;
95126
struct nlmsgerr *err;
96127
struct nlmsghdr *nh;
97-
char buf[4096];
98128
int len, ret;
99129

130+
ret = alloc_iov(&iov, 4096);
131+
if (ret)
132+
goto done;
133+
100134
while (multipart) {
101135
start:
102136
multipart = false;
103-
len = recv(sock, buf, sizeof(buf), 0);
137+
len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
138+
if (len < 0) {
139+
ret = len;
140+
goto done;
141+
}
142+
143+
if (len > iov.iov_len) {
144+
ret = alloc_iov(&iov, len);
145+
if (ret)
146+
goto done;
147+
}
148+
149+
len = netlink_recvmsg(sock, &mhdr, 0);
104150
if (len < 0) {
105-
ret = -errno;
151+
ret = len;
106152
goto done;
107153
}
108154

109155
if (len == 0)
110156
break;
111157

112-
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
158+
for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
113159
nh = NLMSG_NEXT(nh, len)) {
114160
if (nh->nlmsg_pid != nl_pid) {
115161
ret = -LIBBPF_ERRNO__WRNGPID;
@@ -151,6 +197,7 @@ static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
151197
}
152198
ret = 0;
153199
done:
200+
free(iov.iov_base);
154201
return ret;
155202
}
156203

0 commit comments

Comments
 (0)