netlink: return an error in nlmsgerr
Now if a process sends an unsupported netlink requests, an error is returned from the send system call. The linux kernel works differently in this case. It returns errors in the nlmsgerr netlink message. Reported-by: syzbot+571d99510c6f935202da@syzkaller.appspotmail.com PiperOrigin-RevId: 262690453
This commit is contained in:
parent
f2762e8c60
commit
af90e68623
|
@ -122,3 +122,9 @@ const (
|
|||
NETLINK_EXT_ACK = 11
|
||||
NETLINK_DUMP_STRICT_CHK = 12
|
||||
)
|
||||
|
||||
// NetlinkErrorMessage is struct nlmsgerr, from uapi/linux/netlink.h.
|
||||
type NetlinkErrorMessage struct {
|
||||
Error int32
|
||||
Header NetlinkMessageHeader
|
||||
}
|
||||
|
|
|
@ -511,6 +511,19 @@ func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Socket) dumpErrorMesage(ctx context.Context, hdr linux.NetlinkMessageHeader, ms *MessageSet, err *syserr.Error) *syserr.Error {
|
||||
m := ms.AddMessage(linux.NetlinkMessageHeader{
|
||||
Type: linux.NLMSG_ERROR,
|
||||
})
|
||||
|
||||
m.Put(linux.NetlinkErrorMessage{
|
||||
Error: int32(-err.ToLinux().Number()),
|
||||
Header: hdr,
|
||||
})
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// processMessages handles each message in buf, passing it to the protocol
|
||||
// handler for final handling.
|
||||
func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error {
|
||||
|
@ -545,14 +558,20 @@ func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error
|
|||
continue
|
||||
}
|
||||
|
||||
ms := NewMessageSet(s.portID, hdr.Seq)
|
||||
var err *syserr.Error
|
||||
// TODO(b/68877377): ACKs not supported yet.
|
||||
if hdr.Flags&linux.NLM_F_ACK == linux.NLM_F_ACK {
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
err = syserr.ErrNotSupported
|
||||
} else {
|
||||
|
||||
ms := NewMessageSet(s.portID, hdr.Seq)
|
||||
if err := s.protocol.ProcessMessage(ctx, hdr, data, ms); err != nil {
|
||||
return err
|
||||
err = s.protocol.ProcessMessage(ctx, hdr, data, ms)
|
||||
}
|
||||
if err != nil {
|
||||
ms = NewMessageSet(s.portID, hdr.Seq)
|
||||
if err := s.dumpErrorMesage(ctx, hdr, ms, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.sendResponse(ctx, ms); err != nil {
|
||||
|
|
|
@ -89,7 +89,8 @@ TEST(NetdeviceTest, Netmask) {
|
|||
// (i.e. netmask) for the loopback device.
|
||||
int prefixlen = -1;
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr *hdr) {
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr *hdr) {
|
||||
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE)));
|
||||
|
||||
EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
|
||||
|
@ -111,7 +112,8 @@ TEST(NetdeviceTest, Netmask) {
|
|||
ifaddrmsg->ifa_family == AF_INET) {
|
||||
prefixlen = ifaddrmsg->ifa_prefixlen;
|
||||
}
|
||||
}));
|
||||
},
|
||||
false));
|
||||
|
||||
ASSERT_GE(prefixlen, 0);
|
||||
|
||||
|
|
|
@ -238,7 +238,8 @@ TEST(NetlinkRouteTest, GetLinkDump) {
|
|||
// Loopback is common among all tests, check that it's found.
|
||||
bool loopbackFound = false;
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr* hdr) {
|
||||
CheckGetLinkResponse(hdr, kSeq, port);
|
||||
if (hdr->nlmsg_type != RTM_NEWLINK) {
|
||||
return;
|
||||
|
@ -252,10 +253,44 @@ TEST(NetlinkRouteTest, GetLinkDump) {
|
|||
loopbackFound = true;
|
||||
EXPECT_NE(msg->ifi_flags & IFF_LOOPBACK, 0);
|
||||
}
|
||||
}));
|
||||
},
|
||||
false));
|
||||
EXPECT_TRUE(loopbackFound);
|
||||
}
|
||||
|
||||
TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) {
|
||||
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket());
|
||||
|
||||
struct request {
|
||||
struct nlmsghdr hdr;
|
||||
struct ifinfomsg ifm;
|
||||
};
|
||||
|
||||
constexpr uint32_t kSeq = 12345;
|
||||
|
||||
struct request req = {};
|
||||
req.hdr.nlmsg_len = sizeof(req);
|
||||
// If type & 0x3 is equal to 0x2, this means a get request
|
||||
// which doesn't require CAP_SYS_ADMIN.
|
||||
req.hdr.nlmsg_type = ((__RTM_MAX + 1024) & (~0x3)) | 0x2;
|
||||
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.hdr.nlmsg_seq = kSeq;
|
||||
req.ifm.ifi_family = AF_UNSPEC;
|
||||
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr* hdr) {
|
||||
EXPECT_THAT(hdr->nlmsg_type, Eq(NLMSG_ERROR));
|
||||
EXPECT_EQ(hdr->nlmsg_seq, kSeq);
|
||||
EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct nlmsgerr));
|
||||
|
||||
const struct nlmsgerr* msg =
|
||||
reinterpret_cast<const struct nlmsgerr*>(NLMSG_DATA(hdr));
|
||||
EXPECT_EQ(msg->error, -EOPNOTSUPP);
|
||||
},
|
||||
true));
|
||||
}
|
||||
|
||||
TEST(NetlinkRouteTest, MsgHdrMsgTrunc) {
|
||||
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket());
|
||||
|
||||
|
@ -364,9 +399,11 @@ TEST(NetlinkRouteTest, ControlMessageIgnored) {
|
|||
req.ifm.ifi_family = AF_UNSPEC;
|
||||
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr* hdr) {
|
||||
CheckGetLinkResponse(hdr, kSeq, port);
|
||||
}));
|
||||
},
|
||||
false));
|
||||
}
|
||||
|
||||
TEST(NetlinkRouteTest, GetAddrDump) {
|
||||
|
@ -388,7 +425,8 @@ TEST(NetlinkRouteTest, GetAddrDump) {
|
|||
req.rgm.rtgen_family = AF_UNSPEC;
|
||||
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr* hdr) {
|
||||
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE)));
|
||||
|
||||
EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
|
||||
|
@ -405,7 +443,8 @@ TEST(NetlinkRouteTest, GetAddrDump) {
|
|||
EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg));
|
||||
|
||||
// TODO(mpratt): Check ifaddrmsg contents and following attrs.
|
||||
}));
|
||||
},
|
||||
false));
|
||||
}
|
||||
|
||||
TEST(NetlinkRouteTest, LookupAll) {
|
||||
|
@ -448,7 +487,8 @@ TEST(NetlinkRouteTest, GetRouteDump) {
|
|||
bool routeFound = false;
|
||||
bool dstFound = true;
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
|
||||
fd, &req, sizeof(req),
|
||||
[&](const struct nlmsghdr* hdr) {
|
||||
// Validate the reponse to RTM_GETROUTE + NLM_F_DUMP.
|
||||
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWROUTE), Eq(NLMSG_DONE)));
|
||||
|
||||
|
@ -491,7 +531,8 @@ TEST(NetlinkRouteTest, GetRouteDump) {
|
|||
routeFound = true;
|
||||
dstFound = rtDstFound && dstFound;
|
||||
}
|
||||
}));
|
||||
},
|
||||
false));
|
||||
// At least one route found in main route table.
|
||||
EXPECT_TRUE(routeFound);
|
||||
// Found RTA_DST for each route in main table.
|
||||
|
|
|
@ -54,7 +54,8 @@ PosixErrorOr<uint32_t> NetlinkPortID(int fd) {
|
|||
|
||||
PosixError NetlinkRequestResponse(
|
||||
const FileDescriptor& fd, void* request, size_t len,
|
||||
const std::function<void(const struct nlmsghdr* hdr)>& fn) {
|
||||
const std::function<void(const struct nlmsghdr* hdr)>& fn,
|
||||
bool expect_nlmsgerr) {
|
||||
struct iovec iov = {};
|
||||
iov.iov_base = request;
|
||||
iov.iov_len = len;
|
||||
|
@ -93,7 +94,11 @@ PosixError NetlinkRequestResponse(
|
|||
}
|
||||
} while (type != NLMSG_DONE && type != NLMSG_ERROR);
|
||||
|
||||
EXPECT_EQ(type, NLMSG_DONE);
|
||||
if (expect_nlmsgerr) {
|
||||
EXPECT_EQ(type, NLMSG_ERROR);
|
||||
} else {
|
||||
EXPECT_EQ(type, NLMSG_DONE);
|
||||
}
|
||||
return NoError();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ PosixErrorOr<uint32_t> NetlinkPortID(int fd);
|
|||
// Send the passed request and call fn will all response netlink messages.
|
||||
PosixError NetlinkRequestResponse(
|
||||
const FileDescriptor& fd, void* request, size_t len,
|
||||
const std::function<void(const struct nlmsghdr* hdr)>& fn);
|
||||
const std::function<void(const struct nlmsghdr* hdr)>& fn,
|
||||
bool expect_nlmsgerr);
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
|
Loading…
Reference in New Issue