xref: /aosp_15_r20/external/cronet/testing/libfuzzer/fuzzers/mach/mach_message_converter.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "testing/libfuzzer/fuzzers/mach/mach_message_converter.h"
6 
7 #include <string.h>
8 #include <sys/types.h>
9 
10 #include <utility>
11 
12 #include "base/apple/mach_logging.h"
13 #include "base/containers/buffer_iterator.h"
14 #include "base/mac/scoped_mach_msg_destroy.h"
15 
16 namespace mach_fuzzer {
17 
18 namespace {
19 
ConvertPort(const MachPortType & port_proto)20 SendablePort ConvertPort(const MachPortType& port_proto) {
21   struct {
22     bool insert_send_right;
23     bool deallocate_receive_right;
24     mach_msg_type_name_t disposition;
25   } recipe;
26   switch (port_proto) {
27     case RECEIVE:
28       recipe = {true, false, MACH_MSG_TYPE_MOVE_RECEIVE};
29       break;
30     case SEND:
31       recipe = {false, false, MACH_MSG_TYPE_MAKE_SEND};
32       break;
33     case SEND_ONCE:
34       recipe = {false, false, MACH_MSG_TYPE_MAKE_SEND_ONCE};
35       break;
36     case DEAD_NAME:
37       recipe = {true, true, MACH_MSG_TYPE_COPY_SEND};
38       break;
39     case RECEIVE_NO_SENDERS:
40       recipe = {false, false, MACH_MSG_TYPE_MOVE_RECEIVE};
41       break;
42   }
43 
44   SendablePort port;
45   kern_return_t kr = mach_port_allocate(
46       mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
47       base::apple::ScopedMachReceiveRight::Receiver(port.receive_right).get());
48   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate";
49 
50   port.name = port.receive_right.get();
51   port.disposition = recipe.disposition;
52   port.proto_type = port_proto;
53 
54   if (recipe.insert_send_right) {
55     kr = mach_port_insert_right(mach_task_self(), port.name, port.name,
56                                 MACH_MSG_TYPE_MAKE_SEND);
57     MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
58     port.send_right.reset(port.name);
59   }
60 
61   if (recipe.deallocate_receive_right) {
62     port.receive_right.reset();
63   }
64 
65   return port;
66 }
67 
ConvertDescriptor(base::BufferIterator<uint8_t> * iterator,const Descriptor & descriptor_proto,SendablePort * opt_port)68 bool ConvertDescriptor(base::BufferIterator<uint8_t>* iterator,
69                        const Descriptor& descriptor_proto,
70                        SendablePort* opt_port) {
71   switch (descriptor_proto.descriptor_oneof_case()) {
72     case Descriptor::kPort: {
73       auto* descriptor = iterator->MutableObject<mach_msg_port_descriptor_t>();
74       SendablePort port = ConvertPort(descriptor_proto.port());
75       descriptor->name = port.name;
76       descriptor->pad1 = 0;
77       descriptor->pad2 = 0;
78       descriptor->disposition = port.disposition;
79       descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
80       *opt_port = std::move(port);
81       return true;
82     }
83     case Descriptor::kOol: {
84       auto* descriptor = iterator->MutableObject<mach_msg_ool_descriptor_t>();
85       descriptor->address =
86           const_cast<char*>(descriptor_proto.ool().data().data());
87       descriptor->size = descriptor_proto.ool().data().size();
88       descriptor->copy = MACH_MSG_VIRTUAL_COPY;
89       descriptor->pad1 = 0;
90       descriptor->type = MACH_MSG_OOL_DESCRIPTOR;
91       return true;
92     }
93     default:
94       return false;
95   }
96 }
97 
98 }  // namespace
99 
ConvertProtoToMachMessage(const MachMessage & proto)100 SendableMessage ConvertProtoToMachMessage(const MachMessage& proto) {
101   SendableMessage message;
102 
103   const size_t descriptor_count = proto.descriptors().size();
104   const size_t data_size = proto.data().size();
105   const bool include_body =
106       proto.include_body_if_not_complex() || descriptor_count > 0;
107 
108   // This is the maximum size of the message. Depending on the descriptor type,
109   // the actual msgh_size may be less.
110   const size_t message_size =
111       sizeof(mach_msg_header_t) + (include_body ? sizeof(mach_msg_body_t) : 0) +
112       (sizeof(mach_msg_descriptor_t) * descriptor_count) + data_size;
113   message.buffer = std::make_unique<uint8_t[]>(round_msg(message_size));
114 
115   base::BufferIterator<uint8_t> iterator(message.buffer.get(), message_size);
116 
117   auto* header = iterator.MutableObject<mach_msg_header_t>();
118   message.header = header;
119   header->msgh_id = proto.id();
120 
121   if (proto.has_local_port()) {
122     SendablePort port = ConvertPort(proto.local_port());
123     auto disposition = port.disposition;
124     // It's not legal to have a receive reply report.
125     if (disposition != MACH_MSG_TYPE_MOVE_RECEIVE) {
126       header->msgh_bits |= MACH_MSGH_BITS(0, disposition);
127       header->msgh_local_port = port.name;
128       message.ports.push_back(std::move(port));
129     }
130   }
131 
132   if (include_body) {
133     auto* body = iterator.MutableObject<mach_msg_body_t>();
134     body->msgh_descriptor_count = descriptor_count;
135   }
136 
137   if (descriptor_count > 0) {
138     header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
139     for (const auto& descriptor : proto.descriptors()) {
140       SendablePort opt_port;
141       if (!ConvertDescriptor(&iterator, descriptor, &opt_port)) {
142         return SendableMessage();
143       }
144       if (opt_port.name != MACH_PORT_NULL) {
145         message.ports.push_back(std::move(opt_port));
146       }
147     }
148   }
149 
150   auto data = iterator.MutableSpan<uint8_t>(data_size);
151   memcpy(data.data(), proto.data().data(), proto.data().size());
152 
153   header->msgh_size = round_msg(iterator.position());
154 
155   return message;
156 }
157 
SendMessage(mach_port_t remote_port,const MachMessage & proto)158 SendResult SendMessage(mach_port_t remote_port, const MachMessage& proto) {
159   SendResult result;
160   result.message = ConvertProtoToMachMessage(proto);
161   if (!result.message.header) {
162     result.kr = KERN_FAILURE;
163     return result;
164   }
165 
166   result.message.header->msgh_remote_port = remote_port;
167   result.message.header->msgh_bits |=
168       MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
169 
170   base::ScopedMachMsgDestroy scoped_message(result.message.header);
171 
172   result.kr = mach_msg(result.message.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
173                        result.message.header->msgh_size, 0, MACH_PORT_NULL,
174                        /*timeout=*/0, MACH_PORT_NULL);
175 
176   if (result.kr == KERN_SUCCESS) {
177     scoped_message.Disarm();
178   }
179 
180   return result;
181 }
182 
183 }  // namespace mach_fuzzer
184